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