1
2
3
4
5
6
7
8
9
10
11
12
13
14
15#include "qemu/osdep.h"
16#include "qapi/error.h"
17#include "trace.h"
18#include "qemu/iov.h"
19#include "qemu/thread.h"
20#include "qemu/error-report.h"
21#include "hw/virtio/virtio-access.h"
22#include "sysemu/block-backend.h"
23#include "hw/virtio/virtio-blk.h"
24#include "virtio-blk.h"
25#include "block/aio.h"
26#include "hw/virtio/virtio-bus.h"
27#include "qom/object_interfaces.h"
28
29struct VirtIOBlockDataPlane {
30 bool starting;
31 bool stopping;
32
33 VirtIOBlkConf *conf;
34
35 VirtIODevice *vdev;
36 VirtQueue *vq;
37 EventNotifier *guest_notifier;
38 QEMUBH *bh;
39
40 Notifier insert_notifier, remove_notifier;
41
42
43
44
45
46
47 IOThread *iothread;
48 AioContext *ctx;
49
50
51 Error *blocker;
52};
53
54
55void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
56{
57 qemu_bh_schedule(s->bh);
58}
59
60static void notify_guest_bh(void *opaque)
61{
62 VirtIOBlockDataPlane *s = opaque;
63
64 if (!virtio_should_notify(s->vdev, s->vq)) {
65 return;
66 }
67
68 event_notifier_set(s->guest_notifier);
69}
70
71static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s)
72{
73 assert(!s->blocker);
74 error_setg(&s->blocker, "block device is in use by data plane");
75 blk_op_block_all(s->conf->conf.blk, s->blocker);
76 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_RESIZE, s->blocker);
77 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_DRIVE_DEL, s->blocker);
78 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_BACKUP_SOURCE, s->blocker);
79 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_CHANGE, s->blocker);
80 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_SOURCE, s->blocker);
81 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_COMMIT_TARGET, s->blocker);
82 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EJECT, s->blocker);
83 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_EXTERNAL_SNAPSHOT,
84 s->blocker);
85 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT,
86 s->blocker);
87 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_INTERNAL_SNAPSHOT_DELETE,
88 s->blocker);
89 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_MIRROR_SOURCE, s->blocker);
90 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_STREAM, s->blocker);
91 blk_op_unblock(s->conf->conf.blk, BLOCK_OP_TYPE_REPLACE, s->blocker);
92}
93
94static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s)
95{
96 if (s->blocker) {
97 blk_op_unblock_all(s->conf->conf.blk, s->blocker);
98 error_free(s->blocker);
99 s->blocker = NULL;
100 }
101}
102
103static void data_plane_blk_insert_notifier(Notifier *n, void *data)
104{
105 VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane,
106 insert_notifier);
107 assert(s->conf->conf.blk == data);
108 data_plane_set_up_op_blockers(s);
109}
110
111static void data_plane_blk_remove_notifier(Notifier *n, void *data)
112{
113 VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane,
114 remove_notifier);
115 assert(s->conf->conf.blk == data);
116 data_plane_remove_op_blockers(s);
117}
118
119
120void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
121 VirtIOBlockDataPlane **dataplane,
122 Error **errp)
123{
124 VirtIOBlockDataPlane *s;
125 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
126 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
127
128 *dataplane = NULL;
129
130 if (!conf->iothread) {
131 return;
132 }
133
134
135 if (!k->set_guest_notifiers || !k->set_host_notifier) {
136 error_setg(errp,
137 "device is incompatible with dataplane "
138 "(transport does not support notifiers)");
139 return;
140 }
141
142
143
144
145 if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
146 error_prepend(errp, "cannot start dataplane thread: ");
147 return;
148 }
149
150 s = g_new0(VirtIOBlockDataPlane, 1);
151 s->vdev = vdev;
152 s->conf = conf;
153
154 if (conf->iothread) {
155 s->iothread = conf->iothread;
156 object_ref(OBJECT(s->iothread));
157 }
158 s->ctx = iothread_get_aio_context(s->iothread);
159 s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
160
161 s->insert_notifier.notify = data_plane_blk_insert_notifier;
162 s->remove_notifier.notify = data_plane_blk_remove_notifier;
163 blk_add_insert_bs_notifier(conf->conf.blk, &s->insert_notifier);
164 blk_add_remove_bs_notifier(conf->conf.blk, &s->remove_notifier);
165
166 data_plane_set_up_op_blockers(s);
167
168 *dataplane = s;
169}
170
171
172void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
173{
174 if (!s) {
175 return;
176 }
177
178 virtio_blk_data_plane_stop(s);
179 data_plane_remove_op_blockers(s);
180 notifier_remove(&s->insert_notifier);
181 notifier_remove(&s->remove_notifier);
182 qemu_bh_delete(s->bh);
183 object_unref(OBJECT(s->iothread));
184 g_free(s);
185}
186
187static void virtio_blk_data_plane_handle_output(VirtIODevice *vdev,
188 VirtQueue *vq)
189{
190 VirtIOBlock *s = (VirtIOBlock *)vdev;
191
192 assert(s->dataplane);
193 assert(s->dataplane_started);
194
195 virtio_blk_handle_vq(s, vq);
196}
197
198
199void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
200{
201 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
202 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
203 VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
204 int r;
205
206 if (vblk->dataplane_started || s->starting) {
207 return;
208 }
209
210 s->starting = true;
211 s->vq = virtio_get_queue(s->vdev, 0);
212
213
214 r = k->set_guest_notifiers(qbus->parent, 1, true);
215 if (r != 0) {
216 fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
217 "ensure -enable-kvm is set\n", r);
218 goto fail_guest_notifiers;
219 }
220 s->guest_notifier = virtio_queue_get_guest_notifier(s->vq);
221
222
223 r = k->set_host_notifier(qbus->parent, 0, true);
224 if (r != 0) {
225 fprintf(stderr, "virtio-blk failed to set host notifier (%d)\n", r);
226 goto fail_host_notifier;
227 }
228
229 s->starting = false;
230 vblk->dataplane_started = true;
231 trace_virtio_blk_data_plane_start(s);
232
233 blk_set_aio_context(s->conf->conf.blk, s->ctx);
234
235
236 event_notifier_set(virtio_queue_get_host_notifier(s->vq));
237
238
239 aio_context_acquire(s->ctx);
240 virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx,
241 virtio_blk_data_plane_handle_output);
242 aio_context_release(s->ctx);
243 return;
244
245 fail_host_notifier:
246 k->set_guest_notifiers(qbus->parent, 1, false);
247 fail_guest_notifiers:
248 vblk->dataplane_disabled = true;
249 s->starting = false;
250 vblk->dataplane_started = true;
251}
252
253
254void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
255{
256 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
257 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
258 VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
259
260 if (!vblk->dataplane_started || s->stopping) {
261 return;
262 }
263
264
265 if (vblk->dataplane_disabled) {
266 vblk->dataplane_disabled = false;
267 vblk->dataplane_started = false;
268 return;
269 }
270 s->stopping = true;
271 trace_virtio_blk_data_plane_stop(s);
272
273 aio_context_acquire(s->ctx);
274
275
276 virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, NULL);
277
278
279 blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
280
281 aio_context_release(s->ctx);
282
283 k->set_host_notifier(qbus->parent, 0, false);
284
285
286 k->set_guest_notifiers(qbus->parent, 1, false);
287
288 vblk->dataplane_started = false;
289 s->stopping = false;
290}
291