master
c 433 lines 12.2 KB
Raw
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/core/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 #include "migration/blocker.h"
20
21 static void vub_start(VirtIODevice *vdev)
22 {
23 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
24 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
25 VHostUserBase *vub = VHOST_USER_BASE(vdev);
26 int ret, i;
27
28 if (!k->set_guest_notifiers) {
29 error_report("binding does not support guest notifiers");
30 return;
31 }
32
33 ret = vhost_dev_enable_notifiers(&vub->vhost_dev, vdev);
34 if (ret < 0) {
35 error_report("Error enabling host notifiers: %d", -ret);
36 return;
37 }
38
39 ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, true);
40 if (ret < 0) {
41 error_report("Error binding guest notifier: %d", -ret);
42 goto err_host_notifiers;
43 }
44
45 vub->vhost_dev.acked_features = vdev->guest_features;
46
47 ret = vhost_dev_start(&vub->vhost_dev, vdev, true);
48 if (ret < 0) {
49 error_report("Error starting vhost-user-base: %d", -ret);
50 goto err_guest_notifiers;
51 }
52
53 /*
54 * guest_notifier_mask/pending not used yet, so just unmask
55 * everything here. virtio-pci will do the right thing by
56 * enabling/disabling irqfd.
57 */
58 for (i = 0; i < vub->vhost_dev.nvqs; i++) {
59 vhost_virtqueue_mask(&vub->vhost_dev, vdev, i, false);
60 }
61
62 return;
63
64 err_guest_notifiers:
65 k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false);
66 err_host_notifiers:
67 vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
68 }
69
70 static int vub_stop(VirtIODevice *vdev)
71 {
72 VHostUserBase *vub = VHOST_USER_BASE(vdev);
73 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
74 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
75 int ret, err;
76
77 if (!k->set_guest_notifiers) {
78 return 0;
79 }
80
81 ret = vhost_dev_stop(&vub->vhost_dev, vdev, true);
82
83 err = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false);
84 if (err < 0) {
85 error_report("vhost guest notifier cleanup failed: %d", err);
86 return err;
87 }
88
89 vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
90 return ret;
91 }
92
93 static int vub_set_status(VirtIODevice *vdev, uint8_t status)
94 {
95 VHostUserBase *vub = VHOST_USER_BASE(vdev);
96 bool should_start = virtio_device_should_start(vdev, status);
97
98 if (vhost_dev_is_started(&vub->vhost_dev) == should_start) {
99 return 0;
100 }
101
102 if (should_start) {
103 vub_start(vdev);
104 } else {
105 int ret;
106 ret = vub_stop(vdev);
107 if (ret < 0) {
108 return ret;
109 }
110 }
111 return 0;
112 }
113
114 /*
115 * For an implementation where everything is delegated to the backend
116 * we don't do anything other than return the full feature set offered
117 * by the daemon (module the reserved feature bit).
118 */
119 static uint64_t vub_get_features(VirtIODevice *vdev,
120 uint64_t requested_features, Error **errp)
121 {
122 VHostUserBase *vub = VHOST_USER_BASE(vdev);
123 uint64_t backend_features = vhost_dev_features(&vub->vhost_dev);
124
125 /* This should be set when the vhost connection initialises */
126 g_assert(backend_features);
127 virtio_clear_feature(&backend_features, VHOST_USER_F_PROTOCOL_FEATURES);
128
129 return backend_features;
130 }
131
132 /*
133 * To handle VirtIO config we need to know the size of the config
134 * space. We don't cache the config but re-fetch it from the guest
135 * every time in case something has changed.
136 */
137 static void vub_get_config(VirtIODevice *vdev, uint8_t *config)
138 {
139 VHostUserBase *vub = VHOST_USER_BASE(vdev);
140 Error *local_err = NULL;
141
142 /*
143 * There will have been a warning during vhost_dev_init, but lets
144 * assert here as nothing will go right now.
145 */
146 g_assert(vub->config_size && vub->vhost_user.supports_config == true);
147
148 if (vhost_dev_get_config(&vub->vhost_dev, config,
149 vub->config_size, &local_err)) {
150 error_report_err(local_err);
151 }
152 }
153
154 static void vub_set_config(VirtIODevice *vdev, const uint8_t *config_data)
155 {
156 VHostUserBase *vub = VHOST_USER_BASE(vdev);
157 int ret;
158
159 g_assert(vub->config_size && vub->vhost_user.supports_config == true);
160
161 ret = vhost_dev_set_config(&vub->vhost_dev, config_data,
162 0, vub->config_size,
163 VHOST_SET_CONFIG_TYPE_FRONTEND);
164 if (ret) {
165 error_report("vhost guest set device config space failed: %d", ret);
166 return;
167 }
168 }
169
170 /*
171 * When the daemon signals an update to the config we just need to
172 * signal the guest as we re-read the config on demand above.
173 */
174 static int vub_config_notifier(struct vhost_dev *dev)
175 {
176 virtio_notify_config(dev->vdev);
177 return 0;
178 }
179
180 const VhostDevConfigOps vub_config_ops = {
181 .vhost_dev_config_notifier = vub_config_notifier,
182 };
183
184 static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq)
185 {
186 /*
187 * Not normally called; it's the daemon that handles the queue;
188 * however virtio's cleanup path can call this.
189 */
190 }
191
192 static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub)
193 {
194 vhost_user_cleanup(&vub->vhost_user);
195
196 if (vub->vqs) {
197 for (int i = 0; i < vub->num_vqs; i++) {
198 VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
199 virtio_delete_queue(vq);
200 }
201 g_ptr_array_free(vub->vqs, true);
202 vub->vqs = NULL;
203 }
204
205 virtio_cleanup(vdev);
206 }
207
208 static int vub_connect(DeviceState *dev)
209 {
210 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
211 VHostUserBase *vub = VHOST_USER_BASE(vdev);
212 struct vhost_dev *vhost_dev = &vub->vhost_dev;
213
214 if (vub->connected) {
215 return 0;
216 }
217 vub->connected = true;
218
219 /*
220 * If we support VHOST_USER_GET_CONFIG we must enable the notifier
221 * so we can ping the guest when it updates.
222 */
223 if (vub->vhost_user.supports_config) {
224 vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops);
225 }
226
227 /* restore vhost state */
228 if (virtio_device_started(vdev, vdev->status)) {
229 vub_start(vdev);
230 }
231
232 return 0;
233 }
234
235 static void vub_event(void *opaque, QEMUChrEvent event);
236
237 static void vub_disconnect(DeviceState *dev)
238 {
239 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
240 VHostUserBase *vub = VHOST_USER_BASE(vdev);
241 struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
242
243 if (!vub->connected) {
244 goto done;
245 }
246 vub->connected = false;
247
248 vub_stop(vdev);
249 vhost_dev_cleanup(&vub->vhost_dev);
250 g_free(vhost_vqs);
251
252 done:
253 /* Re-instate the event handler for new connections */
254 qemu_chr_fe_set_handlers(&vub->chardev,
255 NULL, NULL, vub_event,
256 NULL, dev, NULL, true);
257 }
258
259 static void vub_event(void *opaque, QEMUChrEvent event)
260 {
261 DeviceState *dev = opaque;
262 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
263 VHostUserBase *vub = VHOST_USER_BASE(vdev);
264
265 switch (event) {
266 case CHR_EVENT_OPENED:
267 if (vub_connect(dev) < 0) {
268 qemu_chr_fe_disconnect(&vub->chardev);
269 return;
270 }
271 break;
272 case CHR_EVENT_CLOSED:
273 /* defer close until later to avoid circular close */
274 vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev,
275 vub_disconnect);
276 break;
277 case CHR_EVENT_BREAK:
278 case CHR_EVENT_MUX_IN:
279 case CHR_EVENT_MUX_OUT:
280 /* Ignore */
281 break;
282 }
283 }
284
285 static void vub_device_realize(DeviceState *dev, Error **errp)
286 {
287 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
288 VHostUserBase *vub = VHOST_USER_BASE(dev);
289 uint64_t memory_sizes[VIRTIO_MAX_SHMEM_REGIONS];
290 struct vhost_virtqueue *vhost_vqs = NULL;
291 int i, ret, nregions, regions_processed = 0;
292
293 if (!vub->chardev.chr) {
294 error_setg(errp, "vhost-user-base: missing chardev");
295 return;
296 }
297
298 if (!vub->virtio_id) {
299 error_setg(errp, "vhost-user-base: need to define device id");
300 return;
301 }
302
303 if (!vub->num_vqs) {
304 vub->num_vqs = 1; /* reasonable default? */
305 }
306
307 if (!vub->vq_size) {
308 vub->vq_size = 64;
309 }
310
311 /*
312 * We can't handle config requests unless we know the size of the
313 * config region, specialisations of the vhost-user-base will be
314 * able to set this.
315 */
316 if (vub->config_size) {
317 vub->vhost_user.supports_config = true;
318 }
319
320 if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
321 return;
322 }
323
324 virtio_init(vdev, vub->virtio_id, vub->config_size);
325
326 /*
327 * Disable guest notifiers, by default all notifications will be via the
328 * asynchronous vhost-user socket.
329 */
330 vdev->use_guest_notifier_mask = false;
331
332 /* Allocate queues */
333 vub->vqs = g_ptr_array_sized_new(vub->num_vqs);
334 for (i = 0; i < vub->num_vqs; i++) {
335 g_ptr_array_add(vub->vqs,
336 virtio_add_queue(vdev, vub->vq_size,
337 vub_handle_output));
338 }
339
340 vub->vhost_dev.nvqs = vub->num_vqs;
341 vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
342 vhost_vqs = vub->vhost_dev.vqs;
343
344 /* connect to backend */
345 ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
346 VHOST_BACKEND_TYPE_USER, 0, errp);
347
348 if (ret < 0) {
349 goto err;
350 }
351
352 ret = vub->vhost_dev.vhost_ops->vhost_get_shmem_config(&vub->vhost_dev,
353 &nregions,
354 memory_sizes,
355 errp);
356
357 if (ret < 0) {
358 goto err_vhost_dev;
359 }
360
361 for (i = 0; i < VIRTIO_MAX_SHMEM_REGIONS && regions_processed < nregions; i++) {
362 if (memory_sizes[i]) {
363 regions_processed++;
364 if (vub->vhost_dev.migration_blocker == NULL) {
365 error_setg(&vub->vhost_dev.migration_blocker,
366 "Migration disabled: devices with VIRTIO Shared Memory "
367 "Regions do not support migration yet.");
368 ret = migrate_add_blocker_normal(
369 &vub->vhost_dev.migration_blocker,
370 errp);
371
372 if (ret < 0) {
373 goto err_vhost_dev;
374 }
375 }
376
377 if (memory_sizes[i] % qemu_real_host_page_size() != 0) {
378 error_setg(errp, "Shared memory %d size must be a multiple of "
379 "the host page size", i);
380 goto err_vhost_dev;
381 }
382
383 virtio_new_shmem_region(vdev, i, memory_sizes[i]);
384 }
385 }
386
387 qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
388 dev, NULL, true);
389 return;
390 err_vhost_dev:
391 vhost_dev_cleanup(&vub->vhost_dev);
392 err:
393 g_free(vhost_vqs);
394 do_vhost_user_cleanup(vdev, vub);
395 }
396
397 static void vub_device_unrealize(DeviceState *dev)
398 {
399 VirtIODevice *vdev = VIRTIO_DEVICE(dev);
400 VHostUserBase *vub = VHOST_USER_BASE(dev);
401 struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
402
403 /* This will stop vhost backend if appropriate. */
404 vub_set_status(vdev, 0);
405 vhost_dev_cleanup(&vub->vhost_dev);
406 g_free(vhost_vqs);
407 do_vhost_user_cleanup(vdev, vub);
408 }
409
410 static void vub_class_init(ObjectClass *klass, const void *data)
411 {
412 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
413
414 vdc->realize = vub_device_realize;
415 vdc->unrealize = vub_device_unrealize;
416 vdc->get_features = vub_get_features;
417 vdc->get_config = vub_get_config;
418 vdc->set_config = vub_set_config;
419 vdc->set_status = vub_set_status;
420 }
421
422 static const TypeInfo vub_types[] = {
423 {
424 .name = TYPE_VHOST_USER_BASE,
425 .parent = TYPE_VIRTIO_DEVICE,
426 .instance_size = sizeof(VHostUserBase),
427 .class_init = vub_class_init,
428 .class_size = sizeof(VHostUserBaseClass),
429 .abstract = true
430 }
431 };
432
433 DEFINE_TYPES(vub_types)