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