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
vub_start(VirtIODevice * vdev)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
vub_stop(VirtIODevice * vdev)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
vub_set_status(VirtIODevice * vdev,uint8_t status)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 */
vub_get_features(VirtIODevice * vdev,uint64_t requested_features,Error ** errp)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 */
vub_get_config(VirtIODevice * vdev,uint8_t * config)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
vub_set_config(VirtIODevice * vdev,const uint8_t * config_data)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 */
vub_config_notifier(struct vhost_dev * dev)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
vub_handle_output(VirtIODevice * vdev,VirtQueue * vq)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
do_vhost_user_cleanup(VirtIODevice * vdev,VHostUserBase * vub)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
vub_connect(DeviceState * dev)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
vub_disconnect(DeviceState * dev)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
vub_event(void * opaque,QEMUChrEvent event)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
vub_device_realize(DeviceState * dev,Error ** errp)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
vub_device_unrealize(DeviceState * dev)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
vub_class_init(ObjectClass * klass,const void * data)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