162759896SAlex Bennée /*
262759896SAlex Bennée * Base vhost-user-base implementation. This can be used to derive a
362759896SAlex Bennée * more fully specified vhost-user backend either generically (see
462759896SAlex Bennée * vhost-user-device) or via a specific stub for a device which
562759896SAlex Bennée * encapsulates some fixed parameters.
662759896SAlex Bennée *
762759896SAlex Bennée * Copyright (c) 2023 Linaro Ltd
862759896SAlex Bennée * Author: Alex Bennée <alex.bennee@linaro.org>
962759896SAlex Bennée *
1062759896SAlex Bennée * SPDX-License-Identifier: GPL-2.0-or-later
1162759896SAlex Bennée */
1262759896SAlex Bennée
1362759896SAlex Bennée #include "qemu/osdep.h"
1462759896SAlex Bennée #include "qapi/error.h"
1562759896SAlex Bennée #include "hw/qdev-properties.h"
1662759896SAlex Bennée #include "hw/virtio/virtio-bus.h"
1762759896SAlex Bennée #include "hw/virtio/vhost-user-base.h"
1862759896SAlex Bennée #include "qemu/error-report.h"
1962759896SAlex Bennée
vub_start(VirtIODevice * vdev)2062759896SAlex Bennée static void vub_start(VirtIODevice *vdev)
2162759896SAlex Bennée {
2262759896SAlex Bennée BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
2362759896SAlex Bennée VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
2462759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
2562759896SAlex Bennée int ret, i;
2662759896SAlex Bennée
2762759896SAlex Bennée if (!k->set_guest_notifiers) {
2862759896SAlex Bennée error_report("binding does not support guest notifiers");
2962759896SAlex Bennée return;
3062759896SAlex Bennée }
3162759896SAlex Bennée
3262759896SAlex Bennée ret = vhost_dev_enable_notifiers(&vub->vhost_dev, vdev);
3362759896SAlex Bennée if (ret < 0) {
3462759896SAlex Bennée error_report("Error enabling host notifiers: %d", -ret);
3562759896SAlex Bennée return;
3662759896SAlex Bennée }
3762759896SAlex Bennée
3862759896SAlex Bennée ret = k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, true);
3962759896SAlex Bennée if (ret < 0) {
4062759896SAlex Bennée error_report("Error binding guest notifier: %d", -ret);
4162759896SAlex Bennée goto err_host_notifiers;
4262759896SAlex Bennée }
4362759896SAlex Bennée
4462759896SAlex Bennée vub->vhost_dev.acked_features = vdev->guest_features;
4562759896SAlex Bennée
4662759896SAlex Bennée ret = vhost_dev_start(&vub->vhost_dev, vdev, true);
4762759896SAlex Bennée if (ret < 0) {
4862759896SAlex Bennée error_report("Error starting vhost-user-base: %d", -ret);
4962759896SAlex Bennée goto err_guest_notifiers;
5062759896SAlex Bennée }
5162759896SAlex Bennée
5262759896SAlex Bennée /*
5362759896SAlex Bennée * guest_notifier_mask/pending not used yet, so just unmask
5462759896SAlex Bennée * everything here. virtio-pci will do the right thing by
5562759896SAlex Bennée * enabling/disabling irqfd.
5662759896SAlex Bennée */
5762759896SAlex Bennée for (i = 0; i < vub->vhost_dev.nvqs; i++) {
5862759896SAlex Bennée vhost_virtqueue_mask(&vub->vhost_dev, vdev, i, false);
5962759896SAlex Bennée }
6062759896SAlex Bennée
6162759896SAlex Bennée return;
6262759896SAlex Bennée
6362759896SAlex Bennée err_guest_notifiers:
6462759896SAlex Bennée k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false);
6562759896SAlex Bennée err_host_notifiers:
6662759896SAlex Bennée vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
6762759896SAlex Bennée }
6862759896SAlex Bennée
vub_stop(VirtIODevice * vdev)69*bc85aae4SHaoqian He static int vub_stop(VirtIODevice *vdev)
7062759896SAlex Bennée {
7162759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
7262759896SAlex Bennée BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev)));
7362759896SAlex Bennée VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus);
7462759896SAlex Bennée int ret;
7562759896SAlex Bennée
7662759896SAlex Bennée if (!k->set_guest_notifiers) {
77*bc85aae4SHaoqian He return 0;
7862759896SAlex Bennée }
7962759896SAlex Bennée
80*bc85aae4SHaoqian He ret = vhost_dev_stop(&vub->vhost_dev, vdev, true);
8162759896SAlex Bennée
82*bc85aae4SHaoqian He if (k->set_guest_notifiers(qbus->parent, vub->vhost_dev.nvqs, false) < 0) {
8362759896SAlex Bennée error_report("vhost guest notifier cleanup failed: %d", ret);
84*bc85aae4SHaoqian He return -1;
8562759896SAlex Bennée }
8662759896SAlex Bennée
8762759896SAlex Bennée vhost_dev_disable_notifiers(&vub->vhost_dev, vdev);
88*bc85aae4SHaoqian He return ret;
8962759896SAlex Bennée }
9062759896SAlex Bennée
vub_set_status(VirtIODevice * vdev,uint8_t status)91*bc85aae4SHaoqian He static int vub_set_status(VirtIODevice *vdev, uint8_t status)
9262759896SAlex Bennée {
9362759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
9462759896SAlex Bennée bool should_start = virtio_device_should_start(vdev, status);
9562759896SAlex Bennée
9662759896SAlex Bennée if (vhost_dev_is_started(&vub->vhost_dev) == should_start) {
97*bc85aae4SHaoqian He return 0;
9862759896SAlex Bennée }
9962759896SAlex Bennée
10062759896SAlex Bennée if (should_start) {
10162759896SAlex Bennée vub_start(vdev);
10262759896SAlex Bennée } else {
103*bc85aae4SHaoqian He int ret;
104*bc85aae4SHaoqian He ret = vub_stop(vdev);
105*bc85aae4SHaoqian He if (ret < 0) {
106*bc85aae4SHaoqian He return ret;
10762759896SAlex Bennée }
10862759896SAlex Bennée }
109*bc85aae4SHaoqian He return 0;
110*bc85aae4SHaoqian He }
11162759896SAlex Bennée
11262759896SAlex Bennée /*
11362759896SAlex Bennée * For an implementation where everything is delegated to the backend
11462759896SAlex Bennée * we don't do anything other than return the full feature set offered
11562759896SAlex Bennée * by the daemon (module the reserved feature bit).
11662759896SAlex Bennée */
vub_get_features(VirtIODevice * vdev,uint64_t requested_features,Error ** errp)11762759896SAlex Bennée static uint64_t vub_get_features(VirtIODevice *vdev,
11862759896SAlex Bennée uint64_t requested_features, Error **errp)
11962759896SAlex Bennée {
12062759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
12162759896SAlex Bennée /* This should be set when the vhost connection initialises */
12262759896SAlex Bennée g_assert(vub->vhost_dev.features);
12362759896SAlex Bennée return vub->vhost_dev.features & ~(1ULL << VHOST_USER_F_PROTOCOL_FEATURES);
12462759896SAlex Bennée }
12562759896SAlex Bennée
12662759896SAlex Bennée /*
12762759896SAlex Bennée * To handle VirtIO config we need to know the size of the config
12862759896SAlex Bennée * space. We don't cache the config but re-fetch it from the guest
12962759896SAlex Bennée * every time in case something has changed.
13062759896SAlex Bennée */
vub_get_config(VirtIODevice * vdev,uint8_t * config)13162759896SAlex Bennée static void vub_get_config(VirtIODevice *vdev, uint8_t *config)
13262759896SAlex Bennée {
13362759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
13462759896SAlex Bennée Error *local_err = NULL;
13562759896SAlex Bennée
13662759896SAlex Bennée /*
13762759896SAlex Bennée * There will have been a warning during vhost_dev_init, but lets
13862759896SAlex Bennée * assert here as nothing will go right now.
13962759896SAlex Bennée */
14062759896SAlex Bennée g_assert(vub->config_size && vub->vhost_user.supports_config == true);
14162759896SAlex Bennée
14262759896SAlex Bennée if (vhost_dev_get_config(&vub->vhost_dev, config,
14362759896SAlex Bennée vub->config_size, &local_err)) {
14462759896SAlex Bennée error_report_err(local_err);
14562759896SAlex Bennée }
14662759896SAlex Bennée }
14762759896SAlex Bennée
vub_set_config(VirtIODevice * vdev,const uint8_t * config_data)148a26105ddSLeo Yan static void vub_set_config(VirtIODevice *vdev, const uint8_t *config_data)
149a26105ddSLeo Yan {
150a26105ddSLeo Yan VHostUserBase *vub = VHOST_USER_BASE(vdev);
151a26105ddSLeo Yan int ret;
152a26105ddSLeo Yan
153a26105ddSLeo Yan g_assert(vub->config_size && vub->vhost_user.supports_config == true);
154a26105ddSLeo Yan
155a26105ddSLeo Yan ret = vhost_dev_set_config(&vub->vhost_dev, config_data,
156a26105ddSLeo Yan 0, vub->config_size,
157a26105ddSLeo Yan VHOST_SET_CONFIG_TYPE_FRONTEND);
158a26105ddSLeo Yan if (ret) {
159a26105ddSLeo Yan error_report("vhost guest set device config space failed: %d", ret);
160a26105ddSLeo Yan return;
161a26105ddSLeo Yan }
162a26105ddSLeo Yan }
163a26105ddSLeo Yan
16462759896SAlex Bennée /*
16562759896SAlex Bennée * When the daemon signals an update to the config we just need to
16662759896SAlex Bennée * signal the guest as we re-read the config on demand above.
16762759896SAlex Bennée */
vub_config_notifier(struct vhost_dev * dev)16862759896SAlex Bennée static int vub_config_notifier(struct vhost_dev *dev)
16962759896SAlex Bennée {
17062759896SAlex Bennée virtio_notify_config(dev->vdev);
17162759896SAlex Bennée return 0;
17262759896SAlex Bennée }
17362759896SAlex Bennée
17462759896SAlex Bennée const VhostDevConfigOps vub_config_ops = {
17562759896SAlex Bennée .vhost_dev_config_notifier = vub_config_notifier,
17662759896SAlex Bennée };
17762759896SAlex Bennée
vub_handle_output(VirtIODevice * vdev,VirtQueue * vq)17862759896SAlex Bennée static void vub_handle_output(VirtIODevice *vdev, VirtQueue *vq)
17962759896SAlex Bennée {
18062759896SAlex Bennée /*
18162759896SAlex Bennée * Not normally called; it's the daemon that handles the queue;
18262759896SAlex Bennée * however virtio's cleanup path can call this.
18362759896SAlex Bennée */
18462759896SAlex Bennée }
18562759896SAlex Bennée
do_vhost_user_cleanup(VirtIODevice * vdev,VHostUserBase * vub)18662759896SAlex Bennée static void do_vhost_user_cleanup(VirtIODevice *vdev, VHostUserBase *vub)
18762759896SAlex Bennée {
18862759896SAlex Bennée vhost_user_cleanup(&vub->vhost_user);
18962759896SAlex Bennée
19062759896SAlex Bennée for (int i = 0; i < vub->num_vqs; i++) {
19162759896SAlex Bennée VirtQueue *vq = g_ptr_array_index(vub->vqs, i);
19262759896SAlex Bennée virtio_delete_queue(vq);
19362759896SAlex Bennée }
19462759896SAlex Bennée
19562759896SAlex Bennée virtio_cleanup(vdev);
19662759896SAlex Bennée }
19762759896SAlex Bennée
vub_connect(DeviceState * dev)19862759896SAlex Bennée static int vub_connect(DeviceState *dev)
19962759896SAlex Bennée {
20062759896SAlex Bennée VirtIODevice *vdev = VIRTIO_DEVICE(dev);
20162759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
20262759896SAlex Bennée struct vhost_dev *vhost_dev = &vub->vhost_dev;
20362759896SAlex Bennée
20462759896SAlex Bennée if (vub->connected) {
20562759896SAlex Bennée return 0;
20662759896SAlex Bennée }
20762759896SAlex Bennée vub->connected = true;
20862759896SAlex Bennée
20962759896SAlex Bennée /*
21062759896SAlex Bennée * If we support VHOST_USER_GET_CONFIG we must enable the notifier
21162759896SAlex Bennée * so we can ping the guest when it updates.
21262759896SAlex Bennée */
21362759896SAlex Bennée if (vub->vhost_user.supports_config) {
21462759896SAlex Bennée vhost_dev_set_config_notifier(vhost_dev, &vub_config_ops);
21562759896SAlex Bennée }
21662759896SAlex Bennée
21762759896SAlex Bennée /* restore vhost state */
21862759896SAlex Bennée if (virtio_device_started(vdev, vdev->status)) {
21962759896SAlex Bennée vub_start(vdev);
22062759896SAlex Bennée }
22162759896SAlex Bennée
22262759896SAlex Bennée return 0;
22362759896SAlex Bennée }
22462759896SAlex Bennée
22564a312a2SAlex Bennée static void vub_event(void *opaque, QEMUChrEvent event);
22664a312a2SAlex Bennée
vub_disconnect(DeviceState * dev)22762759896SAlex Bennée static void vub_disconnect(DeviceState *dev)
22862759896SAlex Bennée {
22962759896SAlex Bennée VirtIODevice *vdev = VIRTIO_DEVICE(dev);
23062759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
23125b8a0f4SAkihiko Odaki struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
23262759896SAlex Bennée
23362759896SAlex Bennée if (!vub->connected) {
2346eaf0e61SLi Feng goto done;
23562759896SAlex Bennée }
23662759896SAlex Bennée vub->connected = false;
23762759896SAlex Bennée
23862759896SAlex Bennée vub_stop(vdev);
23964a312a2SAlex Bennée vhost_dev_cleanup(&vub->vhost_dev);
24025b8a0f4SAkihiko Odaki g_free(vhost_vqs);
24164a312a2SAlex Bennée
2426eaf0e61SLi Feng done:
24364a312a2SAlex Bennée /* Re-instate the event handler for new connections */
24464a312a2SAlex Bennée qemu_chr_fe_set_handlers(&vub->chardev,
24564a312a2SAlex Bennée NULL, NULL, vub_event,
24664a312a2SAlex Bennée NULL, dev, NULL, true);
24762759896SAlex Bennée }
24862759896SAlex Bennée
vub_event(void * opaque,QEMUChrEvent event)24962759896SAlex Bennée static void vub_event(void *opaque, QEMUChrEvent event)
25062759896SAlex Bennée {
25162759896SAlex Bennée DeviceState *dev = opaque;
25262759896SAlex Bennée VirtIODevice *vdev = VIRTIO_DEVICE(dev);
25362759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(vdev);
25462759896SAlex Bennée
25562759896SAlex Bennée switch (event) {
25662759896SAlex Bennée case CHR_EVENT_OPENED:
25762759896SAlex Bennée if (vub_connect(dev) < 0) {
25862759896SAlex Bennée qemu_chr_fe_disconnect(&vub->chardev);
25962759896SAlex Bennée return;
26062759896SAlex Bennée }
26162759896SAlex Bennée break;
26262759896SAlex Bennée case CHR_EVENT_CLOSED:
26364a312a2SAlex Bennée /* defer close until later to avoid circular close */
26464a312a2SAlex Bennée vhost_user_async_close(dev, &vub->chardev, &vub->vhost_dev,
2659569fe0aSLi Feng vub_disconnect);
26662759896SAlex Bennée break;
26762759896SAlex Bennée case CHR_EVENT_BREAK:
26862759896SAlex Bennée case CHR_EVENT_MUX_IN:
26962759896SAlex Bennée case CHR_EVENT_MUX_OUT:
27062759896SAlex Bennée /* Ignore */
27162759896SAlex Bennée break;
27262759896SAlex Bennée }
27362759896SAlex Bennée }
27462759896SAlex Bennée
vub_device_realize(DeviceState * dev,Error ** errp)27562759896SAlex Bennée static void vub_device_realize(DeviceState *dev, Error **errp)
27662759896SAlex Bennée {
27762759896SAlex Bennée VirtIODevice *vdev = VIRTIO_DEVICE(dev);
27862759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(dev);
27962759896SAlex Bennée int ret;
28062759896SAlex Bennée
28162759896SAlex Bennée if (!vub->chardev.chr) {
28262759896SAlex Bennée error_setg(errp, "vhost-user-base: missing chardev");
28362759896SAlex Bennée return;
28462759896SAlex Bennée }
28562759896SAlex Bennée
28662759896SAlex Bennée if (!vub->virtio_id) {
28762759896SAlex Bennée error_setg(errp, "vhost-user-base: need to define device id");
28862759896SAlex Bennée return;
28962759896SAlex Bennée }
29062759896SAlex Bennée
29162759896SAlex Bennée if (!vub->num_vqs) {
29262759896SAlex Bennée vub->num_vqs = 1; /* reasonable default? */
29362759896SAlex Bennée }
29462759896SAlex Bennée
29562759896SAlex Bennée if (!vub->vq_size) {
29662759896SAlex Bennée vub->vq_size = 64;
29762759896SAlex Bennée }
29862759896SAlex Bennée
29962759896SAlex Bennée /*
30062759896SAlex Bennée * We can't handle config requests unless we know the size of the
30162759896SAlex Bennée * config region, specialisations of the vhost-user-base will be
30262759896SAlex Bennée * able to set this.
30362759896SAlex Bennée */
30462759896SAlex Bennée if (vub->config_size) {
30562759896SAlex Bennée vub->vhost_user.supports_config = true;
30662759896SAlex Bennée }
30762759896SAlex Bennée
30862759896SAlex Bennée if (!vhost_user_init(&vub->vhost_user, &vub->chardev, errp)) {
30962759896SAlex Bennée return;
31062759896SAlex Bennée }
31162759896SAlex Bennée
31262759896SAlex Bennée virtio_init(vdev, vub->virtio_id, vub->config_size);
31362759896SAlex Bennée
31462759896SAlex Bennée /*
31562759896SAlex Bennée * Disable guest notifiers, by default all notifications will be via the
31662759896SAlex Bennée * asynchronous vhost-user socket.
31762759896SAlex Bennée */
31862759896SAlex Bennée vdev->use_guest_notifier_mask = false;
31962759896SAlex Bennée
32062759896SAlex Bennée /* Allocate queues */
32162759896SAlex Bennée vub->vqs = g_ptr_array_sized_new(vub->num_vqs);
32262759896SAlex Bennée for (int i = 0; i < vub->num_vqs; i++) {
32362759896SAlex Bennée g_ptr_array_add(vub->vqs,
32462759896SAlex Bennée virtio_add_queue(vdev, vub->vq_size,
32562759896SAlex Bennée vub_handle_output));
32662759896SAlex Bennée }
32762759896SAlex Bennée
32862759896SAlex Bennée vub->vhost_dev.nvqs = vub->num_vqs;
32962759896SAlex Bennée vub->vhost_dev.vqs = g_new0(struct vhost_virtqueue, vub->vhost_dev.nvqs);
33062759896SAlex Bennée
33162759896SAlex Bennée /* connect to backend */
33262759896SAlex Bennée ret = vhost_dev_init(&vub->vhost_dev, &vub->vhost_user,
33362759896SAlex Bennée VHOST_BACKEND_TYPE_USER, 0, errp);
33462759896SAlex Bennée
33562759896SAlex Bennée if (ret < 0) {
33662759896SAlex Bennée do_vhost_user_cleanup(vdev, vub);
33762759896SAlex Bennée }
33862759896SAlex Bennée
33962759896SAlex Bennée qemu_chr_fe_set_handlers(&vub->chardev, NULL, NULL, vub_event, NULL,
34062759896SAlex Bennée dev, NULL, true);
34162759896SAlex Bennée }
34262759896SAlex Bennée
vub_device_unrealize(DeviceState * dev)34362759896SAlex Bennée static void vub_device_unrealize(DeviceState *dev)
34462759896SAlex Bennée {
34562759896SAlex Bennée VirtIODevice *vdev = VIRTIO_DEVICE(dev);
34662759896SAlex Bennée VHostUserBase *vub = VHOST_USER_BASE(dev);
34762759896SAlex Bennée struct vhost_virtqueue *vhost_vqs = vub->vhost_dev.vqs;
34862759896SAlex Bennée
34962759896SAlex Bennée /* This will stop vhost backend if appropriate. */
35062759896SAlex Bennée vub_set_status(vdev, 0);
35162759896SAlex Bennée vhost_dev_cleanup(&vub->vhost_dev);
35262759896SAlex Bennée g_free(vhost_vqs);
35362759896SAlex Bennée do_vhost_user_cleanup(vdev, vub);
35462759896SAlex Bennée }
35562759896SAlex Bennée
vub_class_init(ObjectClass * klass,const void * data)35612d1a768SPhilippe Mathieu-Daudé static void vub_class_init(ObjectClass *klass, const void *data)
35762759896SAlex Bennée {
35862759896SAlex Bennée VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass);
35962759896SAlex Bennée
36062759896SAlex Bennée vdc->realize = vub_device_realize;
36162759896SAlex Bennée vdc->unrealize = vub_device_unrealize;
36262759896SAlex Bennée vdc->get_features = vub_get_features;
36362759896SAlex Bennée vdc->get_config = vub_get_config;
364a26105ddSLeo Yan vdc->set_config = vub_set_config;
36562759896SAlex Bennée vdc->set_status = vub_set_status;
36662759896SAlex Bennée }
36762759896SAlex Bennée
36862759896SAlex Bennée static const TypeInfo vub_types[] = {
36962759896SAlex Bennée {
37062759896SAlex Bennée .name = TYPE_VHOST_USER_BASE,
37162759896SAlex Bennée .parent = TYPE_VIRTIO_DEVICE,
37262759896SAlex Bennée .instance_size = sizeof(VHostUserBase),
37362759896SAlex Bennée .class_init = vub_class_init,
37462759896SAlex Bennée .class_size = sizeof(VHostUserBaseClass),
37562759896SAlex Bennée .abstract = true
37662759896SAlex Bennée }
37762759896SAlex Bennée };
37862759896SAlex Bennée
37962759896SAlex Bennée DEFINE_TYPES(vub_types)
380