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