include/qemu/osdep.h: Don't include qapi/error.h
[qemu/ar7.git] / hw / block / dataplane / virtio-blk.c
blobe666dd4ff0e78ee82bf6cb56930a5ab7a758509c
1 /*
2 * Dedicated thread for virtio-blk I/O processing
4 * Copyright 2012 IBM, Corp.
5 * Copyright 2012 Red Hat, Inc. and/or its affiliates
7 * Authors:
8 * Stefan Hajnoczi <stefanha@redhat.com>
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.
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"
29 struct VirtIOBlockDataPlane {
30 bool starting;
31 bool stopping;
32 bool disabled;
34 VirtIOBlkConf *conf;
36 VirtIODevice *vdev;
37 VirtQueue *vq; /* virtqueue vring */
38 EventNotifier *guest_notifier; /* irq */
39 QEMUBH *bh; /* bh for guest notification */
41 Notifier insert_notifier, remove_notifier;
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).
48 IOThread *iothread;
49 AioContext *ctx;
51 /* Operation blocker on BDS */
52 Error *blocker;
55 /* Raise an interrupt to signal guest, if necessary */
56 void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
58 qemu_bh_schedule(s->bh);
61 static void notify_guest_bh(void *opaque)
63 VirtIOBlockDataPlane *s = opaque;
65 if (!virtio_should_notify(s->vdev, s->vq)) {
66 return;
69 event_notifier_set(s->guest_notifier);
72 static void data_plane_set_up_op_blockers(VirtIOBlockDataPlane *s)
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);
95 static void data_plane_remove_op_blockers(VirtIOBlockDataPlane *s)
97 if (s->blocker) {
98 blk_op_unblock_all(s->conf->conf.blk, s->blocker);
99 error_free(s->blocker);
100 s->blocker = NULL;
104 static void data_plane_blk_insert_notifier(Notifier *n, void *data)
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);
112 static void data_plane_blk_remove_notifier(Notifier *n, void *data)
114 VirtIOBlockDataPlane *s = container_of(n, VirtIOBlockDataPlane,
115 remove_notifier);
116 assert(s->conf->conf.blk == data);
117 data_plane_remove_op_blockers(s);
120 /* Context: QEMU global mutex held */
121 void virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
122 VirtIOBlockDataPlane **dataplane,
123 Error **errp)
125 VirtIOBlockDataPlane *s;
126 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
127 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
129 *dataplane = NULL;
131 if (!conf->iothread) {
132 return;
135 /* Don't try if transport does not support notifiers. */
136 if (!k->set_guest_notifiers || !k->set_host_notifier) {
137 error_setg(errp,
138 "device is incompatible with dataplane "
139 "(transport does not support notifiers)");
140 return;
143 /* If dataplane is (re-)enabled while the guest is running there could be
144 * block jobs that can conflict.
146 if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
147 error_prepend(errp, "cannot start dataplane thread: ");
148 return;
151 s = g_new0(VirtIOBlockDataPlane, 1);
152 s->vdev = vdev;
153 s->conf = conf;
155 if (conf->iothread) {
156 s->iothread = conf->iothread;
157 object_ref(OBJECT(s->iothread));
159 s->ctx = iothread_get_aio_context(s->iothread);
160 s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
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);
167 data_plane_set_up_op_blockers(s);
169 *dataplane = s;
172 /* Context: QEMU global mutex held */
173 void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
175 if (!s) {
176 return;
179 virtio_blk_data_plane_stop(s);
180 data_plane_remove_op_blockers(s);
181 notifier_remove(&s->insert_notifier);
182 notifier_remove(&s->remove_notifier);
183 qemu_bh_delete(s->bh);
184 object_unref(OBJECT(s->iothread));
185 g_free(s);
188 /* Context: QEMU global mutex held */
189 void virtio_blk_data_plane_start(VirtIOBlockDataPlane *s)
191 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
192 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
193 VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
194 int r;
196 if (vblk->dataplane_started || s->starting) {
197 return;
200 s->starting = true;
201 s->vq = virtio_get_queue(s->vdev, 0);
203 /* Set up guest notifier (irq) */
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);
208 goto fail_guest_notifiers;
210 s->guest_notifier = virtio_queue_get_guest_notifier(s->vq);
212 /* Set up virtqueue notify */
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);
216 goto fail_host_notifier;
219 s->starting = false;
220 vblk->dataplane_started = true;
221 trace_virtio_blk_data_plane_start(s);
223 blk_set_aio_context(s->conf->conf.blk, s->ctx);
225 /* Kick right away to begin processing requests already in vring */
226 event_notifier_set(virtio_queue_get_host_notifier(s->vq));
228 /* Get this show started by hooking up our callbacks */
229 aio_context_acquire(s->ctx);
230 virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, true, true);
231 aio_context_release(s->ctx);
232 return;
234 fail_host_notifier:
235 k->set_guest_notifiers(qbus->parent, 1, false);
236 fail_guest_notifiers:
237 s->disabled = true;
238 s->starting = false;
239 vblk->dataplane_started = true;
242 /* Context: QEMU global mutex held */
243 void virtio_blk_data_plane_stop(VirtIOBlockDataPlane *s)
245 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(s->vdev)));
246 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
247 VirtIOBlock *vblk = VIRTIO_BLK(s->vdev);
249 if (!vblk->dataplane_started || s->stopping) {
250 return;
253 /* Better luck next time. */
254 if (s->disabled) {
255 s->disabled = false;
256 vblk->dataplane_started = false;
257 return;
259 s->stopping = true;
260 trace_virtio_blk_data_plane_stop(s);
262 aio_context_acquire(s->ctx);
264 /* Stop notifications for new requests from guest */
265 virtio_queue_aio_set_host_notifier_handler(s->vq, s->ctx, false, false);
267 /* Drain and switch bs back to the QEMU main loop */
268 blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
270 aio_context_release(s->ctx);
272 k->set_host_notifier(qbus->parent, 0, false);
274 /* Clean up guest notifier (irq) */
275 k->set_guest_notifiers(qbus->parent, 1, false);
277 vblk->dataplane_started = false;
278 s->stopping = false;