Keep a separate lock for each virtqueue. While not particularly important now, it prepares the code for when we will add support for multiple request queues. It is also more tidy as soon as we introduce a separate lock for the sglist. Signed-off-by: Paolo Bonzini <pbonzini@xxxxxxxxxx> --- drivers/scsi/virtio_scsi.c | 77 +++++++++++++++++++++++++++++-------------- 1 files changed, 52 insertions(+), 25 deletions(-) diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index b0ad5aa..0ecf95e 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -43,15 +43,22 @@ struct virtio_scsi_cmd { } resp; } ____cacheline_aligned_in_smp; +struct virtio_scsi_vq { + /* Protects vq */ + spinlock_t vq_lock; + + struct virtqueue *vq; +}; + /* Driver instance state */ struct virtio_scsi { - /* Protects ctrl_vq, req_vq and sg[] */ - spinlock_t vq_lock; + /* Protects sg[]. The lock hierarchy is sg_lock -> vq_lock. */ + spinlock_t sg_lock; struct virtio_device *vdev; - struct virtqueue *ctrl_vq; - struct virtqueue *event_vq; - struct virtqueue *req_vq; + struct virtio_scsi_vq ctrl_vq; + struct virtio_scsi_vq event_vq; + struct virtio_scsi_vq req_vq; /* For sglist construction when adding commands to the virtqueue. */ struct scatterlist sg[]; @@ -147,26 +154,25 @@ static void virtscsi_complete_cmd(void *buf) static void virtscsi_vq_done(struct virtqueue *vq, void (*fn)(void *buf)) { - struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); - struct virtio_scsi *vscsi = shost_priv(sh); void *buf; - unsigned long flags; unsigned int len; - spin_lock_irqsave(&vscsi->vq_lock, flags); - do { virtqueue_disable_cb(vq); while ((buf = virtqueue_get_buf(vq, &len)) != NULL) fn(buf); } while (!virtqueue_enable_cb(vq)); - - spin_unlock_irqrestore(&vscsi->vq_lock, flags); } static void virtscsi_req_done(struct virtqueue *vq) { + struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); + struct virtio_scsi *vscsi = shost_priv(sh); + unsigned long flags; + + spin_lock_irqsave(&vscsi->req_vq.vq_lock, flags); virtscsi_vq_done(vq, virtscsi_complete_cmd); + spin_unlock_irqrestore(&vscsi->req_vq.vq_lock, flags); }; static void virtscsi_complete_free(void *buf) @@ -181,12 +187,24 @@ static void virtscsi_complete_free(void *buf) static void virtscsi_ctrl_done(struct virtqueue *vq) { + struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); + struct virtio_scsi *vscsi = shost_priv(sh); + unsigned long flags; + + spin_lock_irqsave(&vscsi->ctrl_vq.vq_lock, flags); virtscsi_vq_done(vq, virtscsi_complete_free); + spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags); }; static void virtscsi_event_done(struct virtqueue *vq) { + struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); + struct virtio_scsi *vscsi = shost_priv(sh); + unsigned long flags; + + spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); virtscsi_vq_done(vq, virtscsi_complete_free); + spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); }; static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx, @@ -250,7 +268,7 @@ static void virtscsi_map_cmd(struct virtio_scsi *vscsi, *in_num = idx - *out_num; } -static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq, +static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *vq, struct virtio_scsi_cmd *cmd, size_t req_size, size_t resp_size, gfp_t gfp) { @@ -258,17 +276,19 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq, unsigned long flags; int ret; - spin_lock_irqsave(&vscsi->vq_lock, flags); - + spin_lock_irqsave(&vscsi->sg_lock, flags); virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size); - ret = virtqueue_add_buf(vq, vscsi->sg, out_num, in_num, cmd, gfp); + spin_lock(&vq->vq_lock); + ret = virtqueue_add_buf(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp); if (ret >= 0) - ret = virtqueue_kick_prepare(vq); + ret = virtqueue_kick_prepare(vq->vq); + + spin_unlock(&vq->vq_lock); + spin_unlock_irqrestore(&vscsi->sg_lock, flags); - spin_unlock_irqrestore(&vscsi->vq_lock, flags); if (ret > 0) - virtqueue_notify(vq); + virtqueue_notify(vq->vq); return ret; } @@ -302,7 +322,7 @@ static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE); memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len); - if (virtscsi_kick_cmd(vscsi, vscsi->req_vq, cmd, + if (virtscsi_kick_cmd(vscsi, &vscsi->req_vq, cmd, sizeof cmd->req.cmd, sizeof cmd->resp.cmd, GFP_ATOMIC) >= 0) ret = 0; @@ -317,7 +337,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) int ret = FAILED; cmd->comp = ∁ - if (virtscsi_kick_cmd(vscsi, vscsi->ctrl_vq, cmd, + if (virtscsi_kick_cmd(vscsi, &vscsi->ctrl_vq, cmd, sizeof cmd->req.tmf, sizeof cmd->resp.tmf, GFP_NOIO) < 0) goto out; @@ -410,6 +430,13 @@ static struct scsi_host_template virtscsi_host_template = { &__val, sizeof(__val)); \ }) +static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, + struct virtqueue *vq) +{ + spin_lock_init(&virtscsi_vq->vq_lock); + virtscsi_vq->vq = vq; +} + static int virtscsi_init(struct virtio_device *vdev, struct virtio_scsi *vscsi) { @@ -431,9 +458,9 @@ static int virtscsi_init(struct virtio_device *vdev, if (err) return err; - vscsi->ctrl_vq = vqs[0]; - vscsi->event_vq = vqs[1]; - vscsi->req_vq = vqs[2]; + virtscsi_init_vq(&vscsi->ctrl_vq, vqs[0]); + virtscsi_init_vq(&vscsi->event_vq, vqs[1]); + virtscsi_init_vq(&vscsi->req_vq, vqs[2]); virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); @@ -466,7 +493,7 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) vdev->priv = shost; /* Random initializations. */ - spin_lock_init(&vscsi->vq_lock); + spin_lock_init(&vscsi->sg_lock); sg_init_table(vscsi->sg, sg_elems + 2); err = virtscsi_init(vdev, vscsi); -- 1.7.1 -- To unsubscribe from this list: send the line "unsubscribe linux-scsi" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html