1 /* 2 * Vhost Vdpa Device 3 * 4 * Copyright (c) Huawei Technologies Co., Ltd. 2022. All Rights Reserved. 5 * 6 * Authors: 7 * Longpeng <longpeng2@huawei.com> 8 * 9 * Largely based on the "vhost-user-blk-pci.c" and "vhost-user-blk.c" 10 * implemented by: 11 * Changpeng Liu <changpeng.liu@intel.com> 12 * 13 * This work is licensed under the terms of the GNU LGPL, version 2 or later. 14 * See the COPYING.LIB file in the top-level directory. 15 */ 16 #include "qemu/osdep.h" 17 #include <sys/ioctl.h> 18 #include <linux/vhost.h> 19 #include "qapi/error.h" 20 #include "qemu/error-report.h" 21 #include "qemu/cutils.h" 22 #include "hw/qdev-core.h" 23 #include "hw/qdev-properties.h" 24 #include "hw/qdev-properties-system.h" 25 #include "hw/virtio/vhost.h" 26 #include "hw/virtio/virtio.h" 27 #include "hw/virtio/virtio-bus.h" 28 #include "hw/virtio/virtio-access.h" 29 #include "hw/virtio/vdpa-dev.h" 30 #include "sysemu/sysemu.h" 31 #include "sysemu/runstate.h" 32 33 static void 34 vhost_vdpa_device_dummy_handle_output(VirtIODevice *vdev, VirtQueue *vq) 35 { 36 /* Nothing to do */ 37 } 38 39 static uint32_t 40 vhost_vdpa_device_get_u32(int fd, unsigned long int cmd, Error **errp) 41 { 42 uint32_t val = (uint32_t)-1; 43 44 if (ioctl(fd, cmd, &val) < 0) { 45 error_setg(errp, "vhost-vdpa-device: cmd 0x%lx failed: %s", 46 cmd, strerror(errno)); 47 } 48 49 return val; 50 } 51 52 static void vhost_vdpa_device_realize(DeviceState *dev, Error **errp) 53 { 54 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 55 VhostVdpaDevice *v = VHOST_VDPA_DEVICE(vdev); 56 struct vhost_vdpa_iova_range iova_range; 57 uint16_t max_queue_size; 58 struct vhost_virtqueue *vqs; 59 int i, ret; 60 61 if (!v->vhostdev) { 62 error_setg(errp, "vhost-vdpa-device: vhostdev are missing"); 63 return; 64 } 65 66 v->vhostfd = qemu_open(v->vhostdev, O_RDWR, errp); 67 if (*errp) { 68 return; 69 } 70 v->vdpa.device_fd = v->vhostfd; 71 72 v->vdev_id = vhost_vdpa_device_get_u32(v->vhostfd, 73 VHOST_VDPA_GET_DEVICE_ID, errp); 74 if (*errp) { 75 goto out; 76 } 77 78 max_queue_size = vhost_vdpa_device_get_u32(v->vhostfd, 79 VHOST_VDPA_GET_VRING_NUM, errp); 80 if (*errp) { 81 goto out; 82 } 83 84 if (v->queue_size > max_queue_size) { 85 error_setg(errp, "vhost-vdpa-device: invalid queue_size: %u (max:%u)", 86 v->queue_size, max_queue_size); 87 goto out; 88 } else if (!v->queue_size) { 89 v->queue_size = max_queue_size; 90 } 91 92 v->num_queues = vhost_vdpa_device_get_u32(v->vhostfd, 93 VHOST_VDPA_GET_VQS_COUNT, errp); 94 if (*errp) { 95 goto out; 96 } 97 98 if (!v->num_queues || v->num_queues > VIRTIO_QUEUE_MAX) { 99 error_setg(errp, "invalid number of virtqueues: %u (max:%u)", 100 v->num_queues, VIRTIO_QUEUE_MAX); 101 goto out; 102 } 103 104 v->dev.nvqs = v->num_queues; 105 vqs = g_new0(struct vhost_virtqueue, v->dev.nvqs); 106 v->dev.vqs = vqs; 107 v->dev.vq_index = 0; 108 v->dev.vq_index_end = v->dev.nvqs; 109 v->dev.backend_features = 0; 110 v->started = false; 111 112 ret = vhost_vdpa_get_iova_range(v->vhostfd, &iova_range); 113 if (ret < 0) { 114 error_setg(errp, "vhost-vdpa-device: get iova range failed: %s", 115 strerror(-ret)); 116 goto free_vqs; 117 } 118 v->vdpa.iova_range = iova_range; 119 120 ret = vhost_dev_init(&v->dev, &v->vdpa, VHOST_BACKEND_TYPE_VDPA, 0, NULL); 121 if (ret < 0) { 122 error_setg(errp, "vhost-vdpa-device: vhost initialization failed: %s", 123 strerror(-ret)); 124 goto free_vqs; 125 } 126 127 v->config_size = vhost_vdpa_device_get_u32(v->vhostfd, 128 VHOST_VDPA_GET_CONFIG_SIZE, 129 errp); 130 if (*errp) { 131 goto vhost_cleanup; 132 } 133 134 /* 135 * Invoke .post_init() to initialize the transport-specific fields 136 * before calling virtio_init(). 137 */ 138 if (v->post_init && v->post_init(v, errp) < 0) { 139 goto vhost_cleanup; 140 } 141 142 v->config = g_malloc0(v->config_size); 143 144 ret = vhost_dev_get_config(&v->dev, v->config, v->config_size, NULL); 145 if (ret < 0) { 146 error_setg(errp, "vhost-vdpa-device: get config failed"); 147 goto free_config; 148 } 149 150 virtio_init(vdev, v->vdev_id, v->config_size); 151 152 v->virtqs = g_new0(VirtQueue *, v->dev.nvqs); 153 for (i = 0; i < v->dev.nvqs; i++) { 154 v->virtqs[i] = virtio_add_queue(vdev, v->queue_size, 155 vhost_vdpa_device_dummy_handle_output); 156 } 157 158 return; 159 160 free_config: 161 g_free(v->config); 162 vhost_cleanup: 163 vhost_dev_cleanup(&v->dev); 164 free_vqs: 165 g_free(vqs); 166 out: 167 qemu_close(v->vhostfd); 168 v->vhostfd = -1; 169 } 170 171 static void vhost_vdpa_device_unrealize(DeviceState *dev) 172 { 173 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 174 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 175 int i; 176 177 virtio_set_status(vdev, 0); 178 179 for (i = 0; i < s->num_queues; i++) { 180 virtio_delete_queue(s->virtqs[i]); 181 } 182 g_free(s->virtqs); 183 virtio_cleanup(vdev); 184 185 g_free(s->config); 186 g_free(s->dev.vqs); 187 vhost_dev_cleanup(&s->dev); 188 qemu_close(s->vhostfd); 189 s->vhostfd = -1; 190 } 191 192 static void 193 vhost_vdpa_device_get_config(VirtIODevice *vdev, uint8_t *config) 194 { 195 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 196 197 memcpy(config, s->config, s->config_size); 198 } 199 200 static void 201 vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) 202 { 203 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 204 int ret; 205 206 ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, 207 VHOST_SET_CONFIG_TYPE_MASTER); 208 if (ret) { 209 error_report("set device config space failed"); 210 return; 211 } 212 } 213 214 static uint64_t vhost_vdpa_device_get_features(VirtIODevice *vdev, 215 uint64_t features, 216 Error **errp) 217 { 218 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 219 uint64_t backend_features = s->dev.features; 220 221 if (!virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM)) { 222 virtio_clear_feature(&backend_features, VIRTIO_F_IOMMU_PLATFORM); 223 } 224 225 return backend_features; 226 } 227 228 static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp) 229 { 230 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 231 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 232 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 233 int i, ret; 234 235 if (!k->set_guest_notifiers) { 236 error_setg(errp, "binding does not support guest notifiers"); 237 return -ENOSYS; 238 } 239 240 ret = vhost_dev_enable_notifiers(&s->dev, vdev); 241 if (ret < 0) { 242 error_setg_errno(errp, -ret, "Error enabling host notifiers"); 243 return ret; 244 } 245 246 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); 247 if (ret < 0) { 248 error_setg_errno(errp, -ret, "Error binding guest notifier"); 249 goto err_host_notifiers; 250 } 251 252 s->dev.acked_features = vdev->guest_features; 253 254 ret = vhost_dev_start(&s->dev, vdev, false); 255 if (ret < 0) { 256 error_setg_errno(errp, -ret, "Error starting vhost"); 257 goto err_guest_notifiers; 258 } 259 s->started = true; 260 261 /* 262 * guest_notifier_mask/pending not used yet, so just unmask 263 * everything here. virtio-pci will do the right thing by 264 * enabling/disabling irqfd. 265 */ 266 for (i = 0; i < s->dev.nvqs; i++) { 267 vhost_virtqueue_mask(&s->dev, vdev, i, false); 268 } 269 270 return ret; 271 272 err_guest_notifiers: 273 k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 274 err_host_notifiers: 275 vhost_dev_disable_notifiers(&s->dev, vdev); 276 return ret; 277 } 278 279 static void vhost_vdpa_device_stop(VirtIODevice *vdev) 280 { 281 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 282 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 283 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 284 int ret; 285 286 if (!s->started) { 287 return; 288 } 289 s->started = false; 290 291 if (!k->set_guest_notifiers) { 292 return; 293 } 294 295 vhost_dev_stop(&s->dev, vdev, false); 296 297 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 298 if (ret < 0) { 299 error_report("vhost guest notifier cleanup failed: %d", ret); 300 return; 301 } 302 303 vhost_dev_disable_notifiers(&s->dev, vdev); 304 } 305 306 static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) 307 { 308 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 309 bool should_start = virtio_device_started(vdev, status); 310 Error *local_err = NULL; 311 int ret; 312 313 if (!vdev->vm_running) { 314 should_start = false; 315 } 316 317 if (s->started == should_start) { 318 return; 319 } 320 321 if (should_start) { 322 ret = vhost_vdpa_device_start(vdev, &local_err); 323 if (ret < 0) { 324 error_reportf_err(local_err, "vhost-vdpa-device: start failed: "); 325 } 326 } else { 327 vhost_vdpa_device_stop(vdev); 328 } 329 } 330 331 static Property vhost_vdpa_device_properties[] = { 332 DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), 333 DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), 334 DEFINE_PROP_END_OF_LIST(), 335 }; 336 337 static const VMStateDescription vmstate_vhost_vdpa_device = { 338 .name = "vhost-vdpa-device", 339 .unmigratable = 1, 340 .minimum_version_id = 1, 341 .version_id = 1, 342 .fields = (VMStateField[]) { 343 VMSTATE_VIRTIO_DEVICE, 344 VMSTATE_END_OF_LIST() 345 }, 346 }; 347 348 static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) 349 { 350 DeviceClass *dc = DEVICE_CLASS(klass); 351 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 352 353 device_class_set_props(dc, vhost_vdpa_device_properties); 354 dc->desc = "VDPA-based generic device assignment"; 355 dc->vmsd = &vmstate_vhost_vdpa_device; 356 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 357 vdc->realize = vhost_vdpa_device_realize; 358 vdc->unrealize = vhost_vdpa_device_unrealize; 359 vdc->get_config = vhost_vdpa_device_get_config; 360 vdc->set_config = vhost_vdpa_device_set_config; 361 vdc->get_features = vhost_vdpa_device_get_features; 362 vdc->set_status = vhost_vdpa_device_set_status; 363 } 364 365 static void vhost_vdpa_device_instance_init(Object *obj) 366 { 367 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(obj); 368 369 device_add_bootindex_property(obj, &s->bootindex, "bootindex", 370 NULL, DEVICE(obj)); 371 } 372 373 static const TypeInfo vhost_vdpa_device_info = { 374 .name = TYPE_VHOST_VDPA_DEVICE, 375 .parent = TYPE_VIRTIO_DEVICE, 376 .instance_size = sizeof(VhostVdpaDevice), 377 .class_init = vhost_vdpa_device_class_init, 378 .instance_init = vhost_vdpa_device_instance_init, 379 }; 380 381 static void register_vhost_vdpa_device_type(void) 382 { 383 type_register_static(&vhost_vdpa_device_info); 384 } 385 386 type_init(register_vhost_vdpa_device_type); 387