]>
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 | ||
14 | #include "hw/virtio/virtio-scsi.h" | |
15 | #include "qemu/error-report.h" | |
4be74634 | 16 | #include "sysemu/block-backend.h" |
91cb1c9b FZ |
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" | |
21 | #include "stdio.h" | |
22 | ||
23 | /* Context: QEMU global mutex held */ | |
24 | void virtio_scsi_set_iothread(VirtIOSCSI *s, IOThread *iothread) | |
25 | { | |
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); | |
29 | ||
30 | assert(!s->ctx); | |
31 | s->ctx = iothread_get_aio_context(vs->conf.iothread); | |
32 | ||
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)"); | |
37 | exit(1); | |
38 | } | |
39 | } | |
40 | ||
41 | static VirtIOSCSIVring *virtio_scsi_vring_init(VirtIOSCSI *s, | |
42 | VirtQueue *vq, | |
43 | EventNotifierHandler *handler, | |
44 | int n) | |
45 | { | |
46 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
47 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
196d4fc5 | 48 | VirtIOSCSIVring *r; |
6d2c8316 | 49 | int rc; |
91cb1c9b FZ |
50 | |
51 | /* Set up virtqueue notify */ | |
6d2c8316 CH |
52 | rc = k->set_host_notifier(qbus->parent, n, true); |
53 | if (rc != 0) { | |
54 | fprintf(stderr, "virtio-scsi: Failed to set host notifier (%d)\n", | |
55 | rc); | |
4adea804 | 56 | s->dataplane_fenced = true; |
361dcc79 | 57 | return NULL; |
91cb1c9b | 58 | } |
196d4fc5 BS |
59 | |
60 | r = g_slice_new(VirtIOSCSIVring); | |
91cb1c9b FZ |
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); | |
64 | ||
65 | r->parent = s; | |
66 | ||
67 | if (!vring_setup(&r->vring, VIRTIO_DEVICE(s), n)) { | |
68 | fprintf(stderr, "virtio-scsi: VRing setup failed\n"); | |
361dcc79 | 69 | goto fail_vring; |
91cb1c9b FZ |
70 | } |
71 | return r; | |
361dcc79 CH |
72 | |
73 | fail_vring: | |
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); | |
77 | return NULL; | |
91cb1c9b FZ |
78 | } |
79 | ||
80 | VirtIOSCSIReq *virtio_scsi_pop_req_vring(VirtIOSCSI *s, | |
81 | VirtIOSCSIVring *vring) | |
82 | { | |
83 | VirtIOSCSIReq *req = virtio_scsi_init_req(s, NULL); | |
84 | int r; | |
85 | ||
86 | req->vring = vring; | |
87 | r = vring_pop((VirtIODevice *)s, &vring->vring, &req->elem); | |
88 | if (r < 0) { | |
89 | virtio_scsi_free_req(req); | |
90 | req = NULL; | |
91 | } | |
92 | return req; | |
93 | } | |
94 | ||
95 | void virtio_scsi_vring_push_notify(VirtIOSCSIReq *req) | |
96 | { | |
6012ca81 ML |
97 | VirtIODevice *vdev = VIRTIO_DEVICE(req->vring->parent); |
98 | ||
b0e5d90e | 99 | vring_push(vdev, &req->vring->vring, &req->elem, |
91cb1c9b | 100 | req->qsgl.size + req->resp_iov.size); |
6012ca81 ML |
101 | |
102 | if (vring_should_notify(vdev, &req->vring->vring)) { | |
103 | event_notifier_set(&req->vring->guest_notifier); | |
104 | } | |
91cb1c9b FZ |
105 | } |
106 | ||
107 | static void virtio_scsi_iothread_handle_ctrl(EventNotifier *notifier) | |
108 | { | |
109 | VirtIOSCSIVring *vring = container_of(notifier, | |
110 | VirtIOSCSIVring, host_notifier); | |
111 | VirtIOSCSI *s = VIRTIO_SCSI(vring->parent); | |
112 | VirtIOSCSIReq *req; | |
113 | ||
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); | |
117 | } | |
118 | } | |
119 | ||
120 | static void virtio_scsi_iothread_handle_event(EventNotifier *notifier) | |
121 | { | |
122 | VirtIOSCSIVring *vring = container_of(notifier, | |
123 | VirtIOSCSIVring, host_notifier); | |
124 | VirtIOSCSI *s = vring->parent; | |
125 | VirtIODevice *vdev = VIRTIO_DEVICE(s); | |
126 | ||
127 | event_notifier_test_and_clear(notifier); | |
128 | ||
129 | if (!(vdev->status & VIRTIO_CONFIG_S_DRIVER_OK)) { | |
130 | return; | |
131 | } | |
132 | ||
133 | if (s->events_dropped) { | |
134 | virtio_scsi_push_event(s, NULL, VIRTIO_SCSI_T_NO_EVENT, 0); | |
135 | } | |
136 | } | |
137 | ||
138 | static void virtio_scsi_iothread_handle_cmd(EventNotifier *notifier) | |
139 | { | |
140 | VirtIOSCSIVring *vring = container_of(notifier, | |
141 | VirtIOSCSIVring, host_notifier); | |
142 | VirtIOSCSI *s = (VirtIOSCSI *)vring->parent; | |
1880ad4f FZ |
143 | VirtIOSCSIReq *req, *next; |
144 | QTAILQ_HEAD(, VirtIOSCSIReq) reqs = QTAILQ_HEAD_INITIALIZER(reqs); | |
91cb1c9b FZ |
145 | |
146 | event_notifier_test_and_clear(notifier); | |
147 | while ((req = virtio_scsi_pop_req_vring(s, vring))) { | |
359eea71 | 148 | if (virtio_scsi_handle_cmd_req_prepare(s, req)) { |
1880ad4f | 149 | QTAILQ_INSERT_TAIL(&reqs, req, next); |
359eea71 | 150 | } |
91cb1c9b | 151 | } |
1880ad4f FZ |
152 | |
153 | QTAILQ_FOREACH_SAFE(req, &reqs, next, next) { | |
154 | virtio_scsi_handle_cmd_req_submit(s, req); | |
155 | } | |
91cb1c9b FZ |
156 | } |
157 | ||
361dcc79 CH |
158 | /* assumes s->ctx held */ |
159 | static void virtio_scsi_clear_aio(VirtIOSCSI *s) | |
160 | { | |
161 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | |
162 | int i; | |
163 | ||
164 | if (s->ctrl_vring) { | |
165 | aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); | |
166 | } | |
167 | if (s->event_vring) { | |
168 | aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); | |
169 | } | |
170 | if (s->cmd_vrings) { | |
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); | |
173 | } | |
174 | } | |
175 | } | |
176 | ||
177 | static void virtio_scsi_vring_teardown(VirtIOSCSI *s) | |
178 | { | |
179 | VirtIODevice *vdev = VIRTIO_DEVICE(s); | |
180 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | |
181 | int i; | |
182 | ||
183 | if (s->ctrl_vring) { | |
184 | vring_teardown(&s->ctrl_vring->vring, vdev, 0); | |
185 | } | |
186 | if (s->event_vring) { | |
187 | vring_teardown(&s->event_vring->vring, vdev, 1); | |
188 | } | |
189 | if (s->cmd_vrings) { | |
190 | for (i = 0; i < vs->conf.num_queues && s->cmd_vrings[i]; i++) { | |
191 | vring_teardown(&s->cmd_vrings[i]->vring, vdev, 2 + i); | |
192 | } | |
193 | free(s->cmd_vrings); | |
194 | s->cmd_vrings = NULL; | |
195 | } | |
196 | } | |
197 | ||
91cb1c9b FZ |
198 | /* Context: QEMU global mutex held */ |
199 | void virtio_scsi_dataplane_start(VirtIOSCSI *s) | |
200 | { | |
201 | int i; | |
202 | int rc; | |
203 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
204 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
205 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); | |
206 | ||
207 | if (s->dataplane_started || | |
208 | s->dataplane_starting || | |
4adea804 | 209 | s->dataplane_fenced || |
91cb1c9b FZ |
210 | s->ctx != iothread_get_aio_context(vs->conf.iothread)) { |
211 | return; | |
212 | } | |
213 | ||
214 | s->dataplane_starting = true; | |
215 | ||
216 | /* Set up guest notifier (irq) */ | |
217 | rc = k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, true); | |
218 | if (rc != 0) { | |
6d2c8316 CH |
219 | fprintf(stderr, "virtio-scsi: Failed to set guest notifiers (%d), " |
220 | "ensure -enable-kvm is set\n", rc); | |
4adea804 | 221 | s->dataplane_fenced = true; |
361dcc79 | 222 | goto fail_guest_notifiers; |
91cb1c9b FZ |
223 | } |
224 | ||
225 | aio_context_acquire(s->ctx); | |
226 | s->ctrl_vring = virtio_scsi_vring_init(s, vs->ctrl_vq, | |
227 | virtio_scsi_iothread_handle_ctrl, | |
228 | 0); | |
361dcc79 CH |
229 | if (!s->ctrl_vring) { |
230 | goto fail_vrings; | |
231 | } | |
91cb1c9b FZ |
232 | s->event_vring = virtio_scsi_vring_init(s, vs->event_vq, |
233 | virtio_scsi_iothread_handle_event, | |
234 | 1); | |
361dcc79 CH |
235 | if (!s->event_vring) { |
236 | goto fail_vrings; | |
237 | } | |
ed4b4326 | 238 | s->cmd_vrings = g_new(VirtIOSCSIVring *, vs->conf.num_queues); |
91cb1c9b FZ |
239 | for (i = 0; i < vs->conf.num_queues; i++) { |
240 | s->cmd_vrings[i] = | |
241 | virtio_scsi_vring_init(s, vs->cmd_vqs[i], | |
242 | virtio_scsi_iothread_handle_cmd, | |
243 | i + 2); | |
361dcc79 CH |
244 | if (!s->cmd_vrings[i]) { |
245 | goto fail_vrings; | |
246 | } | |
91cb1c9b FZ |
247 | } |
248 | ||
91cb1c9b FZ |
249 | s->dataplane_starting = false; |
250 | s->dataplane_started = true; | |
ace386b4 PB |
251 | aio_context_release(s->ctx); |
252 | return; | |
361dcc79 CH |
253 | |
254 | fail_vrings: | |
255 | virtio_scsi_clear_aio(s); | |
256 | aio_context_release(s->ctx); | |
257 | virtio_scsi_vring_teardown(s); | |
258 | for (i = 0; i < vs->conf.num_queues + 2; i++) { | |
259 | k->set_host_notifier(qbus->parent, i, false); | |
260 | } | |
261 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
262 | fail_guest_notifiers: | |
263 | s->dataplane_starting = false; | |
91cb1c9b FZ |
264 | } |
265 | ||
266 | /* Context: QEMU global mutex held */ | |
267 | void virtio_scsi_dataplane_stop(VirtIOSCSI *s) | |
268 | { | |
269 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s))); | |
270 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
91cb1c9b FZ |
271 | VirtIOSCSICommon *vs = VIRTIO_SCSI_COMMON(s); |
272 | int i; | |
273 | ||
4adea804 CH |
274 | /* Better luck next time. */ |
275 | if (s->dataplane_fenced) { | |
276 | s->dataplane_fenced = false; | |
277 | return; | |
278 | } | |
91cb1c9b FZ |
279 | if (!s->dataplane_started || s->dataplane_stopping) { |
280 | return; | |
281 | } | |
282 | s->dataplane_stopping = true; | |
283 | assert(s->ctx == iothread_get_aio_context(vs->conf.iothread)); | |
284 | ||
285 | aio_context_acquire(s->ctx); | |
286 | ||
287 | aio_set_event_notifier(s->ctx, &s->ctrl_vring->host_notifier, NULL); | |
288 | aio_set_event_notifier(s->ctx, &s->event_vring->host_notifier, NULL); | |
289 | for (i = 0; i < vs->conf.num_queues; i++) { | |
290 | aio_set_event_notifier(s->ctx, &s->cmd_vrings[i]->host_notifier, NULL); | |
291 | } | |
292 | ||
4be74634 | 293 | blk_drain_all(); /* ensure there are no in-flight requests */ |
91cb1c9b FZ |
294 | |
295 | aio_context_release(s->ctx); | |
296 | ||
297 | /* Sync vring state back to virtqueue so that non-dataplane request | |
298 | * processing can continue when we disable the host notifier below. | |
299 | */ | |
361dcc79 | 300 | virtio_scsi_vring_teardown(s); |
91cb1c9b FZ |
301 | |
302 | for (i = 0; i < vs->conf.num_queues + 2; i++) { | |
303 | k->set_host_notifier(qbus->parent, i, false); | |
304 | } | |
305 | ||
306 | /* Clean up guest notifier (irq) */ | |
307 | k->set_guest_notifiers(qbus->parent, vs->conf.num_queues + 2, false); | |
308 | s->dataplane_stopping = false; | |
309 | s->dataplane_started = false; | |
310 | } |