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 #include "stdio.h" 23 24 /* Context: QEMU global mutex held */ 25 void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) 26 { 27 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 28 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 29 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 30 31 assert(!s->ctx); 32 s->ctx = iothread_get_aio_context(vs->conf.iothread); 33 34 /* Don't try if transport does not support notifiers. */ 35 if (!k->set_guest_notifiers || !k->set_host_notifier) { 36 fprintf(stderr, "virtio-scsi: Failed to set iothread " 37 "(transport does not support notifiers)"); 38 exit(1); 39 } 40 } 41 42 static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, 43 VirtQueue *vq, 44 EventNotifierHandler *handler, 45 int n) 46 { 47 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 48 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 49 VirtIOSCSIVring *r; 50 int rc; 51 52 /* Set up virtqueue notify */ 53 rc = k->set_host_notifier(qbus->parent, n, true); 54 if (rc != 0) { 55 fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", 56 rc); 57 s->dataplane_fenced = true; 58 return NULL; 59 } 60 61 r = g_new(VirtIOSCSIVring, 1); 62 r->host_notifier = *virtio_queue_get_host_notifier(vq); 63 r->guest_notifier = *virtio_queue_get_guest_notifier(vq); 64 aio_set_event_notifier(s->ctx, &r->host_notifier, true, handler); 65 66 r->parent = s; 67 68 if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { 69 fprintf(stderr, "virtio-scsi: VRing setup failed\n"); 70 goto fail_vring; 71 } 72 return r; 73 74 fail_vring: 75 aio_set_event_notifier(s->ctx, &r->host_notifier, true, NULL); 76 k->set_host_notifier(qbus->parent, n, false); 77 g_free(r); 78 return NULL; 79 } 80 81 VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, 82 VirtIOSCSIVring *vring) 83 { 84 VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL); 85 int r; 86 87 req->vring = vring; 88 r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem); 89 if (r < 0) { 90 virtio_scsi_free_req(req); 91 req = NULL; 92 } 93 return req; 94 } 95 96 void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) 97 { 98 VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); 99 100 vring_push(vdev, &req->vring->vring, &req->elem, 101 req->qsgl.size + req->resp_iov.size); 102 103 if (vring_should_notify(vdev, &req->vring->vring)) { 104 event_notifier_set(&req->vring->guest_notifier); 105 } 106 } 107 108 static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier) 109 { 110 VirtIOSCSIVring *vring = container_of(notifier, 111 VirtIOSCSIVring, host_notifier); 112 VirtIOSCSI *s = VIRTIO_SCSI(vring->parent); 113 VirtIOSCSIReq *req; 114 115 event_notifier_test_and_clear(notifier); 116 while ((req = virtio_scsi_pop_req_vring(s, vring))) { 117 virtio_scsi_handle_ctrl_req(s, req); 118 } 119 } 120 121 static void virtio_scsi_iothread_handle_event(EventNotifier *notifier) 122 { 123 VirtIOSCSIVring *vring = container_of(notifier, 124 VirtIOSCSIVring, host_notifier); 125 VirtIOSCSI *s = vring->parent; 126 VirtIODevice *vdev = VIRTIO_DEVICE(s); 127 128 event_notifier_test_and_clear(notifier); 129 130 if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { 131 return; 132 } 133 134 if (s->events_dropped) { 135 virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); 136 } 137 } 138 139 static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) 140 { 141 VirtIOSCSIVring *vring = container_of(notifier, 142 VirtIOSCSIVring, host_notifier); 143 VirtIOSCSI *s = (VirtIOSCSI *)vring->parent; 144 VirtIOSCSIReq *req, *next; 145 QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); 146 147 event_notifier_test_and_clear(notifier); 148 while ((req = virtio_scsi_pop_req_vring(s, vring))) { 149 if (virtio_scsi_handle_cmd_req_prepare(s, req)) { 150 QTAILQ_INSERT_TAIL(&reqs, req, next); 151 } 152 } 153 154 QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { 155 virtio_scsi_handle_cmd_req_submit(s, req); 156 } 157 } 158 159 /* assumes s->ctx held */ 160 static void virtio_scsi_clear_aio(VirtIOSCSI *s) 161 { 162 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 163 int i; 164 165 if (s->ctrl_vring) { 166 aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, 167 true, NULL); 168 } 169 if (s->event_vring) { 170 aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, 171 true, NULL); 172 } 173 if (s->cmd_vrings) { 174 for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { 175 aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, 176 true, NULL); 177 } 178 } 179 } 180 181 static void virtio_scsi_vring_teardown(VirtIOSCSI *s) 182 { 183 VirtIODevice *vdev = VIRTIO_DEVICE(s); 184 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 185 int i; 186 187 if (s->ctrl_vring) { 188 vring_teardown(&s->ctrl_vring->vring, vdev, 0); 189 g_free(s->ctrl_vring); 190 s->ctrl_vring = NULL; 191 } 192 if (s->event_vring) { 193 vring_teardown(&s->event_vring->vring, vdev, 1); 194 g_free(s->event_vring); 195 s->event_vring = NULL; 196 } 197 if (s->cmd_vrings) { 198 for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { 199 vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); 200 g_free(s->cmd_vrings[i]); 201 s->cmd_vrings[i] = NULL; 202 } 203 free(s->cmd_vrings); 204 s->cmd_vrings = NULL; 205 } 206 } 207 208 /* Context: QEMU global mutex held */ 209 void virtio_scsi_dataplane_start(VirtIOSCSI *s) 210 { 211 int i; 212 int rc; 213 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 214 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 215 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 216 217 if (s->dataplane_started || 218 s->dataplane_starting || 219 s->dataplane_fenced || 220 s->ctx != iothread_get_aio_context(vs->conf.iothread)) { 221 return; 222 } 223 224 s->dataplane_starting = true; 225 226 /* Set up guest notifier (irq) */ 227 rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); 228 if (rc != 0) { 229 fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " 230 "ensure -enable-kvm is set\n", rc); 231 s->dataplane_fenced = true; 232 goto fail_guest_notifiers; 233 } 234 235 aio_context_acquire(s->ctx); 236 s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, 237 virtio_scsi_iothread_handle_ctrl, 238 0); 239 if (!s->ctrl_vring) { 240 goto fail_vrings; 241 } 242 s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, 243 virtio_scsi_iothread_handle_event, 244 1); 245 if (!s->event_vring) { 246 goto fail_vrings; 247 } 248 s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues); 249 for (i = 0; i < vs->conf.num_queues; i++) { 250 s->cmd_vrings[i] = 251 virtio_scsi_vring_init(s, vs->cmd_vqs[i], 252 virtio_scsi_iothread_handle_cmd, 253 i + 2); 254 if (!s->cmd_vrings[i]) { 255 goto fail_vrings; 256 } 257 } 258 259 s->dataplane_starting = false; 260 s->dataplane_started = true; 261 aio_context_release(s->ctx); 262 return; 263 264 fail_vrings: 265 virtio_scsi_clear_aio(s); 266 aio_context_release(s->ctx); 267 virtio_scsi_vring_teardown(s); 268 for (i = 0; i < vs->conf.num_queues + 2; i++) { 269 k->set_host_notifier(qbus->parent, i, false); 270 } 271 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 272 fail_guest_notifiers: 273 s->dataplane_starting = false; 274 } 275 276 /* Context: QEMU global mutex held */ 277 void virtio_scsi_dataplane_stop(VirtIOSCSI *s) 278 { 279 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 280 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 281 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 282 int i; 283 284 /* Better luck next time. */ 285 if (s->dataplane_fenced) { 286 s->dataplane_fenced = false; 287 return; 288 } 289 if (!s->dataplane_started || s->dataplane_stopping) { 290 return; 291 } 292 s->dataplane_stopping = true; 293 assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); 294 295 aio_context_acquire(s->ctx); 296 297 aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, 298 true, NULL); 299 aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, 300 true, NULL); 301 for (i = 0; i < vs->conf.num_queues; i++) { 302 aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, 303 true, NULL); 304 } 305 306 blk_drain_all(); /* ensure there are no in-flight requests */ 307 308 aio_context_release(s->ctx); 309 310 /* Sync vring state back to virtqueue so that non-dataplane request 311 * processing can continue when we disable the host notifier below. 312 */ 313 virtio_scsi_vring_teardown(s); 314 315 for (i = 0; i < vs->conf.num_queues + 2; i++) { 316 k->set_host_notifier(qbus->parent, i, false); 317 } 318 319 /* Clean up guest notifier (irq) */ 320 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 321 s->dataplane_stopping = false; 322 s->dataplane_started = false; 323 } 324