aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi
diff options
context:
space:
mode:
authorCong Meng <mc@linux.vnet.ibm.com>2012-07-05 05:06:43 -0400
committerJames Bottomley <JBottomley@Parallels.com>2012-07-20 03:58:58 -0400
commit365a7150094114a0f8ef0b6164e6b04b519039e8 (patch)
treedbe3630391256c1771de226d4d74d2ce5ab50ab6 /drivers/scsi
parent2bd37f0fde99cbf8b78fb55f1128e8c3a63cf1da (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/scsi')
-rw-r--r--drivers/scsi/virtio_scsi.c124
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 */
30struct virtio_scsi_cmd { 31struct 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
47struct virtio_scsi_event_node {
48 struct virtio_scsi *vscsi;
49 struct virtio_scsi_event event;
50 struct work_struct work;
51};
52
46struct virtio_scsi_vq { 53struct 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
215static 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
235static 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
247static 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
255static 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
282static 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
306static 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
205static void virtscsi_event_done(struct virtqueue *vq) 314static 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:
580static void __devexit virtscsi_remove(struct virtio_device *vdev) 692static 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
727static unsigned int features[] = {
728 VIRTIO_SCSI_F_HOTPLUG
729};
730
611static struct virtio_driver virtio_scsi_driver = { 731static 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,