]>
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 <stefanha@redhat.com> | |
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; |
2f5f70fa | 32 | bool disabled; |
e72f66a0 | 33 | |
2a30307f | 34 | VirtIOBlkConf *conf; |
e72f66a0 SH |
35 | |
36 | VirtIODevice *vdev; | |
03de2f52 | 37 | VirtQueue *vq; /* virtqueue vring */ |
e72f66a0 | 38 | EventNotifier *guest_notifier; /* irq */ |
5b2ffbe4 | 39 | QEMUBH *bh; /* bh for guest notification */ |
e72f66a0 | 40 | |
1b1e0659 HR |
41 | Notifier insert_notifier, remove_notifier; |
42 | ||
2c20e711 PB |
43 | /* Note that these EventNotifiers are assigned by value. This is |
44 | * fine as long as you do not call event_notifier_cleanup on them | |
45 | * (because you don't own the file descriptor or handle; you just | |
46 | * use it). | |
47 | */ | |
48ff2692 | 48 | IOThread *iothread; |
2c20e711 | 49 | AioContext *ctx; |
e72f66a0 | 50 | |
3718d8ab FZ |
51 | /* Operation blocker on BDS */ |
52 | Error *blocker; | |
e72f66a0 SH |
53 | }; |
54 | ||
55 | /* Raise an interrupt to signal guest, if necessary */ | |
03de2f52 | 56 | void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s) |
e72f66a0 | 57 | { |
03de2f52 | 58 | qemu_bh_schedule(s->bh); |
e72f66a0 SH |
59 | } |
60 | ||
5b2ffbe4 ML |
61 | static void notify_guest_bh(void *opaque) |
62 | { | |
63 | VirtIOBlockDataPlane *s = opaque; | |
64 | ||
03de2f52 PB |
65 | if (!virtio_should_notify(s->vdev, s->vq)) { |
66 | return; | |
e72f66a0 | 67 | } |
03de2f52 PB |
68 | |
69 | event_notifier_set(s->guest_notifier); | |
e72f66a0 SH |
70 | } |
71 | ||
1b1e0659 HR |
72 | static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s) |
73 | { | |
74 | assert(!s->blocker); | |
75 | error_setg(&s->blocker, "block device is in use by data plane"); | |
76 | blk_op_block_all(s->conf->conf.blk, s->blocker); | |
77 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker); | |
78 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker); | |
79 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker); | |
80 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker); | |
81 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker); | |
82 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker); | |
83 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker); | |
84 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT, | |
85 | s->blocker); | |
86 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT, | |
87 | s->blocker); | |
88 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE, | |
89 | s->blocker); | |
90 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker); | |
91 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker); | |
92 | blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker); | |
93 | } | |
94 | ||
95 | static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s) | |
96 | { | |
97 | if (s->blocker) { | |
98 | blk_op_unblock_all(s->conf->conf.blk, s->blocker); | |
99 | error_free(s->blocker); | |
100 | s->blocker = NULL; | |
101 | } | |
102 | } | |
103 | ||
104 | static void data_plane_blk_insert_notifier(Notifier *n, void *data) | |
105 | { | |
106 | VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, | |
107 | insert_notifier); | |
108 | assert(s->conf->conf.blk == data); | |
109 | data_plane_set_up_op_blockers(s); | |
110 | } | |
111 | ||
112 | static void data_plane_blk_remove_notifier(Notifier *n, void *data) | |
113 | { | |
114 | VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane, | |
115 | remove_notifier); | |
116 | assert(s->conf->conf.blk == data); | |
117 | data_plane_remove_op_blockers(s); | |
118 | } | |
119 | ||
48ff2692 | 120 | /* Context: QEMU global mutex held */ |
2a30307f | 121 | void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf, |
3ffeeef7 AF |
122 | VirtIOBlockDataPlane **dataplane, |
123 | Error **errp) | |
e72f66a0 SH |
124 | { |
125 | VirtIOBlockDataPlane *s; | |
a9968c77 CH |
126 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); |
127 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
e72f66a0 SH |
128 | |
129 | *dataplane = NULL; | |
130 | ||
a616fb75 | 131 | if (!conf->iothread) { |
3ffeeef7 | 132 | return; |
e72f66a0 SH |
133 | } |
134 | ||
a9968c77 CH |
135 | /* Don't try if transport does not support notifiers. */ |
136 | if (!k->set_guest_notifiers || !k->set_host_notifier) { | |
137 | error_setg(errp, | |
a616fb75 | 138 | "device is incompatible with dataplane " |
a9968c77 CH |
139 | "(transport does not support notifiers)"); |
140 | return; | |
141 | } | |
142 | ||
b0f2027c SH |
143 | /* If dataplane is (re-)enabled while the guest is running there could be |
144 | * block jobs that can conflict. | |
145 | */ | |
e43bfd9c MA |
146 | if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) { |
147 | error_prepend(errp, "cannot start dataplane thread: "); | |
3ffeeef7 | 148 | return; |
b0f2027c SH |
149 | } |
150 | ||
e72f66a0 SH |
151 | s = g_new0(VirtIOBlockDataPlane, 1); |
152 | s->vdev = vdev; | |
2a30307f | 153 | s->conf = conf; |
e72f66a0 | 154 | |
2a30307f MA |
155 | if (conf->iothread) { |
156 | s->iothread = conf->iothread; | |
54bee5c2 | 157 | object_ref(OBJECT(s->iothread)); |
48ff2692 SH |
158 | } |
159 | s->ctx = iothread_get_aio_context(s->iothread); | |
5b2ffbe4 | 160 | s->bh = aio_bh_new(s->ctx, notify_guest_bh, s); |
48ff2692 | 161 | |
1b1e0659 HR |
162 | s->insert_notifier.notify = data_plane_blk_insert_notifier; |
163 | s->remove_notifier.notify = data_plane_blk_remove_notifier; | |
164 | blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier); | |
165 | blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier); | |
166 | ||
167 | data_plane_set_up_op_blockers(s); | |
e72f66a0 | 168 | |
e72f66a0 | 169 | *dataplane = s; |
e72f66a0 SH |
170 | } |
171 | ||
48ff2692 | 172 | /* Context: QEMU global mutex held */ |
e72f66a0 SH |
173 | void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s) |
174 | { | |
175 | if (!s) { | |
176 | return; | |
177 | } | |
178 | ||
179 | virtio_blk_data_plane_stop(s); | |
1b1e0659 HR |
180 | data_plane_remove_op_blockers(s); |
181 | notifier_remove(&s->insert_notifier); | |
182 | notifier_remove(&s->remove_notifier); | |
5b2ffbe4 | 183 | qemu_bh_delete(s->bh); |
fed105e2 | 184 | object_unref(OBJECT(s->iothread)); |
e72f66a0 SH |
185 | g_free(s); |
186 | } | |
187 | ||
48ff2692 | 188 | /* Context: QEMU global mutex held */ |
e72f66a0 SH |
189 | void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s) |
190 | { | |
1c819449 FK |
191 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); |
192 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
e926d9b8 | 193 | VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); |
267e1a20 | 194 | int r; |
e72f66a0 | 195 | |
2906cddf | 196 | if (vblk->dataplane_started || s->starting) { |
8caf907f CH |
197 | return; |
198 | } | |
199 | ||
200 | s->starting = true; | |
03de2f52 | 201 | s->vq = virtio_get_queue(s->vdev, 0); |
e72f66a0 | 202 | |
e72f66a0 | 203 | /* Set up guest notifier (irq) */ |
267e1a20 CH |
204 | r = k->set_guest_notifiers(qbus->parent, 1, true); |
205 | if (r != 0) { | |
206 | fprintf(stderr, "virtio-blk failed to set guest notifier (%d), " | |
207 | "ensure -enable-kvm is set\n", r); | |
f9907ebc | 208 | goto fail_guest_notifiers; |
e72f66a0 | 209 | } |
03de2f52 | 210 | s->guest_notifier = virtio_queue_get_guest_notifier(s->vq); |
e72f66a0 SH |
211 | |
212 | /* Set up virtqueue notify */ | |
267e1a20 CH |
213 | r = k->set_host_notifier(qbus->parent, 0, true); |
214 | if (r != 0) { | |
215 | fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r); | |
f9907ebc | 216 | goto fail_host_notifier; |
e72f66a0 | 217 | } |
e926d9b8 | 218 | |
8caf907f | 219 | s->starting = false; |
2906cddf | 220 | vblk->dataplane_started = true; |
e72f66a0 SH |
221 | trace_virtio_blk_data_plane_start(s); |
222 | ||
4be74634 | 223 | blk_set_aio_context(s->conf->conf.blk, s->ctx); |
580b6b2a | 224 | |
e72f66a0 | 225 | /* Kick right away to begin processing requests already in vring */ |
03de2f52 | 226 | event_notifier_set(virtio_queue_get_host_notifier(s->vq)); |
e72f66a0 | 227 | |
48ff2692 SH |
228 | /* Get this show started by hooking up our callbacks */ |
229 | aio_context_acquire(s->ctx); | |
03de2f52 | 230 | virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, true, true); |
48ff2692 | 231 | aio_context_release(s->ctx); |
f9907ebc CH |
232 | return; |
233 | ||
234 | fail_host_notifier: | |
235 | k->set_guest_notifiers(qbus->parent, 1, false); | |
236 | fail_guest_notifiers: | |
2906cddf | 237 | s->disabled = true; |
f9907ebc | 238 | s->starting = false; |
2906cddf | 239 | vblk->dataplane_started = true; |
e72f66a0 SH |
240 | } |
241 | ||
48ff2692 | 242 | /* Context: QEMU global mutex held */ |
e72f66a0 SH |
243 | void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s) |
244 | { | |
1c819449 FK |
245 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev))); |
246 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
b002254d | 247 | VirtIOBlock *vblk = VIRTIO_BLK(s->vdev); |
2f5f70fa | 248 | |
2906cddf PB |
249 | if (!vblk->dataplane_started || s->stopping) { |
250 | return; | |
251 | } | |
2f5f70fa CH |
252 | |
253 | /* Better luck next time. */ | |
254 | if (s->disabled) { | |
255 | s->disabled = false; | |
2906cddf | 256 | vblk->dataplane_started = false; |
e72f66a0 SH |
257 | return; |
258 | } | |
cd7fdfe5 | 259 | s->stopping = true; |
e72f66a0 SH |
260 | trace_virtio_blk_data_plane_stop(s); |
261 | ||
48ff2692 SH |
262 | aio_context_acquire(s->ctx); |
263 | ||
264 | /* Stop notifications for new requests from guest */ | |
03de2f52 | 265 | virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, false, false); |
48ff2692 | 266 | |
580b6b2a | 267 | /* Drain and switch bs back to the QEMU main loop */ |
4be74634 | 268 | blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context()); |
e72f66a0 | 269 | |
48ff2692 | 270 | aio_context_release(s->ctx); |
e72f66a0 | 271 | |
48ff2692 | 272 | k->set_host_notifier(qbus->parent, 0, false); |
e72f66a0 SH |
273 | |
274 | /* Clean up guest notifier (irq) */ | |
1c819449 | 275 | k->set_guest_notifiers(qbus->parent, 1, false); |
e72f66a0 | 276 | |
2906cddf | 277 | vblk->dataplane_started = false; |
cd7fdfe5 | 278 | s->stopping = false; |
e72f66a0 | 279 | } |