xref: /qemu/hw/virtio/vhost-user-base.c (revision 6275989647efb708f126eb4f880e593792301ed4)
1*62759896SAlex Bennée /*
2*62759896SAlex Bennée  * Base vhost-user-base implementation. This can be used to derive a
3*62759896SAlex Bennée  * more fully specified vhost-user backend either generically (see
4*62759896SAlex Bennée  * vhost-user-device) or via a specific stub for a device which
5*62759896SAlex Bennée  * encapsulates some fixed parameters.
6*62759896SAlex Bennée  *
7*62759896SAlex Bennée  * Copyright (c) 2023 Linaro Ltd
8*62759896SAlex Bennée  * Author: Alex Bennée <alex.bennee@linaro.org>
9*62759896SAlex Bennée  *
10*62759896SAlex Bennée  * SPDX-License-Identifier: GPL-2.0-or-later
11*62759896SAlex Bennée  */
12*62759896SAlex Bennée 
13*62759896SAlex Bennée #include "qemu/osdep.h"
14*62759896SAlex Bennée #include "qapi/error.h"
15*62759896SAlex Bennée #include "hw/qdev-properties.h"
16*62759896SAlex Bennée #include "hw/virtio/virtio-bus.h"
17*62759896SAlex Bennée #include "hw/virtio/vhost-user-base.h"
18*62759896SAlex Bennée #include "qemu/error-report.h"
19*62759896SAlex Bennée 
20*62759896SAlex Bennée static void vub_start(VirtIODevice *vdev)
21*62759896SAlex Bennée {
22*62759896SAlex Bennée     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
23*62759896SAlex Bennée     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
24*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
25*62759896SAlex Bennée     int ret, i;
26*62759896SAlex Bennée 
27*62759896SAlex Bennée     if (!k->set_guest_notifiers) {
28*62759896SAlex Bennée         error_report("binding does not support guest notifiers");
29*62759896SAlex Bennée         return;
30*62759896SAlex Bennée     }
31*62759896SAlex Bennée 
32*62759896SAlex Bennée     ret = vhost_dev_enable_notifiers(&vub->vhost_dev, vdev);
33*62759896SAlex Bennée     if (ret < 0) {
34*62759896SAlex Bennée         error_report("Error enabling host notifiers: %d", -ret);
35*62759896SAlex Bennée         return;
36*62759896SAlex Bennée     }
37*62759896SAlex Bennée 
38*62759896SAlex Bennée     ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, true);
39*62759896SAlex Bennée     if (ret < 0) {
40*62759896SAlex Bennée         error_report("Error binding guest notifier: %d", -ret);
41*62759896SAlex Bennée         goto err_host_notifiers;
42*62759896SAlex Bennée     }
43*62759896SAlex Bennée 
44*62759896SAlex Bennée     vub->vhost_dev.acked_features = vdev->guest_features;
45*62759896SAlex Bennée 
46*62759896SAlex Bennée     ret = vhost_dev_start(&vub->vhost_dev, vdev, true);
47*62759896SAlex Bennée     if (ret < 0) {
48*62759896SAlex Bennée         error_report("Error starting vhost-user-base: %d", -ret);
49*62759896SAlex Bennée         goto err_guest_notifiers;
50*62759896SAlex Bennée     }
51*62759896SAlex Bennée 
52*62759896SAlex Bennée     /*
53*62759896SAlex Bennée      * guest_notifier_mask/pending not used yet, so just unmask
54*62759896SAlex Bennée      * everything here. virtio-pci will do the right thing by
55*62759896SAlex Bennée      * enabling/disabling irqfd.
56*62759896SAlex Bennée      */
57*62759896SAlex Bennée     for (i = 0; i < vub->vhost_dev.nvqs; i++) {
58*62759896SAlex Bennée         vhost_virtqueue_mask(&vub->vhost_dev, vdev, i, false);
59*62759896SAlex Bennée     }
60*62759896SAlex Bennée 
61*62759896SAlex Bennée     return;
62*62759896SAlex Bennée 
63*62759896SAlex Bennée err_guest_notifiers:
64*62759896SAlex Bennée     k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false);
65*62759896SAlex Bennée err_host_notifiers:
66*62759896SAlex Bennée     vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
67*62759896SAlex Bennée }
68*62759896SAlex Bennée 
69*62759896SAlex Bennée static void vub_stop(VirtIODevice *vdev)
70*62759896SAlex Bennée {
71*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
72*62759896SAlex Bennée     BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
73*62759896SAlex Bennée     VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
74*62759896SAlex Bennée     int ret;
75*62759896SAlex Bennée 
76*62759896SAlex Bennée     if (!k->set_guest_notifiers) {
77*62759896SAlex Bennée         return;
78*62759896SAlex Bennée     }
79*62759896SAlex Bennée 
80*62759896SAlex Bennée     vhost_dev_stop(&vub->vhost_dev, vdev, true);
81*62759896SAlex Bennée 
82*62759896SAlex Bennée     ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false);
83*62759896SAlex Bennée     if (ret < 0) {
84*62759896SAlex Bennée         error_report("vhost guest notifier cleanup failed: %d", ret);
85*62759896SAlex Bennée         return;
86*62759896SAlex Bennée     }
87*62759896SAlex Bennée 
88*62759896SAlex Bennée     vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
89*62759896SAlex Bennée }
90*62759896SAlex Bennée 
91*62759896SAlex Bennée static void vub_set_status(VirtIODevice *vdev, uint8_t status)
92*62759896SAlex Bennée {
93*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
94*62759896SAlex Bennée     bool should_start = virtio_device_should_start(vdev, status);
95*62759896SAlex Bennée 
96*62759896SAlex Bennée     if (vhost_dev_is_started(&vub->vhost_dev) == should_start) {
97*62759896SAlex Bennée         return;
98*62759896SAlex Bennée     }
99*62759896SAlex Bennée 
100*62759896SAlex Bennée     if (should_start) {
101*62759896SAlex Bennée         vub_start(vdev);
102*62759896SAlex Bennée     } else {
103*62759896SAlex Bennée         vub_stop(vdev);
104*62759896SAlex Bennée     }
105*62759896SAlex Bennée }
106*62759896SAlex Bennée 
107*62759896SAlex Bennée /*
108*62759896SAlex Bennée  * For an implementation where everything is delegated to the backend
109*62759896SAlex Bennée  * we don't do anything other than return the full feature set offered
110*62759896SAlex Bennée  * by the daemon (module the reserved feature bit).
111*62759896SAlex Bennée  */
112*62759896SAlex Bennée static uint64_t vub_get_features(VirtIODevice *vdev,
113*62759896SAlex Bennée                                  uint64_t requested_features, Error **errp)
114*62759896SAlex Bennée {
115*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
116*62759896SAlex Bennée     /* This should be set when the vhost connection initialises */
117*62759896SAlex Bennée     g_assert(vub->vhost_dev.features);
118*62759896SAlex Bennée     return vub->vhost_dev.features & ~(1ULL << VHOST_USER_F_PROTOCOL_FEATURES);
119*62759896SAlex Bennée }
120*62759896SAlex Bennée 
121*62759896SAlex Bennée /*
122*62759896SAlex Bennée  * To handle VirtIO config we need to know the size of the config
123*62759896SAlex Bennée  * space. We don't cache the config but re-fetch it from the guest
124*62759896SAlex Bennée  * every time in case something has changed.
125*62759896SAlex Bennée  */
126*62759896SAlex Bennée static void vub_get_config(VirtIODevice *vdev, uint8_t *config)
127*62759896SAlex Bennée {
128*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
129*62759896SAlex Bennée     Error *local_err = NULL;
130*62759896SAlex Bennée 
131*62759896SAlex Bennée     /*
132*62759896SAlex Bennée      * There will have been a warning during vhost_dev_init, but lets
133*62759896SAlex Bennée      * assert here as nothing will go right now.
134*62759896SAlex Bennée      */
135*62759896SAlex Bennée     g_assert(vub->config_size && vub->vhost_user.supports_config == true);
136*62759896SAlex Bennée 
137*62759896SAlex Bennée     if (vhost_dev_get_config(&vub->vhost_dev, config,
138*62759896SAlex Bennée                              vub->config_size, &local_err)) {
139*62759896SAlex Bennée         error_report_err(local_err);
140*62759896SAlex Bennée     }
141*62759896SAlex Bennée }
142*62759896SAlex Bennée 
143*62759896SAlex Bennée /*
144*62759896SAlex Bennée  * When the daemon signals an update to the config we just need to
145*62759896SAlex Bennée  * signal the guest as we re-read the config on demand above.
146*62759896SAlex Bennée  */
147*62759896SAlex Bennée static int vub_config_notifier(struct vhost_dev *dev)
148*62759896SAlex Bennée {
149*62759896SAlex Bennée     virtio_notify_config(dev->vdev);
150*62759896SAlex Bennée     return 0;
151*62759896SAlex Bennée }
152*62759896SAlex Bennée 
153*62759896SAlex Bennée const VhostDevConfigOps vub_config_ops = {
154*62759896SAlex Bennée     .vhost_dev_config_notifier = vub_config_notifier,
155*62759896SAlex Bennée };
156*62759896SAlex Bennée 
157*62759896SAlex Bennée static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq)
158*62759896SAlex Bennée {
159*62759896SAlex Bennée     /*
160*62759896SAlex Bennée      * Not normally called; it's the daemon that handles the queue;
161*62759896SAlex Bennée      * however virtio's cleanup path can call this.
162*62759896SAlex Bennée      */
163*62759896SAlex Bennée }
164*62759896SAlex Bennée 
165*62759896SAlex Bennée static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub)
166*62759896SAlex Bennée {
167*62759896SAlex Bennée     vhost_user_cleanup(&vub->vhost_user);
168*62759896SAlex Bennée 
169*62759896SAlex Bennée     for (int i = 0; i < vub->num_vqs; i++) {
170*62759896SAlex Bennée         VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
171*62759896SAlex Bennée         virtio_delete_queue(vq);
172*62759896SAlex Bennée     }
173*62759896SAlex Bennée 
174*62759896SAlex Bennée     virtio_cleanup(vdev);
175*62759896SAlex Bennée }
176*62759896SAlex Bennée 
177*62759896SAlex Bennée static int vub_connect(DeviceState *dev)
178*62759896SAlex Bennée {
179*62759896SAlex Bennée     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
180*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
181*62759896SAlex Bennée     struct vhost_dev *vhost_dev = &vub->vhost_dev;
182*62759896SAlex Bennée 
183*62759896SAlex Bennée     if (vub->connected) {
184*62759896SAlex Bennée         return 0;
185*62759896SAlex Bennée     }
186*62759896SAlex Bennée     vub->connected = true;
187*62759896SAlex Bennée 
188*62759896SAlex Bennée     /*
189*62759896SAlex Bennée      * If we support VHOST_USER_GET_CONFIG we must enable the notifier
190*62759896SAlex Bennée      * so we can ping the guest when it updates.
191*62759896SAlex Bennée      */
192*62759896SAlex Bennée     if (vub->vhost_user.supports_config) {
193*62759896SAlex Bennée         vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops);
194*62759896SAlex Bennée     }
195*62759896SAlex Bennée 
196*62759896SAlex Bennée     /* restore vhost state */
197*62759896SAlex Bennée     if (virtio_device_started(vdev, vdev->status)) {
198*62759896SAlex Bennée         vub_start(vdev);
199*62759896SAlex Bennée     }
200*62759896SAlex Bennée 
201*62759896SAlex Bennée     return 0;
202*62759896SAlex Bennée }
203*62759896SAlex Bennée 
204*62759896SAlex Bennée static void vub_disconnect(DeviceState *dev)
205*62759896SAlex Bennée {
206*62759896SAlex Bennée     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
207*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
208*62759896SAlex Bennée 
209*62759896SAlex Bennée     if (!vub->connected) {
210*62759896SAlex Bennée         return;
211*62759896SAlex Bennée     }
212*62759896SAlex Bennée     vub->connected = false;
213*62759896SAlex Bennée 
214*62759896SAlex Bennée     if (vhost_dev_is_started(&vub->vhost_dev)) {
215*62759896SAlex Bennée         vub_stop(vdev);
216*62759896SAlex Bennée     }
217*62759896SAlex Bennée }
218*62759896SAlex Bennée 
219*62759896SAlex Bennée static void vub_event(void *opaque, QEMUChrEvent event)
220*62759896SAlex Bennée {
221*62759896SAlex Bennée     DeviceState *dev = opaque;
222*62759896SAlex Bennée     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
223*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(vdev);
224*62759896SAlex Bennée 
225*62759896SAlex Bennée     switch (event) {
226*62759896SAlex Bennée     case CHR_EVENT_OPENED:
227*62759896SAlex Bennée         if (vub_connect(dev) < 0) {
228*62759896SAlex Bennée             qemu_chr_fe_disconnect(&vub->chardev);
229*62759896SAlex Bennée             return;
230*62759896SAlex Bennée         }
231*62759896SAlex Bennée         break;
232*62759896SAlex Bennée     case CHR_EVENT_CLOSED:
233*62759896SAlex Bennée         vub_disconnect(dev);
234*62759896SAlex Bennée         break;
235*62759896SAlex Bennée     case CHR_EVENT_BREAK:
236*62759896SAlex Bennée     case CHR_EVENT_MUX_IN:
237*62759896SAlex Bennée     case CHR_EVENT_MUX_OUT:
238*62759896SAlex Bennée         /* Ignore */
239*62759896SAlex Bennée         break;
240*62759896SAlex Bennée     }
241*62759896SAlex Bennée }
242*62759896SAlex Bennée 
243*62759896SAlex Bennée static void vub_device_realize(DeviceState *dev, Error **errp)
244*62759896SAlex Bennée {
245*62759896SAlex Bennée     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
246*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(dev);
247*62759896SAlex Bennée     int ret;
248*62759896SAlex Bennée 
249*62759896SAlex Bennée     if (!vub->chardev.chr) {
250*62759896SAlex Bennée         error_setg(errp, "vhost-user-base: missing chardev");
251*62759896SAlex Bennée         return;
252*62759896SAlex Bennée     }
253*62759896SAlex Bennée 
254*62759896SAlex Bennée     if (!vub->virtio_id) {
255*62759896SAlex Bennée         error_setg(errp, "vhost-user-base: need to define device id");
256*62759896SAlex Bennée         return;
257*62759896SAlex Bennée     }
258*62759896SAlex Bennée 
259*62759896SAlex Bennée     if (!vub->num_vqs) {
260*62759896SAlex Bennée         vub->num_vqs = 1; /* reasonable default? */
261*62759896SAlex Bennée     }
262*62759896SAlex Bennée 
263*62759896SAlex Bennée     if (!vub->vq_size) {
264*62759896SAlex Bennée         vub->vq_size = 64;
265*62759896SAlex Bennée     }
266*62759896SAlex Bennée 
267*62759896SAlex Bennée     /*
268*62759896SAlex Bennée      * We can't handle config requests unless we know the size of the
269*62759896SAlex Bennée      * config region, specialisations of the vhost-user-base will be
270*62759896SAlex Bennée      * able to set this.
271*62759896SAlex Bennée      */
272*62759896SAlex Bennée     if (vub->config_size) {
273*62759896SAlex Bennée         vub->vhost_user.supports_config = true;
274*62759896SAlex Bennée     }
275*62759896SAlex Bennée 
276*62759896SAlex Bennée     if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
277*62759896SAlex Bennée         return;
278*62759896SAlex Bennée     }
279*62759896SAlex Bennée 
280*62759896SAlex Bennée     virtio_init(vdev, vub->virtio_id, vub->config_size);
281*62759896SAlex Bennée 
282*62759896SAlex Bennée     /*
283*62759896SAlex Bennée      * Disable guest notifiers, by default all notifications will be via the
284*62759896SAlex Bennée      * asynchronous vhost-user socket.
285*62759896SAlex Bennée      */
286*62759896SAlex Bennée     vdev->use_guest_notifier_mask = false;
287*62759896SAlex Bennée 
288*62759896SAlex Bennée     /* Allocate queues */
289*62759896SAlex Bennée     vub->vqs = g_ptr_array_sized_new(vub->num_vqs);
290*62759896SAlex Bennée     for (int i = 0; i < vub->num_vqs; i++) {
291*62759896SAlex Bennée         g_ptr_array_add(vub->vqs,
292*62759896SAlex Bennée                         virtio_add_queue(vdev, vub->vq_size,
293*62759896SAlex Bennée                                          vub_handle_output));
294*62759896SAlex Bennée     }
295*62759896SAlex Bennée 
296*62759896SAlex Bennée     vub->vhost_dev.nvqs = vub->num_vqs;
297*62759896SAlex Bennée     vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
298*62759896SAlex Bennée 
299*62759896SAlex Bennée     /* connect to backend */
300*62759896SAlex Bennée     ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
301*62759896SAlex Bennée                          VHOST_BACKEND_TYPE_USER, 0, errp);
302*62759896SAlex Bennée 
303*62759896SAlex Bennée     if (ret < 0) {
304*62759896SAlex Bennée         do_vhost_user_cleanup(vdev, vub);
305*62759896SAlex Bennée     }
306*62759896SAlex Bennée 
307*62759896SAlex Bennée     qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
308*62759896SAlex Bennée                              dev, NULL, true);
309*62759896SAlex Bennée }
310*62759896SAlex Bennée 
311*62759896SAlex Bennée static void vub_device_unrealize(DeviceState *dev)
312*62759896SAlex Bennée {
313*62759896SAlex Bennée     VirtIODevice *vdev = VIRTIO_DEVICE(dev);
314*62759896SAlex Bennée     VHostUserBase *vub = VHOST_USER_BASE(dev);
315*62759896SAlex Bennée     struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
316*62759896SAlex Bennée 
317*62759896SAlex Bennée     /* This will stop vhost backend if appropriate. */
318*62759896SAlex Bennée     vub_set_status(vdev, 0);
319*62759896SAlex Bennée     vhost_dev_cleanup(&vub->vhost_dev);
320*62759896SAlex Bennée     g_free(vhost_vqs);
321*62759896SAlex Bennée     do_vhost_user_cleanup(vdev, vub);
322*62759896SAlex Bennée }
323*62759896SAlex Bennée 
324*62759896SAlex Bennée static void vub_class_init(ObjectClass *klass, void *data)
325*62759896SAlex Bennée {
326*62759896SAlex Bennée     VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
327*62759896SAlex Bennée 
328*62759896SAlex Bennée     vdc->realize = vub_device_realize;
329*62759896SAlex Bennée     vdc->unrealize = vub_device_unrealize;
330*62759896SAlex Bennée     vdc->get_features = vub_get_features;
331*62759896SAlex Bennée     vdc->get_config = vub_get_config;
332*62759896SAlex Bennée     vdc->set_status = vub_set_status;
333*62759896SAlex Bennée }
334*62759896SAlex Bennée 
335*62759896SAlex Bennée static const TypeInfo vub_types[] = {
336*62759896SAlex Bennée     {
337*62759896SAlex Bennée         .name = TYPE_VHOST_USER_BASE,
338*62759896SAlex Bennée         .parent = TYPE_VIRTIO_DEVICE,
339*62759896SAlex Bennée         .instance_size = sizeof(VHostUserBase),
340*62759896SAlex Bennée         .class_init = vub_class_init,
341*62759896SAlex Bennée         .class_size = sizeof(VHostUserBaseClass),
342*62759896SAlex Bennée         .abstract = true
343*62759896SAlex Bennée     }
344*62759896SAlex Bennée };
345*62759896SAlex Bennée 
346*62759896SAlex Bennée DEFINE_TYPES(vub_types)
347