1 /* 2 * Virtio SCSI dataplane 3 * 4 * Copyright Red Hat, Inc. 2014 5 * 6 * Authors: 7 * Fam Zheng <famz@redhat.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or later. 10 * See the COPYING file in the top-level directory. 11 * 12 */ 13 14 #include "qemu/osdep.h" 15 #include "hw/virtio/virtio-scsi.h" 16 #include "qemu/error-report.h" 17 #include "sysemu/block-backend.h" 18 #include <hw/scsi/scsi.h> 19 #include <block/scsi.h> 20 #include <hw/virtio/virtio-bus.h> 21 #include "hw/virtio/virtio-access.h" 22 23 /* Context: QEMU global mutex held */ 24 void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) 25 { 26 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 27 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 28 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 29 30 assert(!s->ctx); 31 s->ctx = iothread_get_aio_context(vs->conf.iothread); 32 33 /* Don't try if transport does not support notifiers. */ 34 if (!k->set_guest_notifiers || !k->set_host_notifier) { 35 fprintf(stderr, "virtio-scsi: Failed to set iothread " 36 "(transport does not support notifiers)"); 37 exit(1); 38 } 39 } 40 41 static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n) 42 { 43 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 44 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 45 int rc; 46 47 /* Set up virtqueue notify */ 48 rc = k->set_host_notifier(qbus->parent, n, true); 49 if (rc != 0) { 50 fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", 51 rc); 52 s->dataplane_fenced = true; 53 return rc; 54 } 55 56 virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, true, true); 57 return 0; 58 } 59 60 void virtio_scsi_dataplane_notify(VirtIODevice *vdev, VirtIOSCSIReq *req) 61 { 62 if (virtio_should_notify(vdev, req->vq)) { 63 event_notifier_set(virtio_queue_get_guest_notifier(req->vq)); 64 } 65 } 66 67 /* assumes s->ctx held */ 68 static void virtio_scsi_clear_aio(VirtIOSCSI *s) 69 { 70 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 71 int i; 72 73 virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, false, false); 74 virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, false, false); 75 for (i = 0; i < vs->conf.num_queues; i++) { 76 virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, false, false); 77 } 78 } 79 80 /* Context: QEMU global mutex held */ 81 void virtio_scsi_dataplane_start(VirtIOSCSI *s) 82 { 83 int i; 84 int rc; 85 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 86 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 87 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 88 89 if (s->dataplane_started || 90 s->dataplane_starting || 91 s->dataplane_fenced || 92 s->ctx != iothread_get_aio_context(vs->conf.iothread)) { 93 return; 94 } 95 96 s->dataplane_starting = true; 97 98 /* Set up guest notifier (irq) */ 99 rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); 100 if (rc != 0) { 101 fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " 102 "ensure -enable-kvm is set\n", rc); 103 goto fail_guest_notifiers; 104 } 105 106 aio_context_acquire(s->ctx); 107 rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0); 108 if (rc) { 109 goto fail_vrings; 110 } 111 rc = virtio_scsi_vring_init(s, vs->event_vq, 1); 112 if (rc) { 113 goto fail_vrings; 114 } 115 for (i = 0; i < vs->conf.num_queues; i++) { 116 rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2); 117 if (rc) { 118 goto fail_vrings; 119 } 120 } 121 122 s->dataplane_starting = false; 123 s->dataplane_started = true; 124 aio_context_release(s->ctx); 125 return; 126 127 fail_vrings: 128 virtio_scsi_clear_aio(s); 129 aio_context_release(s->ctx); 130 for (i = 0; i < vs->conf.num_queues + 2; i++) { 131 k->set_host_notifier(qbus->parent, i, false); 132 } 133 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 134 fail_guest_notifiers: 135 s->dataplane_fenced = true; 136 s->dataplane_starting = false; 137 s->dataplane_started = true; 138 } 139 140 /* Context: QEMU global mutex held */ 141 void virtio_scsi_dataplane_stop(VirtIOSCSI *s) 142 { 143 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 144 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 145 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 146 int i; 147 148 if (!s->dataplane_started || s->dataplane_stopping) { 149 return; 150 } 151 152 /* Better luck next time. */ 153 if (s->dataplane_fenced) { 154 s->dataplane_fenced = false; 155 s->dataplane_started = false; 156 return; 157 } 158 s->dataplane_stopping = true; 159 assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); 160 161 aio_context_acquire(s->ctx); 162 163 virtio_scsi_clear_aio(s); 164 165 blk_drain_all(); /* ensure there are no in-flight requests */ 166 167 aio_context_release(s->ctx); 168 169 for (i = 0; i < vs->conf.num_queues + 2; i++) { 170 k->set_host_notifier(qbus->parent, i, false); 171 } 172 173 /* Clean up guest notifier (irq) */ 174 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 175 s->dataplane_stopping = false; 176 s->dataplane_started = false; 177 } 178