xref: /openbmc/qemu/hw/virtio/vhost-user-device.c (revision c1774bdb)
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 
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 
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);
180     struct vhost_dev *vhost_dev = &vub->vhost_dev;
181 
182     if (vub->connected) {
183         return 0;
184     }
185     vub->connected = true;
186 
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 
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 
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 
271     if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
272         return;
273     }
274 
275     virtio_init(vdev, vub->virtio_id, vub->config_size);
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;
325     vdc->get_config = vub_get_config;
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),
353     DEFINE_PROP_UINT32("config_size", VHostUserBase, config_size, 0),
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)
381