diff options
author | Cong Meng <mc@linux.vnet.ibm.com> | 2012-07-05 05:06:43 -0400 |
---|---|---|
committer | James Bottomley <JBottomley@Parallels.com> | 2012-07-20 03:58:58 -0400 |
commit | 365a7150094114a0f8ef0b6164e6b04b519039e8 (patch) | |
tree | dbe3630391256c1771de226d4d74d2ce5ab50ab6 /drivers | |
parent | 2bd37f0fde99cbf8b78fb55f1128e8c3a63cf1da (diff) |
[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 <senwang@linux.vnet.ibm.com>
Signed-off-by: Cong Meng <mc@linux.vnet.ibm.com>
Acked-by: Paolo Bonzini <pbonzini@redhat.com>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/scsi/virtio_scsi.c | 124 |
1 files changed, 123 insertions, 1 deletions
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 @@ | |||
25 | #include <scsi/scsi_cmnd.h> | 25 | #include <scsi/scsi_cmnd.h> |
26 | 26 | ||
27 | #define VIRTIO_SCSI_MEMPOOL_SZ 64 | 27 | #define VIRTIO_SCSI_MEMPOOL_SZ 64 |
28 | #define VIRTIO_SCSI_EVENT_LEN 8 | ||
28 | 29 | ||
29 | /* Command queue element */ | 30 | /* Command queue element */ |
30 | struct virtio_scsi_cmd { | 31 | struct virtio_scsi_cmd { |
@@ -43,6 +44,12 @@ struct virtio_scsi_cmd { | |||
43 | } resp; | 44 | } resp; |
44 | } ____cacheline_aligned_in_smp; | 45 | } ____cacheline_aligned_in_smp; |
45 | 46 | ||
47 | struct virtio_scsi_event_node { | ||
48 | struct virtio_scsi *vscsi; | ||
49 | struct virtio_scsi_event event; | ||
50 | struct work_struct work; | ||
51 | }; | ||
52 | |||
46 | struct virtio_scsi_vq { | 53 | struct virtio_scsi_vq { |
47 | /* Protects vq */ | 54 | /* Protects vq */ |
48 | spinlock_t vq_lock; | 55 | spinlock_t vq_lock; |
@@ -67,6 +74,9 @@ struct virtio_scsi { | |||
67 | struct virtio_scsi_vq event_vq; | 74 | struct virtio_scsi_vq event_vq; |
68 | struct virtio_scsi_vq req_vq; | 75 | struct virtio_scsi_vq req_vq; |
69 | 76 | ||
77 | /* Get some buffers ready for event vq */ | ||
78 | struct virtio_scsi_event_node event_list[VIRTIO_SCSI_EVENT_LEN]; | ||
79 | |||
70 | struct virtio_scsi_target_state *tgt[]; | 80 | struct virtio_scsi_target_state *tgt[]; |
71 | }; | 81 | }; |
72 | 82 | ||
@@ -202,6 +212,105 @@ static void virtscsi_ctrl_done(struct virtqueue *vq) | |||
202 | spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags); | 212 | spin_unlock_irqrestore(&vscsi->ctrl_vq.vq_lock, flags); |
203 | }; | 213 | }; |
204 | 214 | ||
215 | static int virtscsi_kick_event(struct virtio_scsi *vscsi, | ||
216 | struct virtio_scsi_event_node *event_node) | ||
217 | { | ||
218 | int ret; | ||
219 | struct scatterlist sg; | ||
220 | unsigned long flags; | ||
221 | |||
222 | sg_set_buf(&sg, &event_node->event, sizeof(struct virtio_scsi_event)); | ||
223 | |||
224 | spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); | ||
225 | |||
226 | ret = virtqueue_add_buf(vscsi->event_vq.vq, &sg, 0, 1, event_node, GFP_ATOMIC); | ||
227 | if (ret >= 0) | ||
228 | virtqueue_kick(vscsi->event_vq.vq); | ||
229 | |||
230 | spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); | ||
231 | |||
232 | return ret; | ||
233 | } | ||
234 | |||
235 | static int virtscsi_kick_event_all(struct virtio_scsi *vscsi) | ||
236 | { | ||
237 | int i; | ||
238 | |||
239 | for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) { | ||
240 | vscsi->event_list[i].vscsi = vscsi; | ||
241 | virtscsi_kick_event(vscsi, &vscsi->event_list[i]); | ||
242 | } | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | static void virtscsi_cancel_event_work(struct virtio_scsi *vscsi) | ||
248 | { | ||
249 | int i; | ||
250 | |||
251 | for (i = 0; i < VIRTIO_SCSI_EVENT_LEN; i++) | ||
252 | cancel_work_sync(&vscsi->event_list[i].work); | ||
253 | } | ||
254 | |||
255 | static void virtscsi_handle_transport_reset(struct virtio_scsi *vscsi, | ||
256 | struct virtio_scsi_event *event) | ||
257 | { | ||
258 | struct scsi_device *sdev; | ||
259 | struct Scsi_Host *shost = virtio_scsi_host(vscsi->vdev); | ||
260 | unsigned int target = event->lun[1]; | ||
261 | unsigned int lun = (event->lun[2] << 8) | event->lun[3]; | ||
262 | |||
263 | switch (event->reason) { | ||
264 | case VIRTIO_SCSI_EVT_RESET_RESCAN: | ||
265 | scsi_add_device(shost, 0, target, lun); | ||
266 | break; | ||
267 | case VIRTIO_SCSI_EVT_RESET_REMOVED: | ||
268 | sdev = scsi_device_lookup(shost, 0, target, lun); | ||
269 | if (sdev) { | ||
270 | scsi_remove_device(sdev); | ||
271 | scsi_device_put(sdev); | ||
272 | } else { | ||
273 | pr_err("SCSI device %d 0 %d %d not found\n", | ||
274 | shost->host_no, target, lun); | ||
275 | } | ||
276 | break; | ||
277 | default: | ||
278 | pr_info("Unsupport virtio scsi event reason %x\n", event->reason); | ||
279 | } | ||
280 | } | ||
281 | |||
282 | static void virtscsi_handle_event(struct work_struct *work) | ||
283 | { | ||
284 | struct virtio_scsi_event_node *event_node = | ||
285 | container_of(work, struct virtio_scsi_event_node, work); | ||
286 | struct virtio_scsi *vscsi = event_node->vscsi; | ||
287 | struct virtio_scsi_event *event = &event_node->event; | ||
288 | |||
289 | if (event->event & VIRTIO_SCSI_T_EVENTS_MISSED) { | ||
290 | event->event &= ~VIRTIO_SCSI_T_EVENTS_MISSED; | ||
291 | scsi_scan_host(virtio_scsi_host(vscsi->vdev)); | ||
292 | } | ||
293 | |||
294 | switch (event->event) { | ||
295 | case VIRTIO_SCSI_T_NO_EVENT: | ||
296 | break; | ||
297 | case VIRTIO_SCSI_T_TRANSPORT_RESET: | ||
298 | virtscsi_handle_transport_reset(vscsi, event); | ||
299 | break; | ||
300 | default: | ||
301 | pr_err("Unsupport virtio scsi event %x\n", event->event); | ||
302 | } | ||
303 | virtscsi_kick_event(vscsi, event_node); | ||
304 | } | ||
305 | |||
306 | static void virtscsi_complete_event(void *buf) | ||
307 | { | ||
308 | struct virtio_scsi_event_node *event_node = buf; | ||
309 | |||
310 | INIT_WORK(&event_node->work, virtscsi_handle_event); | ||
311 | schedule_work(&event_node->work); | ||
312 | } | ||
313 | |||
205 | static void virtscsi_event_done(struct virtqueue *vq) | 314 | static void virtscsi_event_done(struct virtqueue *vq) |
206 | { | 315 | { |
207 | struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); | 316 | struct Scsi_Host *sh = virtio_scsi_host(vq->vdev); |
@@ -209,7 +318,7 @@ static void virtscsi_event_done(struct virtqueue *vq) | |||
209 | unsigned long flags; | 318 | unsigned long flags; |
210 | 319 | ||
211 | spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); | 320 | spin_lock_irqsave(&vscsi->event_vq.vq_lock, flags); |
212 | virtscsi_vq_done(vq, virtscsi_complete_free); | 321 | virtscsi_vq_done(vq, virtscsi_complete_event); |
213 | spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); | 322 | spin_unlock_irqrestore(&vscsi->event_vq.vq_lock, flags); |
214 | }; | 323 | }; |
215 | 324 | ||
@@ -510,6 +619,9 @@ static int virtscsi_init(struct virtio_device *vdev, | |||
510 | virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); | 619 | virtscsi_config_set(vdev, cdb_size, VIRTIO_SCSI_CDB_SIZE); |
511 | virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); | 620 | virtscsi_config_set(vdev, sense_size, VIRTIO_SCSI_SENSE_SIZE); |
512 | 621 | ||
622 | if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) | ||
623 | virtscsi_kick_event_all(vscsi); | ||
624 | |||
513 | /* We need to know how many segments before we allocate. */ | 625 | /* We need to know how many segments before we allocate. */ |
514 | sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; | 626 | sg_elems = virtscsi_config_get(vdev, seg_max) ?: 1; |
515 | 627 | ||
@@ -580,6 +692,10 @@ virtscsi_init_failed: | |||
580 | static void __devexit virtscsi_remove(struct virtio_device *vdev) | 692 | static void __devexit virtscsi_remove(struct virtio_device *vdev) |
581 | { | 693 | { |
582 | struct Scsi_Host *shost = virtio_scsi_host(vdev); | 694 | struct Scsi_Host *shost = virtio_scsi_host(vdev); |
695 | struct virtio_scsi *vscsi = shost_priv(shost); | ||
696 | |||
697 | if (virtio_has_feature(vdev, VIRTIO_SCSI_F_HOTPLUG)) | ||
698 | virtscsi_cancel_event_work(vscsi); | ||
583 | 699 | ||
584 | scsi_remove_host(shost); | 700 | scsi_remove_host(shost); |
585 | 701 | ||
@@ -608,7 +724,13 @@ static struct virtio_device_id id_table[] = { | |||
608 | { 0 }, | 724 | { 0 }, |
609 | }; | 725 | }; |
610 | 726 | ||
727 | static unsigned int features[] = { | ||
728 | VIRTIO_SCSI_F_HOTPLUG | ||
729 | }; | ||
730 | |||
611 | static struct virtio_driver virtio_scsi_driver = { | 731 | static struct virtio_driver virtio_scsi_driver = { |
732 | .feature_table = features, | ||
733 | .feature_table_size = ARRAY_SIZE(features), | ||
612 | .driver.name = KBUILD_MODNAME, | 734 | .driver.name = KBUILD_MODNAME, |
613 | .driver.owner = THIS_MODULE, | 735 | .driver.owner = THIS_MODULE, |
614 | .id_table = id_table, | 736 | .id_table = id_table, |