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 "hw/virtio/virtio-scsi.h"
15 #include "qemu/error-report.h"
16 #include "sysemu/block-backend.h"
17 #include <hw/scsi/scsi.h>
18 #include <block/scsi.h>
19 #include <hw/virtio/virtio-bus.h>
20 #include "hw/virtio/virtio-access.h"
23 /* Context: QEMU global mutex held */
24 void virtio_scsi_set_iothread(VirtIOSCSI
*s
, IOThread
*iothread
)
26 BusState
*qbus
= BUS(qdev_get_parent_bus(DEVICE(s
)));
27 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
28 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
31 s
->ctx
= iothread_get_aio_context(vs
->conf
.iothread
);
33 /* Don't try if transport does not support notifiers. */
34 if (!k
->set_guest_notifiers
|| !k
->set_host_notifier
) {
35 fprintf(stderr
, "virtio-scsi: Failed to set iothread "
36 "(transport does not support notifiers)");
41 static VirtIOSCSIVring
*virtio_scsi_vring_init(VirtIOSCSI
*s
,
43 EventNotifierHandler
*handler
,
46 BusState
*qbus
= BUS(qdev_get_parent_bus(DEVICE(s
)));
47 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
51 /* Set up virtqueue notify */
52 rc
= k
->set_host_notifier(qbus
->parent
, n
, true);
54 fprintf(stderr
, "virtio-scsi: Failed to set host notifier (%d)\n",
56 s
->dataplane_fenced
= true;
60 r
= g_slice_new(VirtIOSCSIVring
);
61 r
->host_notifier
= *virtio_queue_get_host_notifier(vq
);
62 r
->guest_notifier
= *virtio_queue_get_guest_notifier(vq
);
63 aio_set_event_notifier(s
->ctx
, &r
->host_notifier
, handler
);
67 if (!vring_setup(&r
->vring
, VIRTIO_DEVICE(s
), n
)) {
68 fprintf(stderr
, "virtio-scsi: VRing setup failed\n");
74 aio_set_event_notifier(s
->ctx
, &r
->host_notifier
, NULL
);
75 k
->set_host_notifier(qbus
->parent
, n
, false);
76 g_slice_free(VirtIOSCSIVring
, r
);
80 VirtIOSCSIReq
*virtio_scsi_pop_req_vring(VirtIOSCSI
*s
,
81 VirtIOSCSIVring
*vring
)
83 VirtIOSCSIReq
*req
= virtio_scsi_init_req(s
, NULL
);
87 r
= vring_pop((VirtIODevice
*)s
, &vring
->vring
, &req
->elem
);
89 virtio_scsi_free_req(req
);
95 void virtio_scsi_vring_push_notify(VirtIOSCSIReq
*req
)
97 VirtIODevice
*vdev
= VIRTIO_DEVICE(req
->vring
->parent
);
99 vring_push(vdev
, &req
->vring
->vring
, &req
->elem
,
100 req
->qsgl
.size
+ req
->resp_iov
.size
);
102 if (vring_should_notify(vdev
, &req
->vring
->vring
)) {
103 event_notifier_set(&req
->vring
->guest_notifier
);
107 static void virtio_scsi_iothread_handle_ctrl(EventNotifier
*notifier
)
109 VirtIOSCSIVring
*vring
= container_of(notifier
,
110 VirtIOSCSIVring
, host_notifier
);
111 VirtIOSCSI
*s
= VIRTIO_SCSI(vring
->parent
);
114 event_notifier_test_and_clear(notifier
);
115 while ((req
= virtio_scsi_pop_req_vring(s
, vring
))) {
116 virtio_scsi_handle_ctrl_req(s
, req
);
120 static void virtio_scsi_iothread_handle_event(EventNotifier
*notifier
)
122 VirtIOSCSIVring
*vring
= container_of(notifier
,
123 VirtIOSCSIVring
, host_notifier
);
124 VirtIOSCSI
*s
= vring
->parent
;
125 VirtIODevice
*vdev
= VIRTIO_DEVICE(s
);
127 event_notifier_test_and_clear(notifier
);
129 if (!(vdev
->status
& VIRTIO_CONFIG_S_DRIVER_OK
)) {
133 if (s
->events_dropped
) {
134 virtio_scsi_push_event(s
, NULL
, VIRTIO_SCSI_T_NO_EVENT
, 0);
138 static void virtio_scsi_iothread_handle_cmd(EventNotifier
*notifier
)
140 VirtIOSCSIVring
*vring
= container_of(notifier
,
141 VirtIOSCSIVring
, host_notifier
);
142 VirtIOSCSI
*s
= (VirtIOSCSI
*)vring
->parent
;
143 VirtIOSCSIReq
*req
, *next
;
144 QTAILQ_HEAD(, VirtIOSCSIReq
) reqs
= QTAILQ_HEAD_INITIALIZER(reqs
);
146 event_notifier_test_and_clear(notifier
);
147 while ((req
= virtio_scsi_pop_req_vring(s
, vring
))) {
148 if (virtio_scsi_handle_cmd_req_prepare(s
, req
)) {
149 QTAILQ_INSERT_TAIL(&reqs
, req
, next
);
153 QTAILQ_FOREACH_SAFE(req
, &reqs
, next
, next
) {
154 virtio_scsi_handle_cmd_req_submit(s
, req
);
158 /* assumes s->ctx held */
159 static void virtio_scsi_clear_aio(VirtIOSCSI
*s
)
161 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
165 aio_set_event_notifier(s
->ctx
, &s
->ctrl_vring
->host_notifier
, NULL
);
167 if (s
->event_vring
) {
168 aio_set_event_notifier(s
->ctx
, &s
->event_vring
->host_notifier
, NULL
);
171 for (i
= 0; i
< vs
->conf
.num_queues
&& s
->cmd_vrings
[i
]; i
++) {
172 aio_set_event_notifier(s
->ctx
, &s
->cmd_vrings
[i
]->host_notifier
, NULL
);
177 static void virtio_scsi_vring_teardown(VirtIOSCSI
*s
)
179 VirtIODevice
*vdev
= VIRTIO_DEVICE(s
);
180 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
184 vring_teardown(&s
->ctrl_vring
->vring
, vdev
, 0);
185 g_slice_free(VirtIOSCSIVring
, s
->ctrl_vring
);
186 s
->ctrl_vring
= NULL
;
188 if (s
->event_vring
) {
189 vring_teardown(&s
->event_vring
->vring
, vdev
, 1);
190 g_slice_free(VirtIOSCSIVring
, s
->event_vring
);
191 s
->event_vring
= NULL
;
194 for (i
= 0; i
< vs
->conf
.num_queues
&& s
->cmd_vrings
[i
]; i
++) {
195 vring_teardown(&s
->cmd_vrings
[i
]->vring
, vdev
, 2 + i
);
196 g_slice_free(VirtIOSCSIVring
, s
->cmd_vrings
[i
]);
197 s
->cmd_vrings
[i
] = NULL
;
200 s
->cmd_vrings
= NULL
;
204 /* Context: QEMU global mutex held */
205 void virtio_scsi_dataplane_start(VirtIOSCSI
*s
)
209 BusState
*qbus
= BUS(qdev_get_parent_bus(DEVICE(s
)));
210 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
211 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
213 if (s
->dataplane_started
||
214 s
->dataplane_starting
||
215 s
->dataplane_fenced
||
216 s
->ctx
!= iothread_get_aio_context(vs
->conf
.iothread
)) {
220 s
->dataplane_starting
= true;
222 /* Set up guest notifier (irq) */
223 rc
= k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, true);
225 fprintf(stderr
, "virtio-scsi: Failed to set guest notifiers (%d), "
226 "ensure -enable-kvm is set\n", rc
);
227 s
->dataplane_fenced
= true;
228 goto fail_guest_notifiers
;
231 aio_context_acquire(s
->ctx
);
232 s
->ctrl_vring
= virtio_scsi_vring_init(s
, vs
->ctrl_vq
,
233 virtio_scsi_iothread_handle_ctrl
,
235 if (!s
->ctrl_vring
) {
238 s
->event_vring
= virtio_scsi_vring_init(s
, vs
->event_vq
,
239 virtio_scsi_iothread_handle_event
,
241 if (!s
->event_vring
) {
244 s
->cmd_vrings
= g_new(VirtIOSCSIVring
*, vs
->conf
.num_queues
);
245 for (i
= 0; i
< vs
->conf
.num_queues
; i
++) {
247 virtio_scsi_vring_init(s
, vs
->cmd_vqs
[i
],
248 virtio_scsi_iothread_handle_cmd
,
250 if (!s
->cmd_vrings
[i
]) {
255 s
->dataplane_starting
= false;
256 s
->dataplane_started
= true;
257 aio_context_release(s
->ctx
);
261 virtio_scsi_clear_aio(s
);
262 aio_context_release(s
->ctx
);
263 virtio_scsi_vring_teardown(s
);
264 for (i
= 0; i
< vs
->conf
.num_queues
+ 2; i
++) {
265 k
->set_host_notifier(qbus
->parent
, i
, false);
267 k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, false);
268 fail_guest_notifiers
:
269 s
->dataplane_starting
= false;
272 /* Context: QEMU global mutex held */
273 void virtio_scsi_dataplane_stop(VirtIOSCSI
*s
)
275 BusState
*qbus
= BUS(qdev_get_parent_bus(DEVICE(s
)));
276 VirtioBusClass
*k
= VIRTIO_BUS_GET_CLASS(qbus
);
277 VirtIOSCSICommon
*vs
= VIRTIO_SCSI_COMMON(s
);
280 /* Better luck next time. */
281 if (s
->dataplane_fenced
) {
282 s
->dataplane_fenced
= false;
285 if (!s
->dataplane_started
|| s
->dataplane_stopping
) {
288 s
->dataplane_stopping
= true;
289 assert(s
->ctx
== iothread_get_aio_context(vs
->conf
.iothread
));
291 aio_context_acquire(s
->ctx
);
293 aio_set_event_notifier(s
->ctx
, &s
->ctrl_vring
->host_notifier
, NULL
);
294 aio_set_event_notifier(s
->ctx
, &s
->event_vring
->host_notifier
, NULL
);
295 for (i
= 0; i
< vs
->conf
.num_queues
; i
++) {
296 aio_set_event_notifier(s
->ctx
, &s
->cmd_vrings
[i
]->host_notifier
, NULL
);
299 blk_drain_all(); /* ensure there are no in-flight requests */
301 aio_context_release(s
->ctx
);
303 /* Sync vring state back to virtqueue so that non-dataplane request
304 * processing can continue when we disable the host notifier below.
306 virtio_scsi_vring_teardown(s
);
308 for (i
= 0; i
< vs
->conf
.num_queues
+ 2; i
++) {
309 k
->set_host_notifier(qbus
->parent
, i
, false);
312 /* Clean up guest notifier (irq) */
313 k
->set_guest_notifiers(qbus
->parent
, vs
->conf
.num_queues
+ 2, false);
314 s
->dataplane_stopping
= false;
315 s
->dataplane_started
= false;