]>
Commit | Line | Data |
---|---|---|
e72f66a0 SH |
1 | /* |
2 | * Dedicated thread for virtio-blk I/O processing | |
3 | * | |
4 | * Copyright 2012 IBM, Corp. | |
5 | * Copyright 2012 Red Hat, Inc. and/or its affiliates | |
6 | * | |
7 | * Authors: | |
8 | * Stefan Hajnoczi <[email protected]> | |
9 | * | |
10 | * This work is licensed under the terms of the GNU GPL, version 2 or later. | |
11 | * See the COPYING file in the top-level directory. | |
12 | * | |
13 | */ | |
14 | ||
80c71a24 | 15 | #include "qemu/osdep.h" |
da34e65c | 16 | #include "qapi/error.h" |
e72f66a0 SH |
17 | #include "trace.h" |
18 | #include "qemu/iov.h" | |
e72f66a0 | 19 | #include "qemu/thread.h" |
b4a42f81 | 20 | #include "qemu/error-report.h" |
b0e5d90e | 21 | #include "hw/virtio/virtio-access.h" |
4be74634 | 22 | #include "sysemu/block-backend.h" |
0d09e41a PB |
23 | #include "hw/virtio/virtio-blk.h" |
24 | #include "virtio-blk.h" | |
2c20e711 | 25 | #include "block/aio.h" |
1c819449 | 26 | #include "hw/virtio/virtio-bus.h" |
54bee5c2 | 27 | #include "qom/object_interfaces.h" |
e72f66a0 | 28 | |
e72f66a0 | 29 | struct VirtIOBlockDataPlane { |
8caf907f | 30 | bool starting; |
cd7fdfe5 | 31 | bool stopping; |
e72f66a0 | 32 | |
2a30307f | 33 | VirtIOBlkConf *conf; |
e72f66a0 | 34 | VirtIODevice *vdev; |
5b2ffbe4 | 35 | QEMUBH *bh; /* bh for guest notification */ |
e21737ab | 36 | unsigned long *batch_notify_vqs; |
e72f66a0 | 37 | |
2c20e711 PB |
38 | /* Note that these EventNotifiers are assigned by value. This is |
39 | * fine as long as you do not call event_notifier_cleanup on them | |
40 | * (because you don't own the file descriptor or handle; you just | |
41 | * use it). | |
42 | */ | |
48ff2692 | 43 | IOThread *iothread; |
2c20e711 | 44 | AioContext *ctx; |
e72f66a0 SH |
45 | }; |
46 | ||
47 | /* Raise an interrupt to signal guest, if necessary */ | |
b234cdda | 48 | void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq) |
e72f66a0 | 49 | { |
b234cdda | 50 | set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs); |
03de2f52 | 51 | qemu_bh_schedule(s->bh); |
e72f66a0 SH |
52 | } |
53 | ||
5b2ffbe4 ML |
54 | static void notify_guest_bh(void *opaque) |
55 | { | |
56 | VirtIOBlockDataPlane *s = opaque; | |
e21737ab SH |
57 | unsigned nvqs = s->conf->num_queues; |
58 | unsigned long bitmap[BITS_TO_LONGS(nvqs)]; | |
59 | unsigned j; | |
5b2ffbe4 | 60 | |
e21737ab SH |
61 | memcpy(bitmap, s->batch_notify_vqs, sizeof(bitmap)); |
62 | memset(s->batch_notify_vqs, 0, sizeof(bitmap)); | |
63 | ||
64 | for (j = 0; j < nvqs; j += BITS_PER_LONG) { | |
65 | unsigned long bits = bitmap[j]; | |
03de2f52 | 66 | |
e21737ab SH |
67 | while (bits != 0) { |
68 | unsigned i = j + ctzl(bits); | |
69 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | |
70 | ||
71 | if (virtio_should_notify(s->vdev, vq)) { | |
72 | event_notifier_set(virtio_queue_get_guest_notifier(vq)); | |
73 | } | |
74 | ||
75 | bits &= bits - 1; /* clear right-most bit */ | |
76 | } | |
77 | } | |
e72f66a0 SH |
78 | } |
79 | ||
48ff2692 | 80 | /* Context: QEMU global mutex held */ |
2a30307f | 81 | void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, |
3ffeeef7 AF |
82 | VirtIOBlockDataPlane **dataplane, |
83 | Error **errp) | |
e72f66a0 SH |
84 | { |
85 | VirtIOBlockDataPlane *s; | |
a9968c77 CH |
86 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); |
87 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
e72f66a0 SH |
88 | |
89 | *dataplane = NULL; | |
90 | ||
9ffe337c PB |
91 | if (conf->iothread) { |
92 | if (!k->set_guest_notifiers || !k->ioeventfd_assign) { | |
93 | error_setg(errp, | |
94 | "device is incompatible with iothread " | |
95 | "(transport does not support notifiers)"); | |
96 | return; | |
97 | } | |
98 | if (!virtio_device_ioeventfd_enabled(vdev)) { | |
99 | error_setg(errp, "ioeventfd is required for iothread"); | |
100 | return; | |
101 | } | |
e72f66a0 | 102 | |
9ffe337c PB |
103 | /* If dataplane is (re-)enabled while the guest is running there could |
104 | * be block jobs that can conflict. | |
105 | */ | |
106 | if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { | |
107 | error_prepend(errp, "cannot start virtio-blk dataplane: "); | |
108 | return; | |
109 | } | |
a9968c77 | 110 | } |
9ffe337c PB |
111 | /* Don't try if transport does not support notifiers. */ |
112 | if (!virtio_device_ioeventfd_enabled(vdev)) { | |
3ffeeef7 | 113 | return; |
b0f2027c SH |
114 | } |
115 | ||
e72f66a0 SH |
116 | s = g_new0(VirtIOBlockDataPlane, 1); |
117 | s->vdev = vdev; | |
2a30307f | 118 | s->conf = conf; |
e72f66a0 | 119 | |
9ffe337c PB |
120 | if (conf->iothread) { |
121 | s->iothread = conf->iothread; | |
122 | object_ref(OBJECT(s->iothread)); | |
123 | s->ctx = iothread_get_aio_context(s->iothread); | |
124 | } else { | |
125 | s->ctx = qemu_get_aio_context(); | |
126 | } | |
5b2ffbe4 | 127 | s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); |
e21737ab | 128 | s->batch_notify_vqs = bitmap_new(conf->num_queues); |
48ff2692 | 129 | |
e72f66a0 | 130 | *dataplane = s; |
e72f66a0 SH |
131 | } |
132 | ||
48ff2692 | 133 | /* Context: QEMU global mutex held */ |
e72f66a0 SH |
134 | void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) |
135 | { | |
9ffe337c PB |
136 | VirtIOBlock *vblk; |
137 | ||
e72f66a0 SH |
138 | if (!s) { |
139 | return; | |
140 | } | |
141 | ||
9ffe337c PB |
142 | vblk = VIRTIO_BLK(s->vdev); |
143 | assert(!vblk->dataplane_started); | |
e21737ab | 144 | g_free(s->batch_notify_vqs); |
5b2ffbe4 | 145 | qemu_bh_delete(s->bh); |
9ffe337c PB |
146 | if (s->iothread) { |
147 | object_unref(OBJECT(s->iothread)); | |
148 | } | |
e72f66a0 SH |
149 | g_free(s); |
150 | } | |
151 | ||
8a2fad57 MT |
152 | static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev, |
153 | VirtQueue *vq) | |
154 | { | |
155 | VirtIOBlock *s = (VirtIOBlock *)vdev; | |
156 | ||
157 | assert(s->dataplane); | |
158 | assert(s->dataplane_started); | |
159 | ||
160 | virtio_blk_handle_vq(s, vq); | |
161 | } | |
162 | ||
48ff2692 | 163 | /* Context: QEMU global mutex held */ |
9ffe337c | 164 | int virtio_blk_data_plane_start(VirtIODevice *vdev) |
e72f66a0 | 165 | { |
9ffe337c PB |
166 | VirtIOBlock *vblk = VIRTIO_BLK(vdev); |
167 | VirtIOBlockDataPlane *s = vblk->dataplane; | |
168 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk))); | |
1c819449 | 169 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
51b04ac5 SH |
170 | unsigned i; |
171 | unsigned nvqs = s->conf->num_queues; | |
267e1a20 | 172 | int r; |
e72f66a0 | 173 | |
2906cddf | 174 | if (vblk->dataplane_started || s->starting) { |
9ffe337c | 175 | return 0; |
8caf907f CH |
176 | } |
177 | ||
178 | s->starting = true; | |
e72f66a0 | 179 | |
e72f66a0 | 180 | /* Set up guest notifier (irq) */ |
51b04ac5 | 181 | r = k->set_guest_notifiers(qbus->parent, nvqs, true); |
267e1a20 CH |
182 | if (r != 0) { |
183 | fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " | |
184 | "ensure -enable-kvm is set\n", r); | |
f9907ebc | 185 | goto fail_guest_notifiers; |
e72f66a0 | 186 | } |
e72f66a0 SH |
187 | |
188 | /* Set up virtqueue notify */ | |
51b04ac5 SH |
189 | for (i = 0; i < nvqs; i++) { |
190 | r = virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, true); | |
191 | if (r != 0) { | |
192 | fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); | |
193 | while (i--) { | |
194 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); | |
195 | } | |
196 | goto fail_guest_notifiers; | |
197 | } | |
e72f66a0 | 198 | } |
e926d9b8 | 199 | |
8caf907f | 200 | s->starting = false; |
2906cddf | 201 | vblk->dataplane_started = true; |
e72f66a0 SH |
202 | trace_virtio_blk_data_plane_start(s); |
203 | ||
4be74634 | 204 | blk_set_aio_context(s->conf->conf.blk, s->ctx); |
580b6b2a | 205 | |
e72f66a0 | 206 | /* Kick right away to begin processing requests already in vring */ |
51b04ac5 SH |
207 | for (i = 0; i < nvqs; i++) { |
208 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | |
209 | ||
210 | event_notifier_set(virtio_queue_get_host_notifier(vq)); | |
211 | } | |
e72f66a0 | 212 | |
48ff2692 SH |
213 | /* Get this show started by hooking up our callbacks */ |
214 | aio_context_acquire(s->ctx); | |
51b04ac5 SH |
215 | for (i = 0; i < nvqs; i++) { |
216 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | |
217 | ||
218 | virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, | |
219 | virtio_blk_data_plane_handle_output); | |
220 | } | |
48ff2692 | 221 | aio_context_release(s->ctx); |
9ffe337c | 222 | return 0; |
f9907ebc | 223 | |
f9907ebc | 224 | fail_guest_notifiers: |
eb41cf78 | 225 | vblk->dataplane_disabled = true; |
f9907ebc | 226 | s->starting = false; |
2906cddf | 227 | vblk->dataplane_started = true; |
9ffe337c | 228 | return -ENOSYS; |
e72f66a0 SH |
229 | } |
230 | ||
48ff2692 | 231 | /* Context: QEMU global mutex held */ |
9ffe337c | 232 | void virtio_blk_data_plane_stop(VirtIODevice *vdev) |
e72f66a0 | 233 | { |
9ffe337c PB |
234 | VirtIOBlock *vblk = VIRTIO_BLK(vdev); |
235 | VirtIOBlockDataPlane *s = vblk->dataplane; | |
236 | BusState *qbus = qdev_get_parent_bus(DEVICE(vblk)); | |
1c819449 | 237 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); |
51b04ac5 SH |
238 | unsigned i; |
239 | unsigned nvqs = s->conf->num_queues; | |
2f5f70fa | 240 | |
2906cddf PB |
241 | if (!vblk->dataplane_started || s->stopping) { |
242 | return; | |
243 | } | |
2f5f70fa CH |
244 | |
245 | /* Better luck next time. */ | |
eb41cf78 PB |
246 | if (vblk->dataplane_disabled) { |
247 | vblk->dataplane_disabled = false; | |
2906cddf | 248 | vblk->dataplane_started = false; |
e72f66a0 SH |
249 | return; |
250 | } | |
cd7fdfe5 | 251 | s->stopping = true; |
e72f66a0 SH |
252 | trace_virtio_blk_data_plane_stop(s); |
253 | ||
48ff2692 SH |
254 | aio_context_acquire(s->ctx); |
255 | ||
256 | /* Stop notifications for new requests from guest */ | |
51b04ac5 SH |
257 | for (i = 0; i < nvqs; i++) { |
258 | VirtQueue *vq = virtio_get_queue(s->vdev, i); | |
259 | ||
260 | virtio_queue_aio_set_host_notifier_handler(vq, s->ctx, NULL); | |
261 | } | |
48ff2692 | 262 | |
580b6b2a | 263 | /* Drain and switch bs back to the QEMU main loop */ |
4be74634 | 264 | blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); |
e72f66a0 | 265 | |
48ff2692 | 266 | aio_context_release(s->ctx); |
e72f66a0 | 267 | |
51b04ac5 SH |
268 | for (i = 0; i < nvqs; i++) { |
269 | virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false); | |
270 | } | |
e72f66a0 SH |
271 | |
272 | /* Clean up guest notifier (irq) */ | |
51b04ac5 | 273 | k->set_guest_notifiers(qbus->parent, nvqs, false); |
e72f66a0 | 274 | |
2906cddf | 275 | vblk->dataplane_started = false; |
cd7fdfe5 | 276 | s->stopping = false; |
e72f66a0 | 277 | } |