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