]> Git Repo - qemu.git/blob - hw/block/dataplane/virtio-blk.c
Merge remote-tracking branch 'origin/master' into HEAD
[qemu.git] / hw / block / dataplane / virtio-blk.c
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
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
29 struct VirtIOBlockDataPlane {
30     bool starting;
31     bool stopping;
32
33     VirtIOBlkConf *conf;
34     VirtIODevice *vdev;
35     QEMUBH *bh;                     /* bh for guest notification */
36     unsigned long *batch_notify_vqs;
37
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      */
43     IOThread *iothread;
44     AioContext *ctx;
45 };
46
47 /* Raise an interrupt to signal guest, if necessary */
48 void virtio_blk_data_plane_notify(VirtIOBlockDataPlane *s, VirtQueue *vq)
49 {
50     set_bit(virtio_get_queue_index(vq), s->batch_notify_vqs);
51     qemu_bh_schedule(s->bh);
52 }
53
54 static void notify_guest_bh(void *opaque)
55 {
56     VirtIOBlockDataPlane *s = opaque;
57     unsigned nvqs = s->conf->num_queues;
58     unsigned long bitmap[BITS_TO_LONGS(nvqs)];
59     unsigned j;
60
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];
66
67         while (bits != 0) {
68             unsigned i = j + ctzl(bits);
69             VirtQueue *vq = virtio_get_queue(s->vdev, i);
70
71             virtio_notify_irqfd(s->vdev, vq);
72
73             bits &= bits - 1; /* clear right-most bit */
74         }
75     }
76 }
77
78 /* Context: QEMU global mutex held */
79 bool virtio_blk_data_plane_create(VirtIODevice *vdev, VirtIOBlkConf *conf,
80                                   VirtIOBlockDataPlane **dataplane,
81                                   Error **errp)
82 {
83     VirtIOBlockDataPlane *s;
84     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
85     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
86
87     *dataplane = NULL;
88
89     if (conf->iothread) {
90         if (!k->set_guest_notifiers || !k->ioeventfd_assign) {
91             error_setg(errp,
92                        "device is incompatible with iothread "
93                        "(transport does not support notifiers)");
94             return false;
95         }
96         if (!virtio_device_ioeventfd_enabled(vdev)) {
97             error_setg(errp, "ioeventfd is required for iothread");
98             return false;
99         }
100
101         /* If dataplane is (re-)enabled while the guest is running there could
102          * be block jobs that can conflict.
103          */
104         if (blk_op_is_blocked(conf->conf.blk, BLOCK_OP_TYPE_DATAPLANE, errp)) {
105             error_prepend(errp, "cannot start virtio-blk dataplane: ");
106             return false;
107         }
108     }
109     /* Don't try if transport does not support notifiers. */
110     if (!virtio_device_ioeventfd_enabled(vdev)) {
111         return false;
112     }
113
114     s = g_new0(VirtIOBlockDataPlane, 1);
115     s->vdev = vdev;
116     s->conf = conf;
117
118     if (conf->iothread) {
119         s->iothread = conf->iothread;
120         object_ref(OBJECT(s->iothread));
121         s->ctx = iothread_get_aio_context(s->iothread);
122     } else {
123         s->ctx = qemu_get_aio_context();
124     }
125     s->bh = aio_bh_new(s->ctx, notify_guest_bh, s);
126     s->batch_notify_vqs = bitmap_new(conf->num_queues);
127
128     *dataplane = s;
129
130     return true;
131 }
132
133 /* Context: QEMU global mutex held */
134 void virtio_blk_data_plane_destroy(VirtIOBlockDataPlane *s)
135 {
136     VirtIOBlock *vblk;
137
138     if (!s) {
139         return;
140     }
141
142     vblk = VIRTIO_BLK(s->vdev);
143     assert(!vblk->dataplane_started);
144     g_free(s->batch_notify_vqs);
145     qemu_bh_delete(s->bh);
146     if (s->iothread) {
147         object_unref(OBJECT(s->iothread));
148     }
149     g_free(s);
150 }
151
152 static bool 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     return virtio_blk_handle_vq(s, vq);
161 }
162
163 /* Context: QEMU global mutex held */
164 int virtio_blk_data_plane_start(VirtIODevice *vdev)
165 {
166     VirtIOBlock *vblk = VIRTIO_BLK(vdev);
167     VirtIOBlockDataPlane *s = vblk->dataplane;
168     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vblk)));
169     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
170     unsigned i;
171     unsigned nvqs = s->conf->num_queues;
172     int r;
173
174     if (vblk->dataplane_started || s->starting) {
175         return 0;
176     }
177
178     s->starting = true;
179
180     /* Set up guest notifier (irq) */
181     r = k->set_guest_notifiers(qbus->parent, nvqs, true);
182     if (r != 0) {
183         fprintf(stderr, "virtio-blk failed to set guest notifier (%d), "
184                 "ensure -enable-kvm is set\n", r);
185         goto fail_guest_notifiers;
186     }
187
188     /* Set up virtqueue notify */
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         }
198     }
199
200     s->starting = false;
201     vblk->dataplane_started = true;
202     trace_virtio_blk_data_plane_start(s);
203
204     blk_set_aio_context(s->conf->conf.blk, s->ctx);
205
206     /* Kick right away to begin processing requests already in vring */
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     }
212
213     /* Get this show started by hooking up our callbacks */
214     aio_context_acquire(s->ctx);
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     }
221     aio_context_release(s->ctx);
222     return 0;
223
224   fail_guest_notifiers:
225     vblk->dataplane_disabled = true;
226     s->starting = false;
227     vblk->dataplane_started = true;
228     return -ENOSYS;
229 }
230
231 /* Context: QEMU global mutex held */
232 void virtio_blk_data_plane_stop(VirtIODevice *vdev)
233 {
234     VirtIOBlock *vblk = VIRTIO_BLK(vdev);
235     VirtIOBlockDataPlane *s = vblk->dataplane;
236     BusState *qbus = qdev_get_parent_bus(DEVICE(vblk));
237     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
238     unsigned i;
239     unsigned nvqs = s->conf->num_queues;
240
241     if (!vblk->dataplane_started || s->stopping) {
242         return;
243     }
244
245     /* Better luck next time. */
246     if (vblk->dataplane_disabled) {
247         vblk->dataplane_disabled = false;
248         vblk->dataplane_started = false;
249         return;
250     }
251     s->stopping = true;
252     trace_virtio_blk_data_plane_stop(s);
253
254     aio_context_acquire(s->ctx);
255
256     /* Stop notifications for new requests from guest */
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     }
262
263     /* Drain and switch bs back to the QEMU main loop */
264     blk_set_aio_context(s->conf->conf.blk, qemu_get_aio_context());
265
266     aio_context_release(s->ctx);
267
268     for (i = 0; i < nvqs; i++) {
269         virtio_bus_set_host_notifier(VIRTIO_BUS(qbus), i, false);
270     }
271
272     /* Clean up guest notifier (irq) */
273     k->set_guest_notifiers(qbus->parent, nvqs, false);
274
275     vblk->dataplane_started = false;
276     s->stopping = false;
277 }
This page took 0.035722 seconds and 4 git commands to generate.