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 "qapi/error.h" 16 #include "hw/virtio/virtio-scsi.h" 17 #include "qemu/error-report.h" 18 #include "system/block-backend.h" 19 #include "hw/scsi/scsi.h" 20 #include "scsi/constants.h" 21 #include "hw/virtio/iothread-vq-mapping.h" 22 #include "hw/virtio/virtio-bus.h" 23 24 /* Context: BQL held */ 25 void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) 26 { 27 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 28 VirtIODevice *vdev = VIRTIO_DEVICE(s); 29 BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); 30 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 31 32 if (vs->conf.iothread && vs->conf.iothread_vq_mapping_list) { 33 error_setg(errp, 34 "iothread and iothread-vq-mapping properties cannot be set " 35 "at the same time"); 36 return; 37 } 38 39 if (vs->conf.iothread || vs->conf.iothread_vq_mapping_list) { 40 if (!k->set_guest_notifiers || !k->ioeventfd_assign) { 41 error_setg(errp, 42 "device is incompatible with iothread " 43 "(transport does not support notifiers)"); 44 return; 45 } 46 if (!virtio_device_ioeventfd_enabled(vdev)) { 47 error_setg(errp, "ioeventfd is required for iothread"); 48 return; 49 } 50 } 51 52 s->vq_aio_context = g_new(AioContext *, vs->conf.num_queues + 53 VIRTIO_SCSI_VQ_NUM_FIXED); 54 55 /* 56 * Handle the ctrl virtqueue in the main loop thread where device resets 57 * can be performed. 58 */ 59 s->vq_aio_context[0] = qemu_get_aio_context(); 60 61 /* 62 * Handle the event virtqueue in the main loop thread where its no_poll 63 * behavior won't stop IOThread polling. 64 */ 65 s->vq_aio_context[1] = qemu_get_aio_context(); 66 67 if (vs->conf.iothread_vq_mapping_list) { 68 if (!iothread_vq_mapping_apply(vs->conf.iothread_vq_mapping_list, 69 &s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED], 70 vs->conf.num_queues, errp)) { 71 g_free(s->vq_aio_context); 72 s->vq_aio_context = NULL; 73 return; 74 } 75 } else if (vs->conf.iothread) { 76 AioContext *ctx = iothread_get_aio_context(vs->conf.iothread); 77 for (uint16_t i = 0; i < vs->conf.num_queues; i++) { 78 s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; 79 } 80 81 /* Released in virtio_scsi_dataplane_cleanup() */ 82 object_ref(OBJECT(vs->conf.iothread)); 83 } else { 84 AioContext *ctx = qemu_get_aio_context(); 85 for (unsigned i = 0; i < vs->conf.num_queues; i++) { 86 s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i] = ctx; 87 } 88 } 89 } 90 91 /* Context: BQL held */ 92 void virtio_scsi_dataplane_cleanup(VirtIOSCSI *s) 93 { 94 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); 95 96 if (vs->conf.iothread_vq_mapping_list) { 97 iothread_vq_mapping_cleanup(vs->conf.iothread_vq_mapping_list); 98 } 99 100 if (vs->conf.iothread) { 101 object_unref(OBJECT(vs->conf.iothread)); 102 } 103 104 g_free(s->vq_aio_context); 105 s->vq_aio_context = NULL; 106 } 107 108 static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) 109 { 110 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); 111 int rc; 112 113 /* Set up virtqueue notify */ 114 rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); 115 if (rc != 0) { 116 fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", 117 rc); 118 s->dataplane_fenced = true; 119 return rc; 120 } 121 122 return 0; 123 } 124 125 /* Context: BH in IOThread */ 126 static void virtio_scsi_dataplane_stop_vq_bh(void *opaque) 127 { 128 AioContext *ctx = qemu_get_current_aio_context(); 129 VirtQueue *vq = opaque; 130 EventNotifier *host_notifier; 131 132 virtio_queue_aio_detach_host_notifier(vq, ctx); 133 host_notifier = virtio_queue_get_host_notifier(vq); 134 135 /* 136 * Test and clear notifier after disabling event, in case poll callback 137 * didn't have time to run. 138 */ 139 virtio_queue_host_notifier_read(host_notifier); 140 } 141 142 /* Context: BQL held */ 143 int virtio_scsi_dataplane_start(VirtIODevice *vdev) 144 { 145 int i; 146 int rc; 147 int vq_init_count = 0; 148 BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); 149 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 150 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); 151 VirtIOSCSI *s = VIRTIO_SCSI(vdev); 152 153 if (s->dataplane_started || 154 s->dataplane_starting || 155 s->dataplane_fenced) { 156 return 0; 157 } 158 159 s->dataplane_starting = true; 160 161 /* Set up guest notifier (irq) */ 162 rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); 163 if (rc != 0) { 164 error_report("virtio-scsi: Failed to set guest notifiers (%d), " 165 "ensure -accel kvm is set.", rc); 166 goto fail_guest_notifiers; 167 } 168 169 /* 170 * Batch all the host notifiers in a single transaction to avoid 171 * quadratic time complexity in address_space_update_ioeventfds(). 172 */ 173 memory_region_transaction_begin(); 174 175 rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0); 176 if (rc != 0) { 177 goto fail_host_notifiers; 178 } 179 180 vq_init_count++; 181 rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1); 182 if (rc != 0) { 183 goto fail_host_notifiers; 184 } 185 186 vq_init_count++; 187 188 for (i = 0; i < vs->conf.num_queues; i++) { 189 rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2); 190 if (rc) { 191 goto fail_host_notifiers; 192 } 193 vq_init_count++; 194 } 195 196 memory_region_transaction_commit(); 197 198 s->dataplane_starting = false; 199 s->dataplane_started = true; 200 smp_wmb(); /* paired with aio_notify_accept() */ 201 202 if (s->bus.drain_count == 0) { 203 virtio_queue_aio_attach_host_notifier(vs->ctrl_vq, 204 s->vq_aio_context[0]); 205 virtio_queue_aio_attach_host_notifier_no_poll(vs->event_vq, 206 s->vq_aio_context[1]); 207 208 for (i = 0; i < vs->conf.num_queues; i++) { 209 AioContext *ctx = s->vq_aio_context[VIRTIO_SCSI_VQ_NUM_FIXED + i]; 210 virtio_queue_aio_attach_host_notifier(vs->cmd_vqs[i], ctx); 211 } 212 } 213 return 0; 214 215 fail_host_notifiers: 216 for (i = 0; i < vq_init_count; i++) { 217 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); 218 } 219 220 /* 221 * The transaction expects the ioeventfds to be open when it 222 * commits. Do it now, before the cleanup loop. 223 */ 224 memory_region_transaction_commit(); 225 226 for (i = 0; i < vq_init_count; i++) { 227 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); 228 } 229 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 230 fail_guest_notifiers: 231 s->dataplane_fenced = true; 232 s->dataplane_starting = false; 233 s->dataplane_started = true; 234 return -ENOSYS; 235 } 236 237 /* Context: BQL held */ 238 void virtio_scsi_dataplane_stop(VirtIODevice *vdev) 239 { 240 BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); 241 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 242 VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); 243 VirtIOSCSI *s = VIRTIO_SCSI(vdev); 244 int i; 245 246 if (!s->dataplane_started || s->dataplane_stopping) { 247 return; 248 } 249 250 /* Better luck next time. */ 251 if (s->dataplane_fenced) { 252 s->dataplane_fenced = false; 253 s->dataplane_started = false; 254 return; 255 } 256 s->dataplane_stopping = true; 257 258 if (s->bus.drain_count == 0) { 259 for (i = 0; i < vs->conf.num_queues + VIRTIO_SCSI_VQ_NUM_FIXED; i++) { 260 VirtQueue *vq = virtio_get_queue(&vs->parent_obj, i); 261 AioContext *ctx = s->vq_aio_context[i]; 262 aio_wait_bh_oneshot(ctx, virtio_scsi_dataplane_stop_vq_bh, vq); 263 } 264 } 265 266 blk_drain_all(); /* ensure there are no in-flight requests */ 267 268 /* 269 * Batch all the host notifiers in a single transaction to avoid 270 * quadratic time complexity in address_space_update_ioeventfds(). 271 */ 272 memory_region_transaction_begin(); 273 274 for (i = 0; i < vs->conf.num_queues + 2; i++) { 275 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); 276 } 277 278 /* 279 * The transaction expects the ioeventfds to be open when it 280 * commits. Do it now, before the cleanup loop. 281 */ 282 memory_region_transaction_commit(); 283 284 for (i = 0; i < vs->conf.num_queues + 2; i++) { 285 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); 286 } 287 288 /* Clean up guest notifier (irq) */ 289 k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); 290 s->dataplane_stopping = false; 291 s->dataplane_started = false; 292 } 293