2 * Virtio SCSI dataplane
4 * Copyright Red Hat, Inc. 2014
7 * Fam Zheng <famz@redhat.com>
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.
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 "sysemu/block-backend.h"
19 #include "hw/scsi/scsi.h"
20 #include "scsi/constants.h"
21 #include "hw/virtio/virtio-bus.h"
23 /* Context: BQL held */
24 void virtio_scsi_dataplane_setup(VirtIOSCSI
*s
, Error
**errp
)
26 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
27 VirtIODevice
*vdev
= VIRTIO_DEVICE(s
);
28 BusState
*qbus
= qdev_get_parent_bus(DEVICE(vdev
));
29 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
31 if (vs
->conf
.iothread
) {
32 if (!k
->set_guest_notifiers
|| !k
->ioeventfd_assign
) {
34 "device is incompatible with iothread "
35 "(transport does not support notifiers)");
38 if (!virtio_device_ioeventfd_enabled(vdev
)) {
39 error_setg(errp
, "ioeventfd is required for iothread");
42 s
->ctx
= iothread_get_aio_context(vs
->conf
.iothread
);
44 if (!virtio_device_ioeventfd_enabled(vdev
)) {
47 s
->ctx
= qemu_get_aio_context();
51 static int virtio_scsi_set_host_notifier(VirtIOSCSI
*s
, VirtQueue
*vq
, int n
)
53 BusState
*qbus
= BUS(qdev_get_parent_bus(DEVICE(s
)));
56 /* Set up virtqueue notify */
57 rc
= virtio_bus_set_host_notifier(VIRTIO_BUS(qbus
), n
, true);
59 fprintf(stderr
, "virtio-scsi: Failed to set host notifier (%d)\n",
61 s
->dataplane_fenced
= true;
68 /* Context: BH in IOThread */
69 static void virtio_scsi_dataplane_stop_bh(void *opaque
)
71 VirtIOSCSI
*s
= opaque
;
72 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
73 EventNotifier
*host_notifier
;
76 virtio_queue_aio_detach_host_notifier(vs
->ctrl_vq
, s
->ctx
);
77 host_notifier
= virtio_queue_get_host_notifier(vs
->ctrl_vq
);
80 * Test and clear notifier after disabling event, in case poll callback
81 * didn't have time to run.
83 virtio_queue_host_notifier_read(host_notifier
);
85 virtio_queue_aio_detach_host_notifier(vs
->event_vq
, s
->ctx
);
86 host_notifier
= virtio_queue_get_host_notifier(vs
->event_vq
);
87 virtio_queue_host_notifier_read(host_notifier
);
89 for (i
= 0; i
< vs
->conf
.num_queues
; i
++) {
90 virtio_queue_aio_detach_host_notifier(vs
->cmd_vqs
[i
], s
->ctx
);
91 host_notifier
= virtio_queue_get_host_notifier(vs
->cmd_vqs
[i
]);
92 virtio_queue_host_notifier_read(host_notifier
);
96 /* Context: BQL held */
97 int virtio_scsi_dataplane_start(VirtIODevice
*vdev
)
101 int vq_init_count
= 0;
102 BusState
*qbus
= qdev_get_parent_bus(DEVICE(vdev
));
103 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
104 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(vdev
);
105 VirtIOSCSI
*s
= VIRTIO_SCSI(vdev
);
107 if (s
->dataplane_started
||
108 s
->dataplane_starting
||
109 s
->dataplane_fenced
) {
113 s
->dataplane_starting
= true;
115 /* Set up guest notifier (irq) */
116 rc
= k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, true);
118 error_report("virtio-scsi: Failed to set guest notifiers (%d), "
119 "ensure -accel kvm is set.", rc
);
120 goto fail_guest_notifiers
;
124 * Batch all the host notifiers in a single transaction to avoid
125 * quadratic time complexity in address_space_update_ioeventfds().
127 memory_region_transaction_begin();
129 rc
= virtio_scsi_set_host_notifier(s
, vs
->ctrl_vq
, 0);
131 goto fail_host_notifiers
;
135 rc
= virtio_scsi_set_host_notifier(s
, vs
->event_vq
, 1);
137 goto fail_host_notifiers
;
142 for (i
= 0; i
< vs
->conf
.num_queues
; i
++) {
143 rc
= virtio_scsi_set_host_notifier(s
, vs
->cmd_vqs
[i
], i
+ 2);
145 goto fail_host_notifiers
;
150 memory_region_transaction_commit();
152 s
->dataplane_starting
= false;
153 s
->dataplane_started
= true;
154 smp_wmb(); /* paired with aio_notify_accept() */
156 if (s
->bus
.drain_count
== 0) {
157 virtio_queue_aio_attach_host_notifier(vs
->ctrl_vq
, s
->ctx
);
158 virtio_queue_aio_attach_host_notifier_no_poll(vs
->event_vq
, s
->ctx
);
160 for (i
= 0; i
< vs
->conf
.num_queues
; i
++) {
161 virtio_queue_aio_attach_host_notifier(vs
->cmd_vqs
[i
], s
->ctx
);
167 for (i
= 0; i
< vq_init_count
; i
++) {
168 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus
), i
, false);
172 * The transaction expects the ioeventfds to be open when it
173 * commits. Do it now, before the cleanup loop.
175 memory_region_transaction_commit();
177 for (i
= 0; i
< vq_init_count
; i
++) {
178 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus
), i
);
180 k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, false);
181 fail_guest_notifiers
:
182 s
->dataplane_fenced
= true;
183 s
->dataplane_starting
= false;
184 s
->dataplane_started
= true;
188 /* Context: BQL held */
189 void virtio_scsi_dataplane_stop(VirtIODevice
*vdev
)
191 BusState
*qbus
= qdev_get_parent_bus(DEVICE(vdev
));
192 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
193 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(vdev
);
194 VirtIOSCSI
*s
= VIRTIO_SCSI(vdev
);
197 if (!s
->dataplane_started
|| s
->dataplane_stopping
) {
201 /* Better luck next time. */
202 if (s
->dataplane_fenced
) {
203 s
->dataplane_fenced
= false;
204 s
->dataplane_started
= false;
207 s
->dataplane_stopping
= true;
209 if (s
->bus
.drain_count
== 0) {
210 aio_wait_bh_oneshot(s
->ctx
, virtio_scsi_dataplane_stop_bh
, s
);
213 blk_drain_all(); /* ensure there are no in-flight requests */
216 * Batch all the host notifiers in a single transaction to avoid
217 * quadratic time complexity in address_space_update_ioeventfds().
219 memory_region_transaction_begin();
221 for (i
= 0; i
< vs
->conf
.num_queues
+ 2; i
++) {
222 virtio_bus_set_host_notifier(VIRTIO_BUS(qbus
), i
, false);
226 * The transaction expects the ioeventfds to be open when it
227 * commits. Do it now, before the cleanup loop.
229 memory_region_transaction_commit();
231 for (i
= 0; i
< vs
->conf
.num_queues
+ 2; i
++) {
232 virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus
), i
);
235 /* Clean up guest notifier (irq) */
236 k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, false);
237 s
->dataplane_stopping
= false;
238 s
->dataplane_started
= false;