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 "hw/virtio/virtio-scsi.h" 15 #include "qemu/error-report.h" 16 #include "sysemu/block-backend.h" 17 #include <hw/scsi/scsi.h> 18 #include <block/scsi.h> 19 #include <hw/virtio/virtio-bus.h> 20 #include "hw/virtio/virtio-access.h" 21 #include "stdio.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 VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, 42 VirtQueue *vq, 43 EventNotifierHandler *handler, 44 int n) 45 { 46 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 47 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 48 VirtIOSCSIVring *r = g_slice_new(VirtIOSCSIVring); 49 50 /* Set up virtqueue notify */ 51 if (k->set_host_notifier(qbus->parent, n, true) != 0) { 52 fprintf(stderr, "virtio-scsi: Failed to set host notifier\n"); 53 exit(1); 54 } 55 r->host_notifier = *virtio_queue_get_host_notifier(vq); 56 r->guest_notifier = *virtio_queue_get_guest_notifier(vq); 57 aio_set_event_notifier(s->ctx, &r->host_notifier, handler); 58 59 r->parent = s; 60 61 if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { 62 fprintf(stderr, "virtio-scsi: VRing setup failed\n"); 63 exit(1); 64 } 65 return r; 66 } 67 68 VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, 69 VirtIOSCSIVring *vring) 70 { 71 VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL); 72 int r; 73 74 req->vring = vring; 75 r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem); 76 if (r < 0) { 77 virtio_scsi_free_req(req); 78 req = NULL; 79 } 80 return req; 81 } 82 83 void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) 84 { 85 vring_push(&req->vring->vring, &req->elem, 86 req->qsgl.size + req->resp_iov.size); 87 event_notifier_set(&req->vring->guest_notifier); 88 } 89 90 static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier) 91 { 92 VirtIOSCSIVring *vring = container_of(notifier, 93 VirtIOSCSIVring, host_notifier); 94 VirtIOSCSI *s = VIRTIO_SCSI(vring->parent); 95 VirtIOSCSIReq *req; 96 97 event_notifier_test_and_clear(notifier); 98 while ((req = virtio_scsi_pop_req_vring(s, vring))) { 99 virtio_scsi_handle_ctrl_req(s, req); 100 } 101 } 102 103 static void virtio_scsi_iothread_handle_event(EventNotifier *notifier) 104 { 105 VirtIOSCSIVring *vring = container_of(notifier, 106 VirtIOSCSIVring, host_notifier); 107 VirtIOSCSI *s = vring->parent; 108 VirtIODevice *vdev = VIRTIO_DEVICE(s); 109 110 event_notifier_test_and_clear(notifier); 111 112 if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 113 return; 114 } 115 116 if (s->events_dropped) { 117 virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); 118 } 119 } 120 121 static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) 122 { 123 VirtIOSCSIVring *vring = container_of(notifier, 124 VirtIOSCSIVring, host_notifier); 125 VirtIOSCSI *s = (VirtIOSCSI *)vring->parent; 126 VirtIOSCSIReq *req, *next; 127 QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); 128 129 event_notifier_test_and_clear(notifier); 130 while ((req = virtio_scsi_pop_req_vring(s, vring))) { 131 if (virtio_scsi_handle_cmd_req_prepare(s, req)) { 132 QTAILQ_INSERT_TAIL(&reqs, req, next); 133 } 134 } 135 136 QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { 137 virtio_scsi_handle_cmd_req_submit(s, req); 138 } 139 } 140 141 /* Context: QEMU global mutex held */ 142 void virtio_scsi_dataplane_start(VirtIOSCSI *s) 143 { 144 int i; 145 int rc; 146 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 147 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 148 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 149 150 if (s->dataplane_started || 151 s->dataplane_starting || 152 s->ctx != iothread_get_aio_context(vs->conf.iothread)) { 153 return; 154 } 155 156 s->dataplane_starting = true; 157 158 /* Set up guest notifier (irq) */ 159 rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); 160 if (rc != 0) { 161 fprintf(stderr, "virtio-scsi: Failed to set guest notifiers, " 162 "ensure -enable-kvm is set\n"); 163 exit(1); 164 } 165 166 aio_context_acquire(s->ctx); 167 s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, 168 virtio_scsi_iothread_handle_ctrl, 169 0); 170 s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, 171 virtio_scsi_iothread_handle_event, 172 1); 173 s->cmd_vrings = g_malloc0(sizeof(VirtIOSCSIVring) * vs->conf.num_queues); 174 for (i = 0; i < vs->conf.num_queues; i++) { 175 s->cmd_vrings[i] = 176 virtio_scsi_vring_init(s, vs->cmd_vqs[i], 177 virtio_scsi_iothread_handle_cmd, 178 i + 2); 179 } 180 181 aio_context_release(s->ctx); 182 s->dataplane_starting = false; 183 s->dataplane_started = true; 184 } 185 186 /* Context: QEMU global mutex held */ 187 void virtio_scsi_dataplane_stop(VirtIOSCSI *s) 188 { 189 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 190 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 191 VirtIODevice *vdev = VIRTIO_DEVICE(s); 192 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 193 int i; 194 195 if (!s->dataplane_started || s->dataplane_stopping) { 196 return; 197 } 198 s->dataplane_stopping = true; 199 assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); 200 201 aio_context_acquire(s->ctx); 202 203 aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); 204 aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); 205 for (i = 0; i < vs->conf.num_queues; i++) { 206 aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); 207 } 208 209 blk_drain_all(); /* ensure there are no in-flight requests */ 210 211 aio_context_release(s->ctx); 212 213 /* Sync vring state back to virtqueue so that non-dataplane request 214 * processing can continue when we disable the host notifier below. 215 */ 216 vring_teardown(&s->ctrl_vring->vring, vdev, 0); 217 vring_teardown(&s->event_vring->vring, vdev, 1); 218 for (i = 0; i < vs->conf.num_queues; i++) { 219 vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); 220 } 221 222 for (i = 0; i < vs->conf.num_queues + 2; i++) { 223 k->set_host_notifier(qbus->parent, i, false); 224 } 225 226 /* Clean up guest notifier (irq) */ 227 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 228 s->dataplane_stopping = false; 229 s->dataplane_started = false; 230 } 231