xref: /openbmc/qemu/hw/virtio/vhost-user-base.c (revision 64c6e744)
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 
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 
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 
220 static void vub_event(void *opaque, QEMUChrEvent event);
221 
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 
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);
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:
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);
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;
356     vdc->set_config = vub_set_config;
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)
372