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