]>
Commit | Line | Data |
---|---|---|
62759896 AB |
1 | /* |
2 | * Base vhost-user-base implementation. This can be used to derive a | |
3 | * more fully specified vhost-user backend either generically (see | |
4 | * vhost-user-device) or via a specific stub for a device which | |
5 | * encapsulates some fixed parameters. | |
6 | * | |
7 | * Copyright (c) 2023 Linaro Ltd | |
8 | * Author: Alex Bennée <alex.bennee@linaro.org> | |
9 | * | |
10 | * SPDX-License-Identifier: GPL-2.0-or-later | |
11 | */ | |
12 | ||
13 | #include "qemu/osdep.h" | |
14 | #include "qapi/error.h" | |
15 | #include "hw/qdev-properties.h" | |
16 | #include "hw/virtio/virtio-bus.h" | |
17 | #include "hw/virtio/vhost-user-base.h" | |
18 | #include "qemu/error-report.h" | |
19 | ||
20 | static void vub_start(VirtIODevice *vdev) | |
21 | { | |
22 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | |
23 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
24 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
25 | int ret, i; | |
26 | ||
27 | if (!k->set_guest_notifiers) { | |
28 | error_report("binding does not support guest notifiers"); | |
29 | return; | |
30 | } | |
31 | ||
32 | ret = vhost_dev_enable_notifiers(&vub->vhost_dev, vdev); | |
33 | if (ret < 0) { | |
34 | error_report("Error enabling host notifiers: %d", -ret); | |
35 | return; | |
36 | } | |
37 | ||
38 | ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, true); | |
39 | if (ret < 0) { | |
40 | error_report("Error binding guest notifier: %d", -ret); | |
41 | goto err_host_notifiers; | |
42 | } | |
43 | ||
44 | vub->vhost_dev.acked_features = vdev->guest_features; | |
45 | ||
46 | ret = vhost_dev_start(&vub->vhost_dev, vdev, true); | |
47 | if (ret < 0) { | |
48 | error_report("Error starting vhost-user-base: %d", -ret); | |
49 | goto err_guest_notifiers; | |
50 | } | |
51 | ||
52 | /* | |
53 | * guest_notifier_mask/pending not used yet, so just unmask | |
54 | * everything here. virtio-pci will do the right thing by | |
55 | * enabling/disabling irqfd. | |
56 | */ | |
57 | for (i = 0; i < vub->vhost_dev.nvqs; i++) { | |
58 | vhost_virtqueue_mask(&vub->vhost_dev, vdev, i, false); | |
59 | } | |
60 | ||
61 | return; | |
62 | ||
63 | err_guest_notifiers: | |
64 | k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); | |
65 | err_host_notifiers: | |
66 | vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); | |
67 | } | |
68 | ||
69 | static void vub_stop(VirtIODevice *vdev) | |
70 | { | |
71 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
72 | BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); | |
73 | VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); | |
74 | int ret; | |
75 | ||
76 | if (!k->set_guest_notifiers) { | |
77 | return; | |
78 | } | |
79 | ||
80 | vhost_dev_stop(&vub->vhost_dev, vdev, true); | |
81 | ||
82 | ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false); | |
83 | if (ret < 0) { | |
84 | error_report("vhost guest notifier cleanup failed: %d", ret); | |
85 | return; | |
86 | } | |
87 | ||
88 | vhost_dev_disable_notifiers(&vub->vhost_dev, vdev); | |
89 | } | |
90 | ||
91 | static void vub_set_status(VirtIODevice *vdev, uint8_t status) | |
92 | { | |
93 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
94 | bool should_start = virtio_device_should_start(vdev, status); | |
95 | ||
96 | if (vhost_dev_is_started(&vub->vhost_dev) == should_start) { | |
97 | return; | |
98 | } | |
99 | ||
100 | if (should_start) { | |
101 | vub_start(vdev); | |
102 | } else { | |
103 | vub_stop(vdev); | |
104 | } | |
105 | } | |
106 | ||
107 | /* | |
108 | * For an implementation where everything is delegated to the backend | |
109 | * we don't do anything other than return the full feature set offered | |
110 | * by the daemon (module the reserved feature bit). | |
111 | */ | |
112 | static uint64_t vub_get_features(VirtIODevice *vdev, | |
113 | uint64_t requested_features, Error **errp) | |
114 | { | |
115 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
116 | /* This should be set when the vhost connection initialises */ | |
117 | g_assert(vub->vhost_dev.features); | |
118 | return vub->vhost_dev.features & ~(1ULL << VHOST_USER_F_PROTOCOL_FEATURES); | |
119 | } | |
120 | ||
121 | /* | |
122 | * To handle VirtIO config we need to know the size of the config | |
123 | * space. We don't cache the config but re-fetch it from the guest | |
124 | * every time in case something has changed. | |
125 | */ | |
126 | static void vub_get_config(VirtIODevice *vdev, uint8_t *config) | |
127 | { | |
128 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
129 | Error *local_err = NULL; | |
130 | ||
131 | /* | |
132 | * There will have been a warning during vhost_dev_init, but lets | |
133 | * assert here as nothing will go right now. | |
134 | */ | |
135 | g_assert(vub->config_size && vub->vhost_user.supports_config == true); | |
136 | ||
137 | if (vhost_dev_get_config(&vub->vhost_dev, config, | |
138 | vub->config_size, &local_err)) { | |
139 | error_report_err(local_err); | |
140 | } | |
141 | } | |
142 | ||
a26105dd LY |
143 | static void vub_set_config(VirtIODevice *vdev, const uint8_t *config_data) |
144 | { | |
145 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
146 | int ret; | |
147 | ||
148 | g_assert(vub->config_size && vub->vhost_user.supports_config == true); | |
149 | ||
150 | ret = vhost_dev_set_config(&vub->vhost_dev, config_data, | |
151 | 0, vub->config_size, | |
152 | VHOST_SET_CONFIG_TYPE_FRONTEND); | |
153 | if (ret) { | |
154 | error_report("vhost guest set device config space failed: %d", ret); | |
155 | return; | |
156 | } | |
157 | } | |
158 | ||
62759896 AB |
159 | /* |
160 | * When the daemon signals an update to the config we just need to | |
161 | * signal the guest as we re-read the config on demand above. | |
162 | */ | |
163 | static int vub_config_notifier(struct vhost_dev *dev) | |
164 | { | |
165 | virtio_notify_config(dev->vdev); | |
166 | return 0; | |
167 | } | |
168 | ||
169 | const VhostDevConfigOps vub_config_ops = { | |
170 | .vhost_dev_config_notifier = vub_config_notifier, | |
171 | }; | |
172 | ||
173 | static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq) | |
174 | { | |
175 | /* | |
176 | * Not normally called; it's the daemon that handles the queue; | |
177 | * however virtio's cleanup path can call this. | |
178 | */ | |
179 | } | |
180 | ||
181 | static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub) | |
182 | { | |
183 | vhost_user_cleanup(&vub->vhost_user); | |
184 | ||
185 | for (int i = 0; i < vub->num_vqs; i++) { | |
186 | VirtQueue *vq = g_ptr_array_index(vub->vqs, i); | |
187 | virtio_delete_queue(vq); | |
188 | } | |
189 | ||
190 | virtio_cleanup(vdev); | |
191 | } | |
192 | ||
193 | static int vub_connect(DeviceState *dev) | |
194 | { | |
195 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
196 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
197 | struct vhost_dev *vhost_dev = &vub->vhost_dev; | |
198 | ||
199 | if (vub->connected) { | |
200 | return 0; | |
201 | } | |
202 | vub->connected = true; | |
203 | ||
204 | /* | |
205 | * If we support VHOST_USER_GET_CONFIG we must enable the notifier | |
206 | * so we can ping the guest when it updates. | |
207 | */ | |
208 | if (vub->vhost_user.supports_config) { | |
209 | vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops); | |
210 | } | |
211 | ||
212 | /* restore vhost state */ | |
213 | if (virtio_device_started(vdev, vdev->status)) { | |
214 | vub_start(vdev); | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } | |
219 | ||
64a312a2 AB |
220 | static void vub_event(void *opaque, QEMUChrEvent event); |
221 | ||
62759896 AB |
222 | static void vub_disconnect(DeviceState *dev) |
223 | { | |
224 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
225 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
226 | ||
227 | if (!vub->connected) { | |
228 | return; | |
229 | } | |
230 | vub->connected = false; | |
231 | ||
64a312a2 AB |
232 | vub_stop(vdev); |
233 | vhost_dev_cleanup(&vub->vhost_dev); | |
234 | ||
235 | /* Re-instate the event handler for new connections */ | |
236 | qemu_chr_fe_set_handlers(&vub->chardev, | |
237 | NULL, NULL, vub_event, | |
238 | NULL, dev, NULL, true); | |
62759896 AB |
239 | } |
240 | ||
241 | static void vub_event(void *opaque, QEMUChrEvent event) | |
242 | { | |
243 | DeviceState *dev = opaque; | |
244 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
245 | VHostUserBase *vub = VHOST_USER_BASE(vdev); | |
246 | ||
247 | switch (event) { | |
248 | case CHR_EVENT_OPENED: | |
249 | if (vub_connect(dev) < 0) { | |
250 | qemu_chr_fe_disconnect(&vub->chardev); | |
251 | return; | |
252 | } | |
253 | break; | |
254 | case CHR_EVENT_CLOSED: | |
64a312a2 AB |
255 | /* defer close until later to avoid circular close */ |
256 | vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev, | |
257 | vub_disconnect, vub_event); | |
62759896 AB |
258 | break; |
259 | case CHR_EVENT_BREAK: | |
260 | case CHR_EVENT_MUX_IN: | |
261 | case CHR_EVENT_MUX_OUT: | |
262 | /* Ignore */ | |
263 | break; | |
264 | } | |
265 | } | |
266 | ||
267 | static void vub_device_realize(DeviceState *dev, Error **errp) | |
268 | { | |
269 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
270 | VHostUserBase *vub = VHOST_USER_BASE(dev); | |
271 | int ret; | |
272 | ||
273 | if (!vub->chardev.chr) { | |
274 | error_setg(errp, "vhost-user-base: missing chardev"); | |
275 | return; | |
276 | } | |
277 | ||
278 | if (!vub->virtio_id) { | |
279 | error_setg(errp, "vhost-user-base: need to define device id"); | |
280 | return; | |
281 | } | |
282 | ||
283 | if (!vub->num_vqs) { | |
284 | vub->num_vqs = 1; /* reasonable default? */ | |
285 | } | |
286 | ||
287 | if (!vub->vq_size) { | |
288 | vub->vq_size = 64; | |
289 | } | |
290 | ||
291 | /* | |
292 | * We can't handle config requests unless we know the size of the | |
293 | * config region, specialisations of the vhost-user-base will be | |
294 | * able to set this. | |
295 | */ | |
296 | if (vub->config_size) { | |
297 | vub->vhost_user.supports_config = true; | |
298 | } | |
299 | ||
300 | if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) { | |
301 | return; | |
302 | } | |
303 | ||
304 | virtio_init(vdev, vub->virtio_id, vub->config_size); | |
305 | ||
306 | /* | |
307 | * Disable guest notifiers, by default all notifications will be via the | |
308 | * asynchronous vhost-user socket. | |
309 | */ | |
310 | vdev->use_guest_notifier_mask = false; | |
311 | ||
312 | /* Allocate queues */ | |
313 | vub->vqs = g_ptr_array_sized_new(vub->num_vqs); | |
314 | for (int i = 0; i < vub->num_vqs; i++) { | |
315 | g_ptr_array_add(vub->vqs, | |
316 | virtio_add_queue(vdev, vub->vq_size, | |
317 | vub_handle_output)); | |
318 | } | |
319 | ||
320 | vub->vhost_dev.nvqs = vub->num_vqs; | |
321 | vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs); | |
322 | ||
323 | /* connect to backend */ | |
324 | ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user, | |
325 | VHOST_BACKEND_TYPE_USER, 0, errp); | |
326 | ||
327 | if (ret < 0) { | |
328 | do_vhost_user_cleanup(vdev, vub); | |
329 | } | |
330 | ||
331 | qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL, | |
332 | dev, NULL, true); | |
333 | } | |
334 | ||
335 | static void vub_device_unrealize(DeviceState *dev) | |
336 | { | |
337 | VirtIODevice *vdev = VIRTIO_DEVICE(dev); | |
338 | VHostUserBase *vub = VHOST_USER_BASE(dev); | |
339 | struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs; | |
340 | ||
341 | /* This will stop vhost backend if appropriate. */ | |
342 | vub_set_status(vdev, 0); | |
343 | vhost_dev_cleanup(&vub->vhost_dev); | |
344 | g_free(vhost_vqs); | |
345 | do_vhost_user_cleanup(vdev, vub); | |
346 | } | |
347 | ||
348 | static void vub_class_init(ObjectClass *klass, void *data) | |
349 | { | |
350 | VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); | |
351 | ||
352 | vdc->realize = vub_device_realize; | |
353 | vdc->unrealize = vub_device_unrealize; | |
354 | vdc->get_features = vub_get_features; | |
355 | vdc->get_config = vub_get_config; | |
a26105dd | 356 | vdc->set_config = vub_set_config; |
62759896 AB |
357 | vdc->set_status = vub_set_status; |
358 | } | |
359 | ||
360 | static const TypeInfo vub_types[] = { | |
361 | { | |
362 | .name = TYPE_VHOST_USER_BASE, | |
363 | .parent = TYPE_VIRTIO_DEVICE, | |
364 | .instance_size = sizeof(VHostUserBase), | |
365 | .class_init = vub_class_init, | |
366 | .class_size = sizeof(VHostUserBaseClass), | |
367 | .abstract = true | |
368 | } | |
369 | }; | |
370 | ||
371 | DEFINE_TYPES(vub_types) |