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