]>
Commit | Line | Data |
---|---|---|
91cb1c9b FZ |
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 | ||
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 | { | |
6f1a5c37 | 55 | bool progress = false; |
71407786 | 56 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
a8f2e5c8 | 57 | |
71407786 | 58 | virtio_scsi_acquire(s); |
6f1a5c37 ML |
59 | if (!s->dataplane_fenced) { |
60 | assert(s->ctx && s->dataplane_started); | |
61 | progress = virtio_scsi_handle_cmd_vq(s, vq); | |
62 | } | |
71407786 FZ |
63 | virtio_scsi_release(s); |
64 | return progress; | |
a8f2e5c8 PB |
65 | } |
66 | ||
07931698 | 67 | static bool virtio_scsi_data_plane_handle_ctrl(VirtIODevice *vdev, |
a8f2e5c8 PB |
68 | VirtQueue *vq) |
69 | { | |
6f1a5c37 | 70 | bool progress = false; |
a8f2e5c8 PB |
71 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
72 | ||
71407786 | 73 | virtio_scsi_acquire(s); |
6f1a5c37 ML |
74 | if (!s->dataplane_fenced) { |
75 | assert(s->ctx && s->dataplane_started); | |
76 | progress = virtio_scsi_handle_ctrl_vq(s, vq); | |
77 | } | |
71407786 FZ |
78 | virtio_scsi_release(s); |
79 | return progress; | |
a8f2e5c8 PB |
80 | } |
81 | ||
07931698 | 82 | static bool virtio_scsi_data_plane_handle_event(VirtIODevice *vdev, |
a8f2e5c8 PB |
83 | VirtQueue *vq) |
84 | { | |
6f1a5c37 | 85 | bool progress = false; |
a8f2e5c8 PB |
86 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); |
87 | ||
71407786 | 88 | virtio_scsi_acquire(s); |
6f1a5c37 ML |
89 | if (!s->dataplane_fenced) { |
90 | assert(s->ctx && s->dataplane_started); | |
91 | progress = virtio_scsi_handle_event_vq(s, vq); | |
92 | } | |
71407786 FZ |
93 | virtio_scsi_release(s); |
94 | return progress; | |
a8f2e5c8 PB |
95 | } |
96 | ||
61fc57bf | 97 | static int virtio_scsi_set_host_notifier(VirtIOSCSI *s, VirtQueue *vq, int n) |
91cb1c9b FZ |
98 | { |
99 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
6d2c8316 | 100 | int rc; |
91cb1c9b FZ |
101 | |
102 | /* Set up virtqueue notify */ | |
b1f0a33d | 103 | rc = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), n, true); |
6d2c8316 CH |
104 | if (rc != 0) { |
105 | fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", | |
106 | rc); | |
4adea804 | 107 | s->dataplane_fenced = true; |
e24a47c5 | 108 | return rc; |
91cb1c9b | 109 | } |
196d4fc5 | 110 | |
e24a47c5 | 111 | return 0; |
91cb1c9b FZ |
112 | } |
113 | ||
184b9623 SH |
114 | /* Context: BH in IOThread */ |
115 | static void virtio_scsi_dataplane_stop_bh(void *opaque) | |
361dcc79 | 116 | { |
184b9623 | 117 | VirtIOSCSI *s = opaque; |
361dcc79 CH |
118 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
119 | int i; | |
120 | ||
a378b49a PB |
121 | virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, NULL); |
122 | virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, NULL); | |
e24a47c5 | 123 | for (i = 0; i < vs->conf.num_queues; i++) { |
a378b49a | 124 | virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, NULL); |
361dcc79 CH |
125 | } |
126 | } | |
127 | ||
91cb1c9b | 128 | /* Context: QEMU global mutex held */ |
ad07cd69 | 129 | int virtio_scsi_dataplane_start(VirtIODevice *vdev) |
91cb1c9b FZ |
130 | { |
131 | int i; | |
132 | int rc; | |
dec2bb14 | 133 | int vq_init_count = 0; |
ad07cd69 | 134 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 135 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
136 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
137 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
91cb1c9b FZ |
138 | |
139 | if (s->dataplane_started || | |
140 | s->dataplane_starting || | |
ad07cd69 PB |
141 | s->dataplane_fenced) { |
142 | return 0; | |
91cb1c9b FZ |
143 | } |
144 | ||
145 | s->dataplane_starting = true; | |
146 | ||
147 | /* Set up guest notifier (irq) */ | |
148 | rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); | |
149 | if (rc != 0) { | |
a1d30f28 TH |
150 | error_report("virtio-scsi: Failed to set guest notifiers (%d), " |
151 | "ensure -accel kvm is set.", rc); | |
361dcc79 | 152 | goto fail_guest_notifiers; |
91cb1c9b FZ |
153 | } |
154 | ||
c4f5dcc4 GK |
155 | memory_region_transaction_begin(); |
156 | ||
61fc57bf GK |
157 | rc = virtio_scsi_set_host_notifier(s, vs->ctrl_vq, 0); |
158 | if (rc != 0) { | |
159 | goto fail_host_notifiers; | |
361dcc79 | 160 | } |
dec2bb14 ML |
161 | |
162 | vq_init_count++; | |
61fc57bf GK |
163 | rc = virtio_scsi_set_host_notifier(s, vs->event_vq, 1); |
164 | if (rc != 0) { | |
165 | goto fail_host_notifiers; | |
361dcc79 | 166 | } |
dec2bb14 ML |
167 | |
168 | vq_init_count++; | |
61fc57bf | 169 | |
91cb1c9b | 170 | for (i = 0; i < vs->conf.num_queues; i++) { |
61fc57bf | 171 | rc = virtio_scsi_set_host_notifier(s, vs->cmd_vqs[i], i + 2); |
e24a47c5 | 172 | if (rc) { |
61fc57bf | 173 | goto fail_host_notifiers; |
361dcc79 | 174 | } |
dec2bb14 | 175 | vq_init_count++; |
91cb1c9b FZ |
176 | } |
177 | ||
c4f5dcc4 GK |
178 | memory_region_transaction_commit(); |
179 | ||
61fc57bf GK |
180 | aio_context_acquire(s->ctx); |
181 | virtio_queue_aio_set_host_notifier_handler(vs->ctrl_vq, s->ctx, | |
182 | virtio_scsi_data_plane_handle_ctrl); | |
183 | virtio_queue_aio_set_host_notifier_handler(vs->event_vq, s->ctx, | |
184 | virtio_scsi_data_plane_handle_event); | |
185 | ||
186 | for (i = 0; i < vs->conf.num_queues; i++) { | |
187 | virtio_queue_aio_set_host_notifier_handler(vs->cmd_vqs[i], s->ctx, | |
188 | virtio_scsi_data_plane_handle_cmd); | |
189 | } | |
190 | ||
91cb1c9b FZ |
191 | s->dataplane_starting = false; |
192 | s->dataplane_started = true; | |
ace386b4 | 193 | aio_context_release(s->ctx); |
ad07cd69 | 194 | return 0; |
361dcc79 | 195 | |
61fc57bf | 196 | fail_host_notifiers: |
dec2bb14 | 197 | for (i = 0; i < vq_init_count; i++) { |
21a4d962 | 198 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
c4f5dcc4 GK |
199 | } |
200 | ||
201 | memory_region_transaction_commit(); | |
202 | ||
203 | for (i = 0; i < vq_init_count; i++) { | |
76143618 | 204 | virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); |
361dcc79 CH |
205 | } |
206 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
207 | fail_guest_notifiers: | |
e24a47c5 | 208 | s->dataplane_fenced = true; |
361dcc79 | 209 | s->dataplane_starting = false; |
e24a47c5 | 210 | s->dataplane_started = true; |
ad07cd69 | 211 | return -ENOSYS; |
91cb1c9b FZ |
212 | } |
213 | ||
214 | /* Context: QEMU global mutex held */ | |
ad07cd69 | 215 | void virtio_scsi_dataplane_stop(VirtIODevice *vdev) |
91cb1c9b | 216 | { |
ad07cd69 | 217 | BusState *qbus = qdev_get_parent_bus(DEVICE(vdev)); |
91cb1c9b | 218 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
ad07cd69 PB |
219 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(vdev); |
220 | VirtIOSCSI *s = VIRTIO_SCSI(vdev); | |
21a4d962 | 221 | int i; |
91cb1c9b | 222 | |
e24a47c5 PB |
223 | if (!s->dataplane_started || s->dataplane_stopping) { |
224 | return; | |
225 | } | |
226 | ||
4adea804 CH |
227 | /* Better luck next time. */ |
228 | if (s->dataplane_fenced) { | |
229 | s->dataplane_fenced = false; | |
e24a47c5 | 230 | s->dataplane_started = false; |
91cb1c9b FZ |
231 | return; |
232 | } | |
233 | s->dataplane_stopping = true; | |
91cb1c9b FZ |
234 | |
235 | aio_context_acquire(s->ctx); | |
184b9623 | 236 | aio_wait_bh_oneshot(s->ctx, virtio_scsi_dataplane_stop_bh, s); |
c9d1a561 | 237 | aio_context_release(s->ctx); |
91cb1c9b | 238 | |
4be74634 | 239 | blk_drain_all(); /* ensure there are no in-flight requests */ |
91cb1c9b | 240 | |
c4f5dcc4 GK |
241 | memory_region_transaction_begin(); |
242 | ||
91cb1c9b | 243 | for (i = 0; i < vs->conf.num_queues + 2; i++) { |
21a4d962 | 244 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); |
c4f5dcc4 GK |
245 | } |
246 | ||
247 | memory_region_transaction_commit(); | |
248 | ||
249 | for (i = 0; i < vs->conf.num_queues + 2; i++) { | |
76143618 | 250 | virtio_bus_cleanup_host_notifier(VIRTIO_BUS(qbus), i); |
91cb1c9b FZ |
251 | } |
252 | ||
253 | /* Clean up guest notifier (irq) */ | |
254 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
255 | s->dataplane_stopping = false; | |
256 | s->dataplane_started = false; | |
257 | } |