mirror of https://gitee.com/openkylin/linux.git
[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:
parent
b5ee8f2802
commit
139fe45abc
|
@ -43,15 +43,22 @@ struct virtio_scsi_cmd {
|
||||||
} resp;
|
} resp;
|
||||||
} ____cacheline_aligned_in_smp;
|
} ____cacheline_aligned_in_smp;
|
||||||
|
|
||||||
/* Driver instance state */
|
struct virtio_scsi_vq {
|
||||||
struct virtio_scsi {
|
/* Protects vq */
|
||||||
/* Protects ctrl_vq, req_vq and sg[] */
|
|
||||||
spinlock_t vq_lock;
|
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 virtio_device *vdev;
|
||||||
struct virtqueue *ctrl_vq;
|
struct virtio_scsi_vq ctrl_vq;
|
||||||
struct virtqueue *event_vq;
|
struct virtio_scsi_vq event_vq;
|
||||||
struct virtqueue *req_vq;
|
struct virtio_scsi_vq req_vq;
|
||||||
|
|
||||||
/* For sglist construction when adding commands to the virtqueue. */
|
/* For sglist construction when adding commands to the virtqueue. */
|
||||||
struct scatterlist sg[];
|
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))
|
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;
|
void *buf;
|
||||||
unsigned long flags;
|
|
||||||
unsigned int len;
|
unsigned int len;
|
||||||
|
|
||||||
spin_lock_irqsave(&vscsi->vq_lock, flags);
|
|
||||||
|
|
||||||
do {
|
do {
|
||||||
virtqueue_disable_cb(vq);
|
virtqueue_disable_cb(vq);
|
||||||
while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
|
while ((buf = virtqueue_get_buf(vq, &len)) != NULL)
|
||||||
fn(buf);
|
fn(buf);
|
||||||
} while (!virtqueue_enable_cb(vq));
|
} while (!virtqueue_enable_cb(vq));
|
||||||
|
|
||||||
spin_unlock_irqrestore(&vscsi->vq_lock, flags);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void virtscsi_req_done(struct virtqueue *vq)
|
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);
|
virtscsi_vq_done(vq, virtscsi_complete_cmd);
|
||||||
|
spin_unlock_irqrestore(&vscsi->req_vq.vq_lock, flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void virtscsi_complete_free(void *buf)
|
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)
|
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);
|
virtscsi_vq_done(vq, virtscsi_complete_free);
|
||||||
|
spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void virtscsi_event_done(struct virtqueue *vq)
|
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);
|
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,
|
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;
|
*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,
|
struct virtio_scsi_cmd *cmd,
|
||||||
size_t req_size, size_t resp_size, gfp_t gfp)
|
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;
|
unsigned long flags;
|
||||||
int ret;
|
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);
|
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)
|
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)
|
if (ret > 0)
|
||||||
virtqueue_notify(vq);
|
virtqueue_notify(vq->vq);
|
||||||
return ret;
|
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);
|
BUG_ON(sc->cmd_len > VIRTIO_SCSI_CDB_SIZE);
|
||||||
memcpy(cmd->req.cmd.cdb, sc->cmnd, sc->cmd_len);
|
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,
|
sizeof cmd->req.cmd, sizeof cmd->resp.cmd,
|
||||||
GFP_ATOMIC) >= 0)
|
GFP_ATOMIC) >= 0)
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -317,7 +337,7 @@ static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd)
|
||||||
int ret = FAILED;
|
int ret = FAILED;
|
||||||
|
|
||||||
cmd->comp = ∁
|
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,
|
sizeof cmd->req.tmf, sizeof cmd->resp.tmf,
|
||||||
GFP_NOIO) < 0)
|
GFP_NOIO) < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -410,6 +430,13 @@ static struct scsi_host_template virtscsi_host_template = {
|
||||||
&__val, sizeof(__val)); \
|
&__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,
|
static int virtscsi_init(struct virtio_device *vdev,
|
||||||
struct virtio_scsi *vscsi)
|
struct virtio_scsi *vscsi)
|
||||||
{
|
{
|
||||||
|
@ -431,9 +458,9 @@ static int virtscsi_init(struct virtio_device *vdev,
|
||||||
if (err)
|
if (err)
|
||||||
return err;
|
return err;
|
||||||
|
|
||||||
vscsi->ctrl_vq = vqs[0];
|
virtscsi_init_vq(&vscsi->ctrl_vq, vqs[0]);
|
||||||
vscsi->event_vq = vqs[1];
|
virtscsi_init_vq(&vscsi->event_vq, vqs[1]);
|
||||||
vscsi->req_vq = vqs[2];
|
virtscsi_init_vq(&vscsi->req_vq, vqs[2]);
|
||||||
|
|
||||||
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
|
virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE);
|
||||||
virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_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;
|
vdev->priv = shost;
|
||||||
|
|
||||||
/* Random initializations. */
|
/* Random initializations. */
|
||||||
spin_lock_init(&vscsi->vq_lock);
|
spin_lock_init(&vscsi->sg_lock);
|
||||||
sg_init_table(vscsi->sg, sg_elems + 2);
|
sg_init_table(vscsi->sg, sg_elems + 2);
|
||||||
|
|
||||||
err = virtscsi_init(vdev, vscsi);
|
err = virtscsi_init(vdev, vscsi);
|
||||||
|
|
Loading…
Reference in New Issue