[SCSI] virtio-scsi: split locking per vq

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@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
This commit is contained in:
Paolo Bonzini 2012-06-13 16:56:32 +02:00 committed by James Bottomley
parent b5ee8f2802
commit 139fe45abc
1 changed files with 53 additions and 26 deletions

View File

@ -43,15 +43,22 @@ struct virtio_scsi_cmd {
} resp;
} ____cacheline_aligned_in_smp;
/* Driver instance state */
struct virtio_scsi {
/* Protects ctrl_vq, req_vq and sg[] */
struct virtio_scsi_vq {
/* Protects vq */
spinlock_t vq_lock;
struct virtqueue *vq;
};
/* Driver instance state */
struct virtio_scsi {
/* 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 = &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);