From b5ee8f2802c559fccb177c0a117f5cd56c1049cc Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Jun 2012 16:56:31 +0200 Subject: [SCSI] virtio-scsi: unlock during kick Separate virtqueue_kick_prepare from virtqueue_notify, so that the expensive vmexit is done without holding the lock. Signed-off-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 1b3843117268..b0ad5aa6b552 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -264,9 +264,11 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtqueue *vq, ret = virtqueue_add_buf(vq, vscsi->sg, out_num, in_num, cmd, gfp); if (ret >= 0) - virtqueue_kick(vq); + ret = virtqueue_kick_prepare(vq); spin_unlock_irqrestore(&vscsi->vq_lock, flags); + if (ret > 0) + virtqueue_notify(vq); return ret; } -- cgit v1.2.3 From 139fe45abc2234b20fd5ecbcb7ea6d3688fed5e5 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Jun 2012 16:56:32 +0200 Subject: [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 Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 77 +++++++++++++++++++++++++++++++--------------- 1 file changed, 52 insertions(+), 25 deletions(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index b0ad5aa6b552..0ecf95e214d3 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); -- cgit v1.2.3 From bce750b1633927be3eecf821f4d17975c3ba5b6a Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Jun 2012 16:56:33 +0200 Subject: [SCSI] virtio-scsi: release sg_lock after add_buf We do not need the sglist after calling virtqueue_add_buf. Hence we can "pipeline" the locked operations and start preparing the sglist for the next request while we kick the virtqueue. Together with the previous two patches, this improves performance as follows. For a simple "if=/dev/sda of=/dev/null bs=128M iflag=direct" (the source being a 10G disk, residing entirely in the host buffer cache), the additional locking does not cause any penalty with only one dd process, but 2 simultaneous I/O operations improve their times by 3%: number of simultaneous dd 1 2 ---------------------------------------- current 5.9958s 10.2640s patched 5.9531s 9.8663s (Times are best of 10). Signed-off-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 0ecf95e214d3..facfc90ef005 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -281,11 +281,11 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v spin_lock(&vq->vq_lock); ret = virtqueue_add_buf(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp); + spin_unlock(&vscsi->sg_lock); if (ret >= 0) ret = virtqueue_kick_prepare(vq->vq); - spin_unlock(&vq->vq_lock); - spin_unlock_irqrestore(&vscsi->sg_lock, flags); + spin_unlock_irqrestore(&vq->vq_lock, flags); if (ret > 0) virtqueue_notify(vq->vq); -- cgit v1.2.3 From 2bd37f0fde99cbf8b78fb55f1128e8c3a63cf1da Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Wed, 13 Jun 2012 16:56:34 +0200 Subject: [SCSI] virtio-scsi: split scatterlist per target To improve performance for I/O to different targets, add a separate scatterlist for each of them. Signed-off-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 141 ++++++++++++++++++++++++++++++--------------- 1 file changed, 94 insertions(+), 47 deletions(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index facfc90ef005..9fc5e67a0ca5 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -50,18 +50,24 @@ struct virtio_scsi_vq { struct virtqueue *vq; }; +/* Per-target queue state */ +struct virtio_scsi_target_state { + /* Protects sg. Lock hierarchy is tgt_lock -> vq_lock. */ + spinlock_t tgt_lock; + + /* For sglist construction when adding commands to the virtqueue. */ + struct scatterlist sg[]; +}; + /* 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_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[]; + struct virtio_scsi_target_state *tgt[]; }; static struct kmem_cache *virtscsi_cmd_cache; @@ -230,25 +236,17 @@ static void virtscsi_map_sgl(struct scatterlist *sg, unsigned int *p_idx, * @req_size : size of the request buffer * @resp_size : size of the response buffer * - * Called with vq_lock held. + * Called with tgt_lock held. */ -static void virtscsi_map_cmd(struct virtio_scsi *vscsi, +static void virtscsi_map_cmd(struct virtio_scsi_target_state *tgt, struct virtio_scsi_cmd *cmd, unsigned *out_num, unsigned *in_num, size_t req_size, size_t resp_size) { struct scsi_cmnd *sc = cmd->sc; - struct scatterlist *sg = vscsi->sg; + struct scatterlist *sg = tgt->sg; unsigned int idx = 0; - if (sc) { - struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); - BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); - - /* TODO: check feature bit and fail if unsupported? */ - BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL); - } - /* Request header. */ sg_set_buf(&sg[idx++], &cmd->req, req_size); @@ -268,7 +266,8 @@ static void virtscsi_map_cmd(struct virtio_scsi *vscsi, *in_num = idx - *out_num; } -static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *vq, +static int virtscsi_kick_cmd(struct virtio_scsi_target_state *tgt, + struct virtio_scsi_vq *vq, struct virtio_scsi_cmd *cmd, size_t req_size, size_t resp_size, gfp_t gfp) { @@ -276,12 +275,12 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v unsigned long flags; int ret; - spin_lock_irqsave(&vscsi->sg_lock, flags); - virtscsi_map_cmd(vscsi, cmd, &out_num, &in_num, req_size, resp_size); + spin_lock_irqsave(&tgt->tgt_lock, flags); + virtscsi_map_cmd(tgt, cmd, &out_num, &in_num, req_size, resp_size); spin_lock(&vq->vq_lock); - ret = virtqueue_add_buf(vq->vq, vscsi->sg, out_num, in_num, cmd, gfp); - spin_unlock(&vscsi->sg_lock); + ret = virtqueue_add_buf(vq->vq, tgt->sg, out_num, in_num, cmd, gfp); + spin_unlock(&tgt->tgt_lock); if (ret >= 0) ret = virtqueue_kick_prepare(vq->vq); @@ -295,9 +294,16 @@ static int virtscsi_kick_cmd(struct virtio_scsi *vscsi, struct virtio_scsi_vq *v static int virtscsi_queuecommand(struct Scsi_Host *sh, struct scsi_cmnd *sc) { struct virtio_scsi *vscsi = shost_priv(sh); + struct virtio_scsi_target_state *tgt = vscsi->tgt[sc->device->id]; struct virtio_scsi_cmd *cmd; int ret; + struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); + BUG_ON(scsi_sg_count(sc) > shost->sg_tablesize); + + /* TODO: check feature bit and fail if unsupported? */ + BUG_ON(sc->sc_data_direction == DMA_BIDIRECTIONAL); + dev_dbg(&sc->device->sdev_gendev, "cmd %p CDB: %#02x\n", sc, sc->cmnd[0]); @@ -322,7 +328,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(tgt, &vscsi->req_vq, cmd, sizeof cmd->req.cmd, sizeof cmd->resp.cmd, GFP_ATOMIC) >= 0) ret = 0; @@ -334,10 +340,11 @@ out: static int virtscsi_tmf(struct virtio_scsi *vscsi, struct virtio_scsi_cmd *cmd) { DECLARE_COMPLETION_ONSTACK(comp); + struct virtio_scsi_target_state *tgt = vscsi->tgt[cmd->sc->device->id]; int ret = FAILED; cmd->comp = ∁ - if (virtscsi_kick_cmd(vscsi, &vscsi->ctrl_vq, cmd, + if (virtscsi_kick_cmd(tgt, &vscsi->ctrl_vq, cmd, sizeof cmd->req.tmf, sizeof cmd->resp.tmf, GFP_NOIO) < 0) goto out; @@ -437,11 +444,49 @@ static void virtscsi_init_vq(struct virtio_scsi_vq *virtscsi_vq, virtscsi_vq->vq = vq; } +static struct virtio_scsi_target_state *virtscsi_alloc_tgt( + struct virtio_device *vdev, int sg_elems) +{ + struct virtio_scsi_target_state *tgt; + gfp_t gfp_mask = GFP_KERNEL; + + /* We need extra sg elements at head and tail. */ + tgt = kmalloc(sizeof(*tgt) + sizeof(tgt->sg[0]) * (sg_elems + 2), + gfp_mask); + + if (!tgt) + return NULL; + + spin_lock_init(&tgt->tgt_lock); + sg_init_table(tgt->sg, sg_elems + 2); + return tgt; +} + +static void virtscsi_remove_vqs(struct virtio_device *vdev) +{ + struct Scsi_Host *sh = virtio_scsi_host(vdev); + struct virtio_scsi *vscsi = shost_priv(sh); + u32 i, num_targets; + + /* Stop all the virtqueues. */ + vdev->config->reset(vdev); + + num_targets = sh->max_id; + for (i = 0; i < num_targets; i++) { + kfree(vscsi->tgt[i]); + vscsi->tgt[i] = NULL; + } + + vdev->config->del_vqs(vdev); +} + static int virtscsi_init(struct virtio_device *vdev, - struct virtio_scsi *vscsi) + struct virtio_scsi *vscsi, int num_targets) { int err; struct virtqueue *vqs[3]; + u32 i, sg_elems; + vq_callback_t *callbacks[] = { virtscsi_ctrl_done, virtscsi_event_done, @@ -464,7 +509,23 @@ static int virtscsi_init(struct virtio_device *vdev, virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); - return 0; + + /* We need to know how many segments before we allocate. */ + sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; + + for (i = 0; i < num_targets; i++) { + vscsi->tgt[i] = virtscsi_alloc_tgt(vdev, sg_elems); + if (!vscsi->tgt[i]) { + err = -ENOMEM; + goto out; + } + } + err = 0; + +out: + if (err) + virtscsi_remove_vqs(vdev); + return err; } static int __devinit virtscsi_probe(struct virtio_device *vdev) @@ -472,31 +533,25 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) struct Scsi_Host *shost; struct virtio_scsi *vscsi; int err; - u32 sg_elems; + u32 sg_elems, num_targets; u32 cmd_per_lun; - /* We need to know how many segments before we allocate. - * We need an extra sg elements at head and tail. - */ - sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; - /* Allocate memory and link the structs together. */ + num_targets = virtscsi_config_get(vdev, max_target) + 1; shost = scsi_host_alloc(&virtscsi_host_template, - sizeof(*vscsi) + sizeof(vscsi->sg[0]) * (sg_elems + 2)); + sizeof(*vscsi) + + num_targets * sizeof(struct virtio_scsi_target_state)); if (!shost) return -ENOMEM; + sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; shost->sg_tablesize = sg_elems; vscsi = shost_priv(shost); vscsi->vdev = vdev; vdev->priv = shost; - /* Random initializations. */ - spin_lock_init(&vscsi->sg_lock); - sg_init_table(vscsi->sg, sg_elems + 2); - - err = virtscsi_init(vdev, vscsi); + err = virtscsi_init(vdev, vscsi, num_targets); if (err) goto virtscsi_init_failed; @@ -504,7 +559,7 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) shost->cmd_per_lun = min_t(u32, cmd_per_lun, shost->can_queue); shost->max_sectors = virtscsi_config_get(vdev, max_sectors) ?: 0xFFFF; shost->max_lun = virtscsi_config_get(vdev, max_lun) + 1; - shost->max_id = virtscsi_config_get(vdev, max_target) + 1; + shost->max_id = num_targets; shost->max_channel = 0; shost->max_cmd_len = VIRTIO_SCSI_CDB_SIZE; err = scsi_add_host(shost, &vdev->dev); @@ -522,14 +577,6 @@ virtscsi_init_failed: return err; } -static void virtscsi_remove_vqs(struct virtio_device *vdev) -{ - /* Stop all the virtqueues. */ - vdev->config->reset(vdev); - - vdev->config->del_vqs(vdev); -} - static void __devexit virtscsi_remove(struct virtio_device *vdev) { struct Scsi_Host *shost = virtio_scsi_host(vdev); @@ -552,7 +599,7 @@ static int virtscsi_restore(struct virtio_device *vdev) struct Scsi_Host *sh = virtio_scsi_host(vdev); struct virtio_scsi *vscsi = shost_priv(sh); - return virtscsi_init(vdev, vscsi); + return virtscsi_init(vdev, vscsi, sh->max_id); } #endif -- cgit v1.2.3 From 365a7150094114a0f8ef0b6164e6b04b519039e8 Mon Sep 17 00:00:00 2001 From: Cong Meng Date: Thu, 5 Jul 2012 17:06:43 +0800 Subject: [SCSI] virtio-scsi: hotplug support for virtio-scsi This patch implements the hotplug support for virtio-scsi. When there is a device attached/detached, the virtio-scsi driver will be signaled via event virtual queue and it will add/remove the scsi device in question automatically. Signed-off-by: Sen Wang Signed-off-by: Cong Meng Acked-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 124 +++++++++++++++++++++++++++++++++++++++++++- include/linux/virtio_scsi.h | 9 ++++ 2 files changed, 132 insertions(+), 1 deletion(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index 9fc5e67a0ca5..ae3bef7c523f 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -25,6 +25,7 @@ #include #define VIRTIO_SCSI_MEMPOOL_SZ 64 +#define VIRTIO_SCSI_EVENT_LEN 8 /* Command queue element */ struct virtio_scsi_cmd { @@ -43,6 +44,12 @@ struct virtio_scsi_cmd { } resp; } ____cacheline_aligned_in_smp; +struct virtio_scsi_event_node { + struct virtio_scsi *vscsi; + struct virtio_scsi_event event; + struct work_struct work; +}; + struct virtio_scsi_vq { /* Protects vq */ spinlock_t vq_lock; @@ -67,6 +74,9 @@ struct virtio_scsi { struct virtio_scsi_vq event_vq; struct virtio_scsi_vq req_vq; + /* Get some buffers ready for event vq */ + struct virtio_scsi_event_node event_list[VIRTIO_SCSI_EVENT_LEN]; + struct virtio_scsi_target_state *tgt[]; }; @@ -202,6 +212,105 @@ static void virtscsi_ctrl_done(struct virtqueue *vq) spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags); }; +static int virtscsi_kick_event(struct virtio_scsi *vscsi, + struct virtio_scsi_event_node *event_node) +{ + int ret; + struct scatterlist sg; + unsigned long flags; + + sg_set_buf(&sg, &event_node->event, sizeof(struct virtio_scsi_event)); + + spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); + + ret = virtqueue_add_buf(vscsi->event_vq.vq, &sg, 0, 1, event_node, GFP_ATOMIC); + if (ret >= 0) + virtqueue_kick(vscsi->event_vq.vq); + + spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); + + return ret; +} + +static int virtscsi_kick_event_all(struct virtio_scsi *vscsi) +{ + int i; + + for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) { + vscsi->event_list[i].vscsi = vscsi; + virtscsi_kick_event(vscsi, &vscsi->event_list[i]); + } + + return 0; +} + +static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi) +{ + int i; + + for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) + cancel_work_sync(&vscsi->event_list[i].work); +} + +static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, + struct virtio_scsi_event *event) +{ + struct scsi_device *sdev; + struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); + unsigned int target = event->lun[1]; + unsigned int lun = (event->lun[2] << 8) | event->lun[3]; + + switch (event->reason) { + case VIRTIO_SCSI_EVT_RESET_RESCAN: + scsi_add_device(shost, 0, target, lun); + break; + case VIRTIO_SCSI_EVT_RESET_REMOVED: + sdev = scsi_device_lookup(shost, 0, target, lun); + if (sdev) { + scsi_remove_device(sdev); + scsi_device_put(sdev); + } else { + pr_err("SCSI device %d 0 %d %d not found\n", + shost->host_no, target, lun); + } + break; + default: + pr_info("Unsupport virtio scsi event reason %x\n", event->reason); + } +} + +static void virtscsi_handle_event(struct work_struct *work) +{ + struct virtio_scsi_event_node *event_node = + container_of(work, struct virtio_scsi_event_node, work); + struct virtio_scsi *vscsi = event_node->vscsi; + struct virtio_scsi_event *event = &event_node->event; + + if (event->event & VIRTIO_SCSI_T_EVENTS_MISSED) { + event->event &= ~VIRTIO_SCSI_T_EVENTS_MISSED; + scsi_scan_host(virtio_scsi_host(vscsi->vdev)); + } + + switch (event->event) { + case VIRTIO_SCSI_T_NO_EVENT: + break; + case VIRTIO_SCSI_T_TRANSPORT_RESET: + virtscsi_handle_transport_reset(vscsi, event); + break; + default: + pr_err("Unsupport virtio scsi event %x\n", event->event); + } + virtscsi_kick_event(vscsi, event_node); +} + +static void virtscsi_complete_event(void *buf) +{ + struct virtio_scsi_event_node *event_node = buf; + + INIT_WORK(&event_node->work, virtscsi_handle_event); + schedule_work(&event_node->work); +} + static void virtscsi_event_done(struct virtqueue *vq) { struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); @@ -209,7 +318,7 @@ static void virtscsi_event_done(struct virtqueue *vq) 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_event); spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); }; @@ -510,6 +619,9 @@ static int virtscsi_init(struct virtio_device *vdev, virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); + if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) + virtscsi_kick_event_all(vscsi); + /* We need to know how many segments before we allocate. */ sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; @@ -580,6 +692,10 @@ virtscsi_init_failed: static void __devexit virtscsi_remove(struct virtio_device *vdev) { struct Scsi_Host *shost = virtio_scsi_host(vdev); + struct virtio_scsi *vscsi = shost_priv(shost); + + if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) + virtscsi_cancel_event_work(vscsi); scsi_remove_host(shost); @@ -608,7 +724,13 @@ static struct virtio_device_id id_table[] = { { 0 }, }; +static unsigned int features[] = { + VIRTIO_SCSI_F_HOTPLUG +}; + static struct virtio_driver virtio_scsi_driver = { + .feature_table = features, + .feature_table_size = ARRAY_SIZE(features), .driver.name = KBUILD_MODNAME, .driver.owner = THIS_MODULE, .id_table = id_table, diff --git a/include/linux/virtio_scsi.h b/include/linux/virtio_scsi.h index 8ddeafdc0546..dc8d305b0e05 100644 --- a/include/linux/virtio_scsi.h +++ b/include/linux/virtio_scsi.h @@ -69,6 +69,10 @@ struct virtio_scsi_config { u32 max_lun; } __packed; +/* Feature Bits */ +#define VIRTIO_SCSI_F_INOUT 0 +#define VIRTIO_SCSI_F_HOTPLUG 1 + /* Response codes */ #define VIRTIO_SCSI_S_OK 0 #define VIRTIO_SCSI_S_OVERRUN 1 @@ -105,6 +109,11 @@ struct virtio_scsi_config { #define VIRTIO_SCSI_T_TRANSPORT_RESET 1 #define VIRTIO_SCSI_T_ASYNC_NOTIFY 2 +/* Reasons of transport reset event */ +#define VIRTIO_SCSI_EVT_RESET_HARD 0 +#define VIRTIO_SCSI_EVT_RESET_RESCAN 1 +#define VIRTIO_SCSI_EVT_RESET_REMOVED 2 + #define VIRTIO_SCSI_S_SIMPLE 0 #define VIRTIO_SCSI_S_ORDERED 1 #define VIRTIO_SCSI_S_HEAD 2 -- cgit v1.2.3 From 59057fbc37178f10a196ab7ec170b80273f75a47 Mon Sep 17 00:00:00 2001 From: Nicholas Bellinger Date: Wed, 11 Jul 2012 21:22:16 +0000 Subject: [SCSI] virtio-scsi: Add vdrv->scan for post VIRTIO_CONFIG_S_DRIVER_OK LUN scanning This patch changes virtio-scsi to use a new virtio_driver->scan() callback so that scsi_scan_host() can be properly invoked once virtio_dev_probe() has set add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK) to signal active virtio-ring operation, instead of from within virtscsi_probe(). This fixes a bug where SCSI LUN scanning for both virtio-scsi-raw and virtio-scsi/tcm_vhost setups was happening before VIRTIO_CONFIG_S_DRIVER_OK had been set, causing VIRTIO_SCSI_S_BAD_TARGET to occur. This fixes a bug with virtio-scsi/tcm_vhost where LUN scan was not detecting LUNs. Tested with virtio-scsi-raw + virtio-scsi/tcm_vhost w/ IBLOCK on 3.5-rc2 code. Signed-off-by: Nicholas Bellinger Acked-by: Paolo Bonzini Signed-off-by: James Bottomley --- drivers/scsi/virtio_scsi.c | 15 ++++++++++++--- drivers/virtio/virtio.c | 5 ++++- include/linux/virtio.h | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers/scsi/virtio_scsi.c') diff --git a/drivers/scsi/virtio_scsi.c b/drivers/scsi/virtio_scsi.c index ae3bef7c523f..c7030fbee79c 100644 --- a/drivers/scsi/virtio_scsi.c +++ b/drivers/scsi/virtio_scsi.c @@ -571,6 +571,13 @@ static struct virtio_scsi_target_state *virtscsi_alloc_tgt( return tgt; } +static void virtscsi_scan(struct virtio_device *vdev) +{ + struct Scsi_Host *shost = (struct Scsi_Host *)vdev->priv; + + scsi_scan_host(shost); +} + static void virtscsi_remove_vqs(struct virtio_device *vdev) { struct Scsi_Host *sh = virtio_scsi_host(vdev); @@ -677,9 +684,10 @@ static int __devinit virtscsi_probe(struct virtio_device *vdev) err = scsi_add_host(shost, &vdev->dev); if (err) goto scsi_add_host_failed; - - scsi_scan_host(shost); - + /* + * scsi_scan_host() happens in virtscsi_scan() via virtio_driver->scan() + * after VIRTIO_CONFIG_S_DRIVER_OK has been set.. + */ return 0; scsi_add_host_failed: @@ -735,6 +743,7 @@ static struct virtio_driver virtio_scsi_driver = { .driver.owner = THIS_MODULE, .id_table = id_table, .probe = virtscsi_probe, + .scan = virtscsi_scan, #ifdef CONFIG_PM .freeze = virtscsi_freeze, .restore = virtscsi_restore, diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index f3558070e375..c3b3f7f0d9d1 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -141,8 +141,11 @@ static int virtio_dev_probe(struct device *_d) err = drv->probe(dev); if (err) add_status(dev, VIRTIO_CONFIG_S_FAILED); - else + else { add_status(dev, VIRTIO_CONFIG_S_DRIVER_OK); + if (drv->scan) + drv->scan(dev); + } return err; } diff --git a/include/linux/virtio.h b/include/linux/virtio.h index 8efd28ae5597..a1ba8bbd9fbe 100644 --- a/include/linux/virtio.h +++ b/include/linux/virtio.h @@ -92,6 +92,7 @@ struct virtio_driver { const unsigned int *feature_table; unsigned int feature_table_size; int (*probe)(struct virtio_device *dev); + void (*scan)(struct virtio_device *dev); void (*remove)(struct virtio_device *dev); void (*config_changed)(struct virtio_device *dev); #ifdef CONFIG_PM -- cgit v1.2.3