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 uint16_t max_queue_size; 57 struct vhost_virtqueue *vqs; 58 int i, ret; 59 60 if (!v->vhostdev) { 61 error_setg(errp, "vhost-vdpa-device: vhostdev are missing"); 62 return; 63 } 64 65 v->vhostfd = qemu_open(v->vhostdev, O_RDWR, errp); 66 if (*errp) { 67 return; 68 } 69 v->vdpa.device_fd = v->vhostfd; 70 71 v->vdev_id = vhost_vdpa_device_get_u32(v->vhostfd, 72 VHOST_VDPA_GET_DEVICE_ID, errp); 73 if (*errp) { 74 goto out; 75 } 76 77 max_queue_size = vhost_vdpa_device_get_u32(v->vhostfd, 78 VHOST_VDPA_GET_VRING_NUM, errp); 79 if (*errp) { 80 goto out; 81 } 82 83 if (v->queue_size > max_queue_size) { 84 error_setg(errp, "vhost-vdpa-device: invalid queue_size: %u (max:%u)", 85 v->queue_size, max_queue_size); 86 goto out; 87 } else if (!v->queue_size) { 88 v->queue_size = max_queue_size; 89 } 90 91 v->num_queues = vhost_vdpa_device_get_u32(v->vhostfd, 92 VHOST_VDPA_GET_VQS_COUNT, errp); 93 if (*errp) { 94 goto out; 95 } 96 97 if (!v->num_queues || v->num_queues > VIRTIO_QUEUE_MAX) { 98 error_setg(errp, "invalid number of virtqueues: %u (max:%u)", 99 v->num_queues, VIRTIO_QUEUE_MAX); 100 goto out; 101 } 102 103 v->dev.nvqs = v->num_queues; 104 vqs = g_new0(struct vhost_virtqueue, v->dev.nvqs); 105 v->dev.vqs = vqs; 106 v->dev.vq_index = 0; 107 v->dev.vq_index_end = v->dev.nvqs; 108 v->dev.backend_features = 0; 109 v->started = false; 110 111 ret = vhost_dev_init(&v->dev, &v->vdpa, VHOST_BACKEND_TYPE_VDPA, 0, NULL); 112 if (ret < 0) { 113 error_setg(errp, "vhost-vdpa-device: vhost initialization failed: %s", 114 strerror(-ret)); 115 goto free_vqs; 116 } 117 118 v->config_size = vhost_vdpa_device_get_u32(v->vhostfd, 119 VHOST_VDPA_GET_CONFIG_SIZE, 120 errp); 121 if (*errp) { 122 goto vhost_cleanup; 123 } 124 125 /* 126 * Invoke .post_init() to initialize the transport-specific fields 127 * before calling virtio_init(). 128 */ 129 if (v->post_init && v->post_init(v, errp) < 0) { 130 goto vhost_cleanup; 131 } 132 133 v->config = g_malloc0(v->config_size); 134 135 ret = vhost_dev_get_config(&v->dev, v->config, v->config_size, NULL); 136 if (ret < 0) { 137 error_setg(errp, "vhost-vdpa-device: get config failed"); 138 goto free_config; 139 } 140 141 virtio_init(vdev, v->vdev_id, v->config_size); 142 143 v->virtqs = g_new0(VirtQueue *, v->dev.nvqs); 144 for (i = 0; i < v->dev.nvqs; i++) { 145 v->virtqs[i] = virtio_add_queue(vdev, v->queue_size, 146 vhost_vdpa_device_dummy_handle_output); 147 } 148 149 return; 150 151 free_config: 152 g_free(v->config); 153 vhost_cleanup: 154 vhost_dev_cleanup(&v->dev); 155 free_vqs: 156 g_free(vqs); 157 out: 158 qemu_close(v->vhostfd); 159 v->vhostfd = -1; 160 } 161 162 static void vhost_vdpa_device_unrealize(DeviceState *dev) 163 { 164 VirtIODevice *vdev = VIRTIO_DEVICE(dev); 165 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 166 int i; 167 168 virtio_set_status(vdev, 0); 169 170 for (i = 0; i < s->num_queues; i++) { 171 virtio_delete_queue(s->virtqs[i]); 172 } 173 g_free(s->virtqs); 174 virtio_cleanup(vdev); 175 176 g_free(s->config); 177 g_free(s->dev.vqs); 178 vhost_dev_cleanup(&s->dev); 179 qemu_close(s->vhostfd); 180 s->vhostfd = -1; 181 } 182 183 static void 184 vhost_vdpa_device_get_config(VirtIODevice *vdev, uint8_t *config) 185 { 186 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 187 188 memcpy(config, s->config, s->config_size); 189 } 190 191 static void 192 vhost_vdpa_device_set_config(VirtIODevice *vdev, const uint8_t *config) 193 { 194 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 195 int ret; 196 197 ret = vhost_dev_set_config(&s->dev, s->config, 0, s->config_size, 198 VHOST_SET_CONFIG_TYPE_MASTER); 199 if (ret) { 200 error_report("set device config space failed"); 201 return; 202 } 203 } 204 205 static uint64_t vhost_vdpa_device_get_features(VirtIODevice *vdev, 206 uint64_t features, 207 Error **errp) 208 { 209 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 210 uint64_t backend_features = s->dev.features; 211 212 if (!virtio_has_feature(features, VIRTIO_F_IOMMU_PLATFORM)) { 213 virtio_clear_feature(&backend_features, VIRTIO_F_IOMMU_PLATFORM); 214 } 215 216 return backend_features; 217 } 218 219 static int vhost_vdpa_device_start(VirtIODevice *vdev, Error **errp) 220 { 221 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 222 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 223 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 224 int i, ret; 225 226 if (!k->set_guest_notifiers) { 227 error_setg(errp, "binding does not support guest notifiers"); 228 return -ENOSYS; 229 } 230 231 ret = vhost_dev_enable_notifiers(&s->dev, vdev); 232 if (ret < 0) { 233 error_setg_errno(errp, -ret, "Error enabling host notifiers"); 234 return ret; 235 } 236 237 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, true); 238 if (ret < 0) { 239 error_setg_errno(errp, -ret, "Error binding guest notifier"); 240 goto err_host_notifiers; 241 } 242 243 s->dev.acked_features = vdev->guest_features; 244 245 ret = vhost_dev_start(&s->dev, vdev, false); 246 if (ret < 0) { 247 error_setg_errno(errp, -ret, "Error starting vhost"); 248 goto err_guest_notifiers; 249 } 250 s->started = true; 251 252 /* 253 * guest_notifier_mask/pending not used yet, so just unmask 254 * everything here. virtio-pci will do the right thing by 255 * enabling/disabling irqfd. 256 */ 257 for (i = 0; i < s->dev.nvqs; i++) { 258 vhost_virtqueue_mask(&s->dev, vdev, i, false); 259 } 260 261 return ret; 262 263 err_guest_notifiers: 264 k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 265 err_host_notifiers: 266 vhost_dev_disable_notifiers(&s->dev, vdev); 267 return ret; 268 } 269 270 static void vhost_vdpa_device_stop(VirtIODevice *vdev) 271 { 272 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 273 BusState *qbus = BUS(qdev_get_parent_bus(DEVICE(vdev))); 274 VirtioBusClass *k = VIRTIO_BUS_GET_CLASS(qbus); 275 int ret; 276 277 if (!s->started) { 278 return; 279 } 280 s->started = false; 281 282 if (!k->set_guest_notifiers) { 283 return; 284 } 285 286 vhost_dev_stop(&s->dev, vdev, false); 287 288 ret = k->set_guest_notifiers(qbus->parent, s->dev.nvqs, false); 289 if (ret < 0) { 290 error_report("vhost guest notifier cleanup failed: %d", ret); 291 return; 292 } 293 294 vhost_dev_disable_notifiers(&s->dev, vdev); 295 } 296 297 static void vhost_vdpa_device_set_status(VirtIODevice *vdev, uint8_t status) 298 { 299 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(vdev); 300 bool should_start = virtio_device_started(vdev, status); 301 Error *local_err = NULL; 302 int ret; 303 304 if (!vdev->vm_running) { 305 should_start = false; 306 } 307 308 if (s->started == should_start) { 309 return; 310 } 311 312 if (should_start) { 313 ret = vhost_vdpa_device_start(vdev, &local_err); 314 if (ret < 0) { 315 error_reportf_err(local_err, "vhost-vdpa-device: start failed: "); 316 } 317 } else { 318 vhost_vdpa_device_stop(vdev); 319 } 320 } 321 322 static Property vhost_vdpa_device_properties[] = { 323 DEFINE_PROP_STRING("vhostdev", VhostVdpaDevice, vhostdev), 324 DEFINE_PROP_UINT16("queue-size", VhostVdpaDevice, queue_size, 0), 325 DEFINE_PROP_END_OF_LIST(), 326 }; 327 328 static const VMStateDescription vmstate_vhost_vdpa_device = { 329 .name = "vhost-vdpa-device", 330 .unmigratable = 1, 331 .minimum_version_id = 1, 332 .version_id = 1, 333 .fields = (VMStateField[]) { 334 VMSTATE_VIRTIO_DEVICE, 335 VMSTATE_END_OF_LIST() 336 }, 337 }; 338 339 static void vhost_vdpa_device_class_init(ObjectClass *klass, void *data) 340 { 341 DeviceClass *dc = DEVICE_CLASS(klass); 342 VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); 343 344 device_class_set_props(dc, vhost_vdpa_device_properties); 345 dc->desc = "VDPA-based generic device assignment"; 346 dc->vmsd = &vmstate_vhost_vdpa_device; 347 set_bit(DEVICE_CATEGORY_MISC, dc->categories); 348 vdc->realize = vhost_vdpa_device_realize; 349 vdc->unrealize = vhost_vdpa_device_unrealize; 350 vdc->get_config = vhost_vdpa_device_get_config; 351 vdc->set_config = vhost_vdpa_device_set_config; 352 vdc->get_features = vhost_vdpa_device_get_features; 353 vdc->set_status = vhost_vdpa_device_set_status; 354 } 355 356 static void vhost_vdpa_device_instance_init(Object *obj) 357 { 358 VhostVdpaDevice *s = VHOST_VDPA_DEVICE(obj); 359 360 device_add_bootindex_property(obj, &s->bootindex, "bootindex", 361 NULL, DEVICE(obj)); 362 } 363 364 static const TypeInfo vhost_vdpa_device_info = { 365 .name = TYPE_VHOST_VDPA_DEVICE, 366 .parent = TYPE_VIRTIO_DEVICE, 367 .instance_size = sizeof(VhostVdpaDevice), 368 .class_init = vhost_vdpa_device_class_init, 369 .instance_init = vhost_vdpa_device_instance_init, 370 }; 371 372 static void register_vhost_vdpa_device_type(void) 373 { 374 type_register_static(&vhost_vdpa_device_info); 375 } 376 377 type_init(register_vhost_vdpa_device_type); 378