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