]> git.ipfire.org Git - thirdparty/qemu.git/blame - hw/block/dataplane/virtio-blk.c
include/qemu/osdep.h: Don't include qapi/error.h
[thirdparty/qemu.git] / hw / block / dataplane / virtio-blk.c
CommitLineData
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 29struct 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 56void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s)
e72f66a0 57{
03de2f52 58 qemu_bh_schedule(s->bh);
e72f66a0
SH
59}
60
5b2ffbe4
ML
61static 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
72static 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
95static 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
104static 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
112static 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 121void 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
173void 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
189void 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
243void 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}