]>
Commit | Line | Data |
---|---|---|
91cb1c9b FZ |
1 | /* |
2 | * Virtio SCSI dataplane | |
3 | * | |
4 | * Copyright Red Hat, Inc. 2014 | |
5 | * | |
6 | * Authors: | |
7 | * Fam Zheng <[email protected]> | |
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 | ||
9b8bfe21 | 14 | #include "qemu/osdep.h" |
ad07cd69 | 15 | #include "qapi/error.h" |
91cb1c9b FZ |
16 | #include "hw/virtio/virtio-scsi.h" |
17 | #include "qemu/error-report.h" | |
4be74634 | 18 | #include "sysemu/block-backend.h" |
a9c94277 | 19 | #include "hw/scsi/scsi.h" |
08e2c9f1 | 20 | #include "scsi/constants.h" |
a9c94277 | 21 | #include "hw/virtio/virtio-bus.h" |
91cb1c9b | 22 | #include "hw/virtio/virtio-access.h" |
91cb1c9b FZ |
23 | |
24 | /* Context: QEMU global mutex held */ | |
ad07cd69 | 25 | void virtio_scsi_dataplane_setup(VirtIOSCSI *s, Error **errp) |
91cb1c9b | 26 | { |
91cb1c9b | 27 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
ad07cd69 PB |
28 | VirtIODevice *vdev = VIRTIO_DEVICE(s); |
29 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); | |
30 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
91cb1c9b | 31 | |
ad07cd69 PB |
32 | if (vs->conf.iothread) { |
33 | if (!k->set_guest_notifiers || !k->ioeventfd_assign) { | |
34 | error_setg(errp, | |
35 | "device is incompatible with iothread " | |
36 | "(transport does not support notifiers)"); | |
37 | return; | |
38 | } | |
39 | if (!virtio_device_ioeventfd_enabled(vdev)) { | |
40 | error_setg(errp, "ioeventfd is required for iothread"); | |
41 | return; | |
42 | } | |
43 | s->ctx = iothread_get_aio_context(vs->conf.iothread); | |
44 | } else { | |
45 | if (!virtio_device_ioeventfd_enabled(vdev)) { | |
46 | return; | |
47 | } | |
48 | s->ctx = qemu_get_aio_context(); | |
91cb1c9b FZ |
49 | } |
50 | } | |
51 | ||
07931698 | 52 | static bool virtio_scsi_data_plane_handle_cmd(VirtIODevice *vdev, |
a8f2e5c8 PB |
53 | VirtQueue *vq) |
54 | { | |
71407786 FZ |
55 | bool progress; |
56 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
a8f2e5c8 | 57 | |
71407786 | 58 | virtio_scsi_acquire(s); |
a8f2e5c8 | 59 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
60 | progress = virtio_scsi_handle_cmd_vq(s, vq); |
61 | virtio_scsi_release(s); | |
62 | return progress; | |
a8f2e5c8 PB |
63 | } |
64 | ||
07931698 | 65 | static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, |
a8f2e5c8 PB |
66 | VirtQueue *vq) |
67 | { | |
71407786 | 68 | bool progress; |
a8f2e5c8 PB |
69 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
70 | ||
71407786 | 71 | virtio_scsi_acquire(s); |
a8f2e5c8 | 72 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
73 | progress = virtio_scsi_handle_ctrl_vq(s, vq); |
74 | virtio_scsi_release(s); | |
75 | return progress; | |
a8f2e5c8 PB |
76 | } |
77 | ||
07931698 | 78 | static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, |
a8f2e5c8 PB |
79 | VirtQueue *vq) |
80 | { | |
71407786 | 81 | bool progress; |
a8f2e5c8 PB |
82 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
83 | ||
71407786 | 84 | virtio_scsi_acquire(s); |
a8f2e5c8 | 85 | assert(s->ctx && s->dataplane_started); |
71407786 FZ |
86 | progress = virtio_scsi_handle_event_vq(s, vq); |
87 | virtio_scsi_release(s); | |
88 | return progress; | |
a8f2e5c8 PB |
89 | } |
90 | ||
91 | static int virtio_scsi_vring_init(VirtIOSCSI *s, VirtQueue *vq, int n, | |
07931698 | 92 | VirtIOHandleAIOOutput fn) |
91cb1c9b FZ |
93 | { |
94 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
6d2c8316 | 95 | int rc; |
91cb1c9b FZ |
96 | |
97 | /* Set up virtqueue notify */ | |
b1f0a33d | 98 | rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); |
6d2c8316 CH |
99 | if (rc != 0) { |
100 | fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", | |
101 | rc); | |
4adea804 | 102 | s->dataplane_fenced = true; |
e24a47c5 | 103 | return rc; |
91cb1c9b | 104 | } |
196d4fc5 | 105 | |
a378b49a | 106 | virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, fn); |
e24a47c5 | 107 | return 0; |
91cb1c9b FZ |
108 | } |
109 | ||
184b9623 SH |
110 | /* Context: BH in IOThread */ |
111 | static void virtio_scsi_dataplane_stop_bh(void *opaque) | |
361dcc79 | 112 | { |
184b9623 | 113 | VirtIOSCSI *s = opaque; |
361dcc79 CH |
114 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
115 | int i; | |
116 | ||
a378b49a PB |
117 | virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); |
118 | virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); | |
e24a47c5 | 119 | for (i = 0; i < vs->conf.num_queues; i++) { |
a378b49a | 120 | virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); |
361dcc79 CH |
121 | } |
122 | } | |
123 | ||
91cb1c9b | 124 | /* Context: QEMU global mutex held */ |
ad07cd69 | 125 | int virtio_scsi_dataplane_start(VirtIODevice *vdev) |
91cb1c9b FZ |
126 | { |
127 | int i; | |
128 | int rc; | |
ad07cd69 | 129 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 130 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
131 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
132 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
91cb1c9b FZ |
133 | |
134 | if (s->dataplane_started || | |
135 | s->dataplane_starting || | |
ad07cd69 PB |
136 | s->dataplane_fenced) { |
137 | return 0; | |
91cb1c9b FZ |
138 | } |
139 | ||
140 | s->dataplane_starting = true; | |
141 | ||
142 | /* Set up guest notifier (irq) */ | |
143 | rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); | |
144 | if (rc != 0) { | |
a1d30f28 TH |
145 | error_report("virtio-scsi: Failed to set guest notifiers (%d), " |
146 | "ensure -accel kvm is set.", rc); | |
361dcc79 | 147 | goto fail_guest_notifiers; |
91cb1c9b FZ |
148 | } |
149 | ||
150 | aio_context_acquire(s->ctx); | |
a8f2e5c8 PB |
151 | rc = virtio_scsi_vring_init(s, vs->ctrl_vq, 0, |
152 | virtio_scsi_data_plane_handle_ctrl); | |
e24a47c5 | 153 | if (rc) { |
361dcc79 CH |
154 | goto fail_vrings; |
155 | } | |
a8f2e5c8 PB |
156 | rc = virtio_scsi_vring_init(s, vs->event_vq, 1, |
157 | virtio_scsi_data_plane_handle_event); | |
e24a47c5 | 158 | if (rc) { |
361dcc79 CH |
159 | goto fail_vrings; |
160 | } | |
91cb1c9b | 161 | for (i = 0; i < vs->conf.num_queues; i++) { |
a8f2e5c8 PB |
162 | rc = virtio_scsi_vring_init(s, vs->cmd_vqs[i], i + 2, |
163 | virtio_scsi_data_plane_handle_cmd); | |
e24a47c5 | 164 | if (rc) { |
361dcc79 CH |
165 | goto fail_vrings; |
166 | } | |
91cb1c9b FZ |
167 | } |
168 | ||
91cb1c9b FZ |
169 | s->dataplane_starting = false; |
170 | s->dataplane_started = true; | |
ace386b4 | 171 | aio_context_release(s->ctx); |
ad07cd69 | 172 | return 0; |
361dcc79 CH |
173 | |
174 | fail_vrings: | |
184b9623 | 175 | aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); |
361dcc79 | 176 | aio_context_release(s->ctx); |
361dcc79 | 177 | for (i = 0; i < vs->conf.num_queues + 2; i++) { |
21a4d962 | 178 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
76143618 | 179 | virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); |
361dcc79 CH |
180 | } |
181 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
182 | fail_guest_notifiers: | |
e24a47c5 | 183 | s->dataplane_fenced = true; |
361dcc79 | 184 | s->dataplane_starting = false; |
e24a47c5 | 185 | s->dataplane_started = true; |
ad07cd69 | 186 | return -ENOSYS; |
91cb1c9b FZ |
187 | } |
188 | ||
189 | /* Context: QEMU global mutex held */ | |
ad07cd69 | 190 | void virtio_scsi_dataplane_stop(VirtIODevice *vdev) |
91cb1c9b | 191 | { |
ad07cd69 | 192 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 193 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
194 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
195 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
21a4d962 | 196 | int i; |
91cb1c9b | 197 | |
e24a47c5 PB |
198 | if (!s->dataplane_started || s->dataplane_stopping) { |
199 | return; | |
200 | } | |
201 | ||
4adea804 CH |
202 | /* Better luck next time. */ |
203 | if (s->dataplane_fenced) { | |
204 | s->dataplane_fenced = false; | |
e24a47c5 | 205 | s->dataplane_started = false; |
91cb1c9b FZ |
206 | return; |
207 | } | |
208 | s->dataplane_stopping = true; | |
91cb1c9b FZ |
209 | |
210 | aio_context_acquire(s->ctx); | |
184b9623 | 211 | aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); |
c9d1a561 | 212 | aio_context_release(s->ctx); |
91cb1c9b | 213 | |
4be74634 | 214 | blk_drain_all(); /* ensure there are no in-flight requests */ |
91cb1c9b | 215 | |
91cb1c9b | 216 | for (i = 0; i < vs->conf.num_queues + 2; i++) { |
21a4d962 | 217 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
76143618 | 218 | virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); |
91cb1c9b FZ |
219 | } |
220 | ||
221 | /* Clean up guest notifier (irq) */ | |
222 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
223 | s->dataplane_stopping = false; | |
224 | s->dataplane_started = false; | |
225 | } |