xref: /qemu/hw/virtio/vhost-user-base.c (revision 73f81da0a3628180409a0ae90ece19534bcdf09b)
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 int 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 0;
78     }
79 
80     ret = vhost_dev_stop(&vub->vhost_dev, vdev, true);
81 
82     if (k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false) < 0) {
83         error_report("vhost guest notifier cleanup failed: %d", ret);
84         return -1;
85     }
86 
87     vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
88     return ret;
89 }
90 
91 static int 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 0;
98     }
99 
100     if (should_start) {
101         vub_start(vdev);
102     } else {
103         int ret;
104         ret = vub_stop(vdev);
105         if (ret < 0) {
106             return ret;
107         }
108     }
109     return 0;
110 }
111 
112 /*
113  * For an implementation where everything is delegated to the backend
114  * we don't do anything other than return the full feature set offered
115  * by the daemon (module the reserved feature bit).
116  */
117 static uint64_t vub_get_features(VirtIODevice *vdev,
118                                  uint64_t requested_features, Error **errp)
119 {
120     VHostUserBase *vub = VHOST_USER_BASE(vdev);
121     /* This should be set when the vhost connection initialises */
122     g_assert(vub->vhost_dev.features);
123     return vub->vhost_dev.features & ~(1ULL << VHOST_USER_F_PROTOCOL_FEATURES);
124 }
125 
126 /*
127  * To handle VirtIO config we need to know the size of the config
128  * space. We don't cache the config but re-fetch it from the guest
129  * every time in case something has changed.
130  */
131 static void vub_get_config(VirtIODevice *vdev, uint8_t *config)
132 {
133     VHostUserBase *vub = VHOST_USER_BASE(vdev);
134     Error *local_err = NULL;
135 
136     /*
137      * There will have been a warning during vhost_dev_init, but lets
138      * assert here as nothing will go right now.
139      */
140     g_assert(vub->config_size && vub->vhost_user.supports_config == true);
141 
142     if (vhost_dev_get_config(&vub->vhost_dev, config,
143                              vub->config_size, &local_err)) {
144         error_report_err(local_err);
145     }
146 }
147 
148 static void vub_set_config(VirtIODevice *vdev, const uint8_t *config_data)
149 {
150     VHostUserBase *vub = VHOST_USER_BASE(vdev);
151     int ret;
152 
153     g_assert(vub->config_size && vub->vhost_user.supports_config == true);
154 
155     ret = vhost_dev_set_config(&vub->vhost_dev, config_data,
156                                0, vub->config_size,
157                                VHOST_SET_CONFIG_TYPE_FRONTEND);
158     if (ret) {
159         error_report("vhost guest set device config space failed: %d", ret);
160         return;
161     }
162 }
163 
164 /*
165  * When the daemon signals an update to the config we just need to
166  * signal the guest as we re-read the config on demand above.
167  */
168 static int vub_config_notifier(struct vhost_dev *dev)
169 {
170     virtio_notify_config(dev->vdev);
171     return 0;
172 }
173 
174 const VhostDevConfigOps vub_config_ops = {
175     .vhost_dev_config_notifier = vub_config_notifier,
176 };
177 
178 static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq)
179 {
180     /*
181      * Not normally called; it's the daemon that handles the queue;
182      * however virtio's cleanup path can call this.
183      */
184 }
185 
186 static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub)
187 {
188     vhost_user_cleanup(&vub->vhost_user);
189 
190     for (int i = 0; i < vub->num_vqs; i++) {
191         VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
192         virtio_delete_queue(vq);
193     }
194 
195     virtio_cleanup(vdev);
196 }
197 
198 static int vub_connect(DeviceState *dev)
199 {
200     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
201     VHostUserBase *vub = VHOST_USER_BASE(vdev);
202     struct vhost_dev *vhost_dev = &vub->vhost_dev;
203 
204     if (vub->connected) {
205         return 0;
206     }
207     vub->connected = true;
208 
209     /*
210      * If we support VHOST_USER_GET_CONFIG we must enable the notifier
211      * so we can ping the guest when it updates.
212      */
213     if (vub->vhost_user.supports_config) {
214         vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops);
215     }
216 
217     /* restore vhost state */
218     if (virtio_device_started(vdev, vdev->status)) {
219         vub_start(vdev);
220     }
221 
222     return 0;
223 }
224 
225 static void vub_event(void *opaque, QEMUChrEvent event);
226 
227 static void vub_disconnect(DeviceState *dev)
228 {
229     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
230     VHostUserBase *vub = VHOST_USER_BASE(vdev);
231     struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
232 
233     if (!vub->connected) {
234         goto done;
235     }
236     vub->connected = false;
237 
238     vub_stop(vdev);
239     vhost_dev_cleanup(&vub->vhost_dev);
240     g_free(vhost_vqs);
241 
242 done:
243     /* Re-instate the event handler for new connections */
244     qemu_chr_fe_set_handlers(&vub->chardev,
245                              NULL, NULL, vub_event,
246                              NULL, dev, NULL, true);
247 }
248 
249 static void vub_event(void *opaque, QEMUChrEvent event)
250 {
251     DeviceState *dev = opaque;
252     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
253     VHostUserBase *vub = VHOST_USER_BASE(vdev);
254 
255     switch (event) {
256     case CHR_EVENT_OPENED:
257         if (vub_connect(dev) < 0) {
258             qemu_chr_fe_disconnect(&vub->chardev);
259             return;
260         }
261         break;
262     case CHR_EVENT_CLOSED:
263         /* defer close until later to avoid circular close */
264         vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev,
265                                vub_disconnect);
266         break;
267     case CHR_EVENT_BREAK:
268     case CHR_EVENT_MUX_IN:
269     case CHR_EVENT_MUX_OUT:
270         /* Ignore */
271         break;
272     }
273 }
274 
275 static void vub_device_realize(DeviceState *dev, Error **errp)
276 {
277     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
278     VHostUserBase *vub = VHOST_USER_BASE(dev);
279     int ret;
280 
281     if (!vub->chardev.chr) {
282         error_setg(errp, "vhost-user-base: missing chardev");
283         return;
284     }
285 
286     if (!vub->virtio_id) {
287         error_setg(errp, "vhost-user-base: need to define device id");
288         return;
289     }
290 
291     if (!vub->num_vqs) {
292         vub->num_vqs = 1; /* reasonable default? */
293     }
294 
295     if (!vub->vq_size) {
296         vub->vq_size = 64;
297     }
298 
299     /*
300      * We can't handle config requests unless we know the size of the
301      * config region, specialisations of the vhost-user-base will be
302      * able to set this.
303      */
304     if (vub->config_size) {
305         vub->vhost_user.supports_config = true;
306     }
307 
308     if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
309         return;
310     }
311 
312     virtio_init(vdev, vub->virtio_id, vub->config_size);
313 
314     /*
315      * Disable guest notifiers, by default all notifications will be via the
316      * asynchronous vhost-user socket.
317      */
318     vdev->use_guest_notifier_mask = false;
319 
320     /* Allocate queues */
321     vub->vqs = g_ptr_array_sized_new(vub->num_vqs);
322     for (int i = 0; i < vub->num_vqs; i++) {
323         g_ptr_array_add(vub->vqs,
324                         virtio_add_queue(vdev, vub->vq_size,
325                                          vub_handle_output));
326     }
327 
328     vub->vhost_dev.nvqs = vub->num_vqs;
329     vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
330 
331     /* connect to backend */
332     ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
333                          VHOST_BACKEND_TYPE_USER, 0, errp);
334 
335     if (ret < 0) {
336         do_vhost_user_cleanup(vdev, vub);
337     }
338 
339     qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
340                              dev, NULL, true);
341 }
342 
343 static void vub_device_unrealize(DeviceState *dev)
344 {
345     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
346     VHostUserBase *vub = VHOST_USER_BASE(dev);
347     struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
348 
349     /* This will stop vhost backend if appropriate. */
350     vub_set_status(vdev, 0);
351     vhost_dev_cleanup(&vub->vhost_dev);
352     g_free(vhost_vqs);
353     do_vhost_user_cleanup(vdev, vub);
354 }
355 
356 static void vub_class_init(ObjectClass *klass, const void *data)
357 {
358     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
359 
360     vdc->realize = vub_device_realize;
361     vdc->unrealize = vub_device_unrealize;
362     vdc->get_features = vub_get_features;
363     vdc->get_config = vub_get_config;
364     vdc->set_config = vub_set_config;
365     vdc->set_status = vub_set_status;
366 }
367 
368 static const TypeInfo vub_types[] = {
369     {
370         .name = TYPE_VHOST_USER_BASE,
371         .parent = TYPE_VIRTIO_DEVICE,
372         .instance_size = sizeof(VHostUserBase),
373         .class_init = vub_class_init,
374         .class_size = sizeof(VHostUserBaseClass),
375         .abstract = true
376     }
377 };
378 
379 DEFINE_TYPES(vub_types)
380