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