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