xref: /openbmc/qemu/hw/virtio/vhost-user-base.c (revision d0b9b28a)
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         goto done;
229     }
230     vub->connected = false;
231 
232     vub_stop(vdev);
233     vhost_dev_cleanup(&vub->vhost_dev);
234 
235 done:
236     /* Re-instate the event handler for new connections */
237     qemu_chr_fe_set_handlers(&vub->chardev,
238                              NULL, NULL, vub_event,
239                              NULL, dev, NULL, true);
240 }
241 
242 static void vub_event(void *opaque, QEMUChrEvent event)
243 {
244     DeviceState *dev = opaque;
245     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
246     VHostUserBase *vub = VHOST_USER_BASE(vdev);
247 
248     switch (event) {
249     case CHR_EVENT_OPENED:
250         if (vub_connect(dev) < 0) {
251             qemu_chr_fe_disconnect(&vub->chardev);
252             return;
253         }
254         break;
255     case CHR_EVENT_CLOSED:
256         /* defer close until later to avoid circular close */
257         vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev,
258                                vub_disconnect);
259         break;
260     case CHR_EVENT_BREAK:
261     case CHR_EVENT_MUX_IN:
262     case CHR_EVENT_MUX_OUT:
263         /* Ignore */
264         break;
265     }
266 }
267 
268 static void vub_device_realize(DeviceState *dev, Error **errp)
269 {
270     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
271     VHostUserBase *vub = VHOST_USER_BASE(dev);
272     int ret;
273 
274     if (!vub->chardev.chr) {
275         error_setg(errp, "vhost-user-base: missing chardev");
276         return;
277     }
278 
279     if (!vub->virtio_id) {
280         error_setg(errp, "vhost-user-base: need to define device id");
281         return;
282     }
283 
284     if (!vub->num_vqs) {
285         vub->num_vqs = 1; /* reasonable default? */
286     }
287 
288     if (!vub->vq_size) {
289         vub->vq_size = 64;
290     }
291 
292     /*
293      * We can't handle config requests unless we know the size of the
294      * config region, specialisations of the vhost-user-base will be
295      * able to set this.
296      */
297     if (vub->config_size) {
298         vub->vhost_user.supports_config = true;
299     }
300 
301     if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
302         return;
303     }
304 
305     virtio_init(vdev, vub->virtio_id, vub->config_size);
306 
307     /*
308      * Disable guest notifiers, by default all notifications will be via the
309      * asynchronous vhost-user socket.
310      */
311     vdev->use_guest_notifier_mask = false;
312 
313     /* Allocate queues */
314     vub->vqs = g_ptr_array_sized_new(vub->num_vqs);
315     for (int i = 0; i < vub->num_vqs; i++) {
316         g_ptr_array_add(vub->vqs,
317                         virtio_add_queue(vdev, vub->vq_size,
318                                          vub_handle_output));
319     }
320 
321     vub->vhost_dev.nvqs = vub->num_vqs;
322     vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
323 
324     /* connect to backend */
325     ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
326                          VHOST_BACKEND_TYPE_USER, 0, errp);
327 
328     if (ret < 0) {
329         do_vhost_user_cleanup(vdev, vub);
330     }
331 
332     qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
333                              dev, NULL, true);
334 }
335 
336 static void vub_device_unrealize(DeviceState *dev)
337 {
338     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
339     VHostUserBase *vub = VHOST_USER_BASE(dev);
340     struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
341 
342     /* This will stop vhost backend if appropriate. */
343     vub_set_status(vdev, 0);
344     vhost_dev_cleanup(&vub->vhost_dev);
345     g_free(vhost_vqs);
346     do_vhost_user_cleanup(vdev, vub);
347 }
348 
349 static void vub_class_init(ObjectClass *klass, void *data)
350 {
351     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
352 
353     vdc->realize = vub_device_realize;
354     vdc->unrealize = vub_device_unrealize;
355     vdc->get_features = vub_get_features;
356     vdc->get_config = vub_get_config;
357     vdc->set_config = vub_set_config;
358     vdc->set_status = vub_set_status;
359 }
360 
361 static const TypeInfo vub_types[] = {
362     {
363         .name = TYPE_VHOST_USER_BASE,
364         .parent = TYPE_VIRTIO_DEVICE,
365         .instance_size = sizeof(VHostUserBase),
366         .class_init = vub_class_init,
367         .class_size = sizeof(VHostUserBaseClass),
368         .abstract = true
369     }
370 };
371 
372 DEFINE_TYPES(vub_types)
373