aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/scsi/virtio_scsi.c77
1 files changed, 52 insertions, 25 deletions
diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c
index b0ad5aa6b55..0ecf95e214d 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);