10adb8eccSMaxime Coquelin /* SPDX-License-Identifier: BSD-3-Clause 20adb8eccSMaxime Coquelin * Copyright (c) 2023 Red Hat, Inc. 30adb8eccSMaxime Coquelin */ 40adb8eccSMaxime Coquelin 50adb8eccSMaxime Coquelin #include <stdint.h> 60adb8eccSMaxime Coquelin #include <stdio.h> 70adb8eccSMaxime Coquelin #include <unistd.h> 80adb8eccSMaxime Coquelin #include <fcntl.h> 90adb8eccSMaxime Coquelin 100adb8eccSMaxime Coquelin 11*4025e36fSStephen Hemminger #include <linux/vduse.h> 120adb8eccSMaxime Coquelin #include <linux/virtio_net.h> 130adb8eccSMaxime Coquelin 140adb8eccSMaxime Coquelin #include <sys/ioctl.h> 150adb8eccSMaxime Coquelin #include <sys/mman.h> 16f27d5206SMaxime Coquelin #include <sys/stat.h> 170adb8eccSMaxime Coquelin 180adb8eccSMaxime Coquelin #include <rte_common.h> 191c1abf17SThomas Monjalon #include <rte_thread.h> 200adb8eccSMaxime Coquelin 2151d018fdSMaxime Coquelin #include "fd_man.h" 22f27d5206SMaxime Coquelin #include "iotlb.h" 230adb8eccSMaxime Coquelin #include "vduse.h" 240adb8eccSMaxime Coquelin #include "vhost.h" 25653327e1SMaxime Coquelin #include "virtio_net_ctrl.h" 260adb8eccSMaxime Coquelin 270adb8eccSMaxime Coquelin #define VHOST_VDUSE_API_VERSION 0 280adb8eccSMaxime Coquelin #define VDUSE_CTRL_PATH "/dev/vduse/control" 290adb8eccSMaxime Coquelin 3051d018fdSMaxime Coquelin struct vduse { 31e68a6feaSMaxime Coquelin struct fdset *fdset; 3251d018fdSMaxime Coquelin }; 3351d018fdSMaxime Coquelin 347945769cSMaxime Coquelin static struct vduse vduse; 3551d018fdSMaxime Coquelin 3651d018fdSMaxime Coquelin static const char * const vduse_reqs_str[] = { 3751d018fdSMaxime Coquelin "VDUSE_GET_VQ_STATE", 3851d018fdSMaxime Coquelin "VDUSE_SET_STATUS", 3951d018fdSMaxime Coquelin "VDUSE_UPDATE_IOTLB", 4051d018fdSMaxime Coquelin }; 4151d018fdSMaxime Coquelin 4251d018fdSMaxime Coquelin #define vduse_req_id_to_str(id) \ 4351d018fdSMaxime Coquelin (id < RTE_DIM(vduse_reqs_str) ? \ 4451d018fdSMaxime Coquelin vduse_reqs_str[id] : "Unknown") 4551d018fdSMaxime Coquelin 46ab28f62cSMaxime Coquelin static int 47ab28f62cSMaxime Coquelin vduse_inject_irq(struct virtio_net *dev, struct vhost_virtqueue *vq) 48ab28f62cSMaxime Coquelin { 49ab28f62cSMaxime Coquelin return ioctl(dev->vduse_dev_fd, VDUSE_VQ_INJECT_IRQ, &vq->index); 50ab28f62cSMaxime Coquelin } 51ab28f62cSMaxime Coquelin 5273d8bbaaSMaxime Coquelin static void 5373d8bbaaSMaxime Coquelin vduse_iotlb_remove_notify(uint64_t addr, uint64_t offset, uint64_t size) 5473d8bbaaSMaxime Coquelin { 5573d8bbaaSMaxime Coquelin munmap((void *)(uintptr_t)addr, offset + size); 5673d8bbaaSMaxime Coquelin } 5773d8bbaaSMaxime Coquelin 58f27d5206SMaxime Coquelin static int 59f27d5206SMaxime Coquelin vduse_iotlb_miss(struct virtio_net *dev, uint64_t iova, uint8_t perm __rte_unused) 60f27d5206SMaxime Coquelin { 61f27d5206SMaxime Coquelin struct vduse_iotlb_entry entry; 62f27d5206SMaxime Coquelin uint64_t size, page_size; 63f27d5206SMaxime Coquelin struct stat stat; 64f27d5206SMaxime Coquelin void *mmap_addr; 65f27d5206SMaxime Coquelin int fd, ret; 66f27d5206SMaxime Coquelin 67f27d5206SMaxime Coquelin entry.start = iova; 68f27d5206SMaxime Coquelin entry.last = iova + 1; 69f27d5206SMaxime Coquelin 70f27d5206SMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_IOTLB_GET_FD, &entry); 71f27d5206SMaxime Coquelin if (ret < 0) { 720e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get IOTLB entry for 0x%" PRIx64, 73f27d5206SMaxime Coquelin iova); 74f27d5206SMaxime Coquelin return -1; 75f27d5206SMaxime Coquelin } 76f27d5206SMaxime Coquelin 77f27d5206SMaxime Coquelin fd = ret; 78f27d5206SMaxime Coquelin 790e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "New IOTLB entry:"); 800e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tIOVA: %" PRIx64 " - %" PRIx64, 81f27d5206SMaxime Coquelin (uint64_t)entry.start, (uint64_t)entry.last); 820e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\toffset: %" PRIx64, (uint64_t)entry.offset); 830e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tfd: %d", fd); 840e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "\tperm: %x", entry.perm); 85f27d5206SMaxime Coquelin 86f27d5206SMaxime Coquelin size = entry.last - entry.start + 1; 87f27d5206SMaxime Coquelin mmap_addr = mmap(0, size + entry.offset, entry.perm, MAP_SHARED, fd, 0); 88f27d5206SMaxime Coquelin if (!mmap_addr) { 890e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 900e21c7c0SDavid Marchand "Failed to mmap IOTLB entry for 0x%" PRIx64, iova); 91f27d5206SMaxime Coquelin ret = -1; 92f27d5206SMaxime Coquelin goto close_fd; 93f27d5206SMaxime Coquelin } 94f27d5206SMaxime Coquelin 95f27d5206SMaxime Coquelin ret = fstat(fd, &stat); 96f27d5206SMaxime Coquelin if (ret < 0) { 970e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get page size."); 98f27d5206SMaxime Coquelin munmap(mmap_addr, entry.offset + size); 99f27d5206SMaxime Coquelin goto close_fd; 100f27d5206SMaxime Coquelin } 101f27d5206SMaxime Coquelin page_size = (uint64_t)stat.st_blksize; 102f27d5206SMaxime Coquelin 103f27d5206SMaxime Coquelin vhost_user_iotlb_cache_insert(dev, entry.start, (uint64_t)(uintptr_t)mmap_addr, 104f27d5206SMaxime Coquelin entry.offset, size, page_size, entry.perm); 105f27d5206SMaxime Coquelin 106f27d5206SMaxime Coquelin ret = 0; 107f27d5206SMaxime Coquelin close_fd: 108f27d5206SMaxime Coquelin close(fd); 109f27d5206SMaxime Coquelin 110f27d5206SMaxime Coquelin return ret; 111f27d5206SMaxime Coquelin } 112f27d5206SMaxime Coquelin 1130adb8eccSMaxime Coquelin static struct vhost_backend_ops vduse_backend_ops = { 114f27d5206SMaxime Coquelin .iotlb_miss = vduse_iotlb_miss, 11573d8bbaaSMaxime Coquelin .iotlb_remove_notify = vduse_iotlb_remove_notify, 116ab28f62cSMaxime Coquelin .inject_irq = vduse_inject_irq, 1170adb8eccSMaxime Coquelin }; 1180adb8eccSMaxime Coquelin 11951d018fdSMaxime Coquelin static void 120653327e1SMaxime Coquelin vduse_control_queue_event(int fd, void *arg, int *remove __rte_unused) 121653327e1SMaxime Coquelin { 122653327e1SMaxime Coquelin struct virtio_net *dev = arg; 123653327e1SMaxime Coquelin uint64_t buf; 124653327e1SMaxime Coquelin int ret; 125653327e1SMaxime Coquelin 126653327e1SMaxime Coquelin ret = read(fd, &buf, sizeof(buf)); 127653327e1SMaxime Coquelin if (ret < 0) { 1280e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read control queue event: %s", 129653327e1SMaxime Coquelin strerror(errno)); 130653327e1SMaxime Coquelin return; 131653327e1SMaxime Coquelin } 132653327e1SMaxime Coquelin 1330e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, DEBUG, "Control queue kicked"); 134653327e1SMaxime Coquelin if (virtio_net_ctrl_handle(dev)) 1350e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to handle ctrl request"); 136653327e1SMaxime Coquelin } 137653327e1SMaxime Coquelin 138653327e1SMaxime Coquelin static void 139da79cc7fSMaxime Coquelin vduse_vring_setup(struct virtio_net *dev, unsigned int index, bool reconnect) 140a9120db8SMaxime Coquelin { 141a9120db8SMaxime Coquelin struct vhost_virtqueue *vq = dev->virtqueue[index]; 142a9120db8SMaxime Coquelin struct vhost_vring_addr *ra = &vq->ring_addrs; 143a9120db8SMaxime Coquelin struct vduse_vq_info vq_info; 144a9120db8SMaxime Coquelin struct vduse_vq_eventfd vq_efd; 145a9120db8SMaxime Coquelin int ret; 146a9120db8SMaxime Coquelin 147a9120db8SMaxime Coquelin vq_info.index = index; 148a9120db8SMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_GET_INFO, &vq_info); 149a9120db8SMaxime Coquelin if (ret) { 1500e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get VQ %u info: %s", 151a9120db8SMaxime Coquelin index, strerror(errno)); 152a9120db8SMaxime Coquelin return; 153a9120db8SMaxime Coquelin } 154a9120db8SMaxime Coquelin 155da79cc7fSMaxime Coquelin if (reconnect) { 156da79cc7fSMaxime Coquelin vq->last_avail_idx = vq->reconnect_log->last_avail_idx; 157da79cc7fSMaxime Coquelin vq->last_used_idx = vq->reconnect_log->last_avail_idx; 158da79cc7fSMaxime Coquelin } else { 159da79cc7fSMaxime Coquelin vq->last_avail_idx = vq_info.split.avail_index; 160da79cc7fSMaxime Coquelin vq->last_used_idx = vq_info.split.avail_index; 161da79cc7fSMaxime Coquelin } 162da79cc7fSMaxime Coquelin vq->size = vq_info.num; 163da79cc7fSMaxime Coquelin vq->ready = true; 164da79cc7fSMaxime Coquelin vq->enabled = vq_info.ready; 165da79cc7fSMaxime Coquelin ra->desc_user_addr = vq_info.desc_addr; 166da79cc7fSMaxime Coquelin ra->avail_user_addr = vq_info.driver_addr; 167da79cc7fSMaxime Coquelin ra->used_user_addr = vq_info.device_addr; 1680e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "VQ %u info:", index); 1690e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnum: %u", vq_info.num); 1700e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdesc_addr: %llx", 171b037cefbSBruce Richardson (unsigned long long)vq_info.desc_addr); 1720e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdriver_addr: %llx", 173b037cefbSBruce Richardson (unsigned long long)vq_info.driver_addr); 1740e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tdevice_addr: %llx", 175b037cefbSBruce Richardson (unsigned long long)vq_info.device_addr); 176da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, INFO, "\tavail_idx: %u", vq->last_avail_idx); 177da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, INFO, "\tused_idx: %u", vq->last_used_idx); 1780e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tready: %u", vq_info.ready); 179a9120db8SMaxime Coquelin vq->kickfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 180a9120db8SMaxime Coquelin if (vq->kickfd < 0) { 1810e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to init kickfd for VQ %u: %s", 182a9120db8SMaxime Coquelin index, strerror(errno)); 183a9120db8SMaxime Coquelin vq->kickfd = VIRTIO_INVALID_EVENTFD; 184a9120db8SMaxime Coquelin return; 185a9120db8SMaxime Coquelin } 1860e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tkick fd: %d", vq->kickfd); 187a9120db8SMaxime Coquelin 188a9120db8SMaxime Coquelin vq->shadow_used_split = rte_malloc_socket(NULL, 189a9120db8SMaxime Coquelin vq->size * sizeof(struct vring_used_elem), 190a9120db8SMaxime Coquelin RTE_CACHE_LINE_SIZE, 0); 191a9120db8SMaxime Coquelin vq->batch_copy_elems = rte_malloc_socket(NULL, 192a9120db8SMaxime Coquelin vq->size * sizeof(struct batch_copy_elem), 193a9120db8SMaxime Coquelin RTE_CACHE_LINE_SIZE, 0); 194a9120db8SMaxime Coquelin 195af532865SDavid Marchand rte_rwlock_write_lock(&vq->access_lock); 196a9120db8SMaxime Coquelin vhost_user_iotlb_rd_lock(vq); 197a9120db8SMaxime Coquelin if (vring_translate(dev, vq)) 1980e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to translate vring %d addresses", 199a9120db8SMaxime Coquelin index); 200a9120db8SMaxime Coquelin 201a9120db8SMaxime Coquelin if (vhost_enable_guest_notification(dev, vq, 0)) 2020e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 2030e21c7c0SDavid Marchand "Failed to disable guest notifications on vring %d", 204a9120db8SMaxime Coquelin index); 205a9120db8SMaxime Coquelin vhost_user_iotlb_rd_unlock(vq); 206af532865SDavid Marchand rte_rwlock_write_unlock(&vq->access_lock); 207a9120db8SMaxime Coquelin 208a9120db8SMaxime Coquelin vq_efd.index = index; 209a9120db8SMaxime Coquelin vq_efd.fd = vq->kickfd; 210a9120db8SMaxime Coquelin 211a9120db8SMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 212a9120db8SMaxime Coquelin if (ret) { 2130e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to setup kickfd for VQ %u: %s", 214a9120db8SMaxime Coquelin index, strerror(errno)); 215a9120db8SMaxime Coquelin close(vq->kickfd); 216a9120db8SMaxime Coquelin vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 217a9120db8SMaxime Coquelin return; 218a9120db8SMaxime Coquelin } 219653327e1SMaxime Coquelin 220653327e1SMaxime Coquelin if (vq == dev->cvq) { 221e68a6feaSMaxime Coquelin ret = fdset_add(vduse.fdset, vq->kickfd, vduse_control_queue_event, NULL, dev); 222653327e1SMaxime Coquelin if (ret) { 2230e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 2240e21c7c0SDavid Marchand "Failed to setup kickfd handler for VQ %u: %s", 225653327e1SMaxime Coquelin index, strerror(errno)); 226653327e1SMaxime Coquelin vq_efd.fd = VDUSE_EVENTFD_DEASSIGN; 227653327e1SMaxime Coquelin ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 228653327e1SMaxime Coquelin close(vq->kickfd); 229653327e1SMaxime Coquelin vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 230653327e1SMaxime Coquelin } 231653327e1SMaxime Coquelin vhost_enable_guest_notification(dev, vq, 1); 2320e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Ctrl queue event handler installed"); 233653327e1SMaxime Coquelin } 234a9120db8SMaxime Coquelin } 235a9120db8SMaxime Coquelin 236a9120db8SMaxime Coquelin static void 237ad67c65eSMaxime Coquelin vduse_vring_cleanup(struct virtio_net *dev, unsigned int index) 238ad67c65eSMaxime Coquelin { 239ad67c65eSMaxime Coquelin struct vhost_virtqueue *vq = dev->virtqueue[index]; 240ad67c65eSMaxime Coquelin struct vduse_vq_eventfd vq_efd; 241ad67c65eSMaxime Coquelin int ret; 242ad67c65eSMaxime Coquelin 2430c18e4fbSMaxime Coquelin if (vq == dev->cvq && vq->kickfd >= 0) 244e68a6feaSMaxime Coquelin fdset_del(vduse.fdset, vq->kickfd); 245ad67c65eSMaxime Coquelin 246ad67c65eSMaxime Coquelin vq_efd.index = index; 247ad67c65eSMaxime Coquelin vq_efd.fd = VDUSE_EVENTFD_DEASSIGN; 248ad67c65eSMaxime Coquelin 249ad67c65eSMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP_KICKFD, &vq_efd); 250ad67c65eSMaxime Coquelin if (ret) 2510e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to cleanup kickfd for VQ %u: %s", 252ad67c65eSMaxime Coquelin index, strerror(errno)); 253ad67c65eSMaxime Coquelin 254ad67c65eSMaxime Coquelin close(vq->kickfd); 255ad67c65eSMaxime Coquelin vq->kickfd = VIRTIO_UNINITIALIZED_EVENTFD; 256ad67c65eSMaxime Coquelin 257af532865SDavid Marchand rte_rwlock_write_lock(&vq->access_lock); 258ad67c65eSMaxime Coquelin vring_invalidate(dev, vq); 259af532865SDavid Marchand rte_rwlock_write_unlock(&vq->access_lock); 260ad67c65eSMaxime Coquelin 261ad67c65eSMaxime Coquelin rte_free(vq->batch_copy_elems); 262ad67c65eSMaxime Coquelin vq->batch_copy_elems = NULL; 263ad67c65eSMaxime Coquelin 264ad67c65eSMaxime Coquelin rte_free(vq->shadow_used_split); 265ad67c65eSMaxime Coquelin vq->shadow_used_split = NULL; 266ad67c65eSMaxime Coquelin 267ad67c65eSMaxime Coquelin vq->enabled = false; 268ad67c65eSMaxime Coquelin vq->ready = false; 269ad67c65eSMaxime Coquelin vq->size = 0; 270ad67c65eSMaxime Coquelin vq->last_used_idx = 0; 271ad67c65eSMaxime Coquelin vq->last_avail_idx = 0; 272ad67c65eSMaxime Coquelin } 273ad67c65eSMaxime Coquelin 274ad67c65eSMaxime Coquelin static void 275da79cc7fSMaxime Coquelin vduse_device_start(struct virtio_net *dev, bool reconnect) 276a9120db8SMaxime Coquelin { 277a9120db8SMaxime Coquelin unsigned int i, ret; 278a9120db8SMaxime Coquelin 2790e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Starting device..."); 280a9120db8SMaxime Coquelin 281a9120db8SMaxime Coquelin dev->notify_ops = vhost_driver_callback_get(dev->ifname); 282a9120db8SMaxime Coquelin if (!dev->notify_ops) { 2830e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, 2840e21c7c0SDavid Marchand "Failed to get callback ops for driver"); 285a9120db8SMaxime Coquelin return; 286a9120db8SMaxime Coquelin } 287a9120db8SMaxime Coquelin 288a9120db8SMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_DEV_GET_FEATURES, &dev->features); 289a9120db8SMaxime Coquelin if (ret) { 2900e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to get features: %s", 291a9120db8SMaxime Coquelin strerror(errno)); 292a9120db8SMaxime Coquelin return; 293a9120db8SMaxime Coquelin } 294a9120db8SMaxime Coquelin 295da79cc7fSMaxime Coquelin if (reconnect && dev->features != dev->reconnect_log->features) { 296da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, 297da79cc7fSMaxime Coquelin "Mismatch between reconnect file features 0x%" PRIx64 " & device features 0x%" PRIx64, 298da79cc7fSMaxime Coquelin dev->reconnect_log->features, dev->features); 299da79cc7fSMaxime Coquelin return; 300da79cc7fSMaxime Coquelin } 301da79cc7fSMaxime Coquelin 302da79cc7fSMaxime Coquelin dev->reconnect_log->features = dev->features; 303da79cc7fSMaxime Coquelin 3040e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Negotiated Virtio features: 0x%" PRIx64, 305a9120db8SMaxime Coquelin dev->features); 306a9120db8SMaxime Coquelin 307a9120db8SMaxime Coquelin if (dev->features & 308a9120db8SMaxime Coquelin ((1ULL << VIRTIO_NET_F_MRG_RXBUF) | 309a9120db8SMaxime Coquelin (1ULL << VIRTIO_F_VERSION_1) | 310a9120db8SMaxime Coquelin (1ULL << VIRTIO_F_RING_PACKED))) { 311a9120db8SMaxime Coquelin dev->vhost_hlen = sizeof(struct virtio_net_hdr_mrg_rxbuf); 312a9120db8SMaxime Coquelin } else { 313a9120db8SMaxime Coquelin dev->vhost_hlen = sizeof(struct virtio_net_hdr); 314a9120db8SMaxime Coquelin } 315a9120db8SMaxime Coquelin 316a9120db8SMaxime Coquelin for (i = 0; i < dev->nr_vring; i++) 317da79cc7fSMaxime Coquelin vduse_vring_setup(dev, i, reconnect); 318a9120db8SMaxime Coquelin 319a9120db8SMaxime Coquelin dev->flags |= VIRTIO_DEV_READY; 320a9120db8SMaxime Coquelin 321a9120db8SMaxime Coquelin if (dev->notify_ops->new_device(dev->vid) == 0) 322a9120db8SMaxime Coquelin dev->flags |= VIRTIO_DEV_RUNNING; 323a9120db8SMaxime Coquelin 324a9120db8SMaxime Coquelin for (i = 0; i < dev->nr_vring; i++) { 325a9120db8SMaxime Coquelin struct vhost_virtqueue *vq = dev->virtqueue[i]; 326a9120db8SMaxime Coquelin 327653327e1SMaxime Coquelin if (vq == dev->cvq) 328653327e1SMaxime Coquelin continue; 329653327e1SMaxime Coquelin 330a9120db8SMaxime Coquelin if (dev->notify_ops->vring_state_changed) 331a9120db8SMaxime Coquelin dev->notify_ops->vring_state_changed(dev->vid, i, vq->enabled); 332a9120db8SMaxime Coquelin } 333a9120db8SMaxime Coquelin } 334a9120db8SMaxime Coquelin 335a9120db8SMaxime Coquelin static void 336ad67c65eSMaxime Coquelin vduse_device_stop(struct virtio_net *dev) 337ad67c65eSMaxime Coquelin { 338ad67c65eSMaxime Coquelin unsigned int i; 339ad67c65eSMaxime Coquelin 3400e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Stopping device..."); 341ad67c65eSMaxime Coquelin 342ad67c65eSMaxime Coquelin vhost_destroy_device_notify(dev); 343ad67c65eSMaxime Coquelin 344ad67c65eSMaxime Coquelin dev->flags &= ~VIRTIO_DEV_READY; 345ad67c65eSMaxime Coquelin 346ad67c65eSMaxime Coquelin for (i = 0; i < dev->nr_vring; i++) 347ad67c65eSMaxime Coquelin vduse_vring_cleanup(dev, i); 348ad67c65eSMaxime Coquelin 349ad67c65eSMaxime Coquelin vhost_user_iotlb_flush_all(dev); 350ad67c65eSMaxime Coquelin } 351ad67c65eSMaxime Coquelin 352ad67c65eSMaxime Coquelin static void 35351d018fdSMaxime Coquelin vduse_events_handler(int fd, void *arg, int *remove __rte_unused) 35451d018fdSMaxime Coquelin { 35551d018fdSMaxime Coquelin struct virtio_net *dev = arg; 35651d018fdSMaxime Coquelin struct vduse_dev_request req; 35751d018fdSMaxime Coquelin struct vduse_dev_response resp; 35810f18cb1SMaxime Coquelin struct vhost_virtqueue *vq; 359ad67c65eSMaxime Coquelin uint8_t old_status = dev->status; 36051d018fdSMaxime Coquelin int ret; 36151d018fdSMaxime Coquelin 36251d018fdSMaxime Coquelin memset(&resp, 0, sizeof(resp)); 36351d018fdSMaxime Coquelin 36451d018fdSMaxime Coquelin ret = read(fd, &req, sizeof(req)); 36551d018fdSMaxime Coquelin if (ret < 0) { 3660e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to read request: %s", 36751d018fdSMaxime Coquelin strerror(errno)); 36851d018fdSMaxime Coquelin return; 36951d018fdSMaxime Coquelin } else if (ret < (int)sizeof(req)) { 3700e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Incomplete to read request %d", ret); 37151d018fdSMaxime Coquelin return; 37251d018fdSMaxime Coquelin } 37351d018fdSMaxime Coquelin 3740e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "New request: %s (%u)", 37551d018fdSMaxime Coquelin vduse_req_id_to_str(req.type), req.type); 37651d018fdSMaxime Coquelin 37751d018fdSMaxime Coquelin switch (req.type) { 37810f18cb1SMaxime Coquelin case VDUSE_GET_VQ_STATE: 37910f18cb1SMaxime Coquelin vq = dev->virtqueue[req.vq_state.index]; 3800e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tvq index: %u, avail_index: %u", 38110f18cb1SMaxime Coquelin req.vq_state.index, vq->last_avail_idx); 38210f18cb1SMaxime Coquelin resp.vq_state.split.avail_index = vq->last_avail_idx; 38310f18cb1SMaxime Coquelin resp.result = VDUSE_REQ_RESULT_OK; 38410f18cb1SMaxime Coquelin break; 38577c78c17SMaxime Coquelin case VDUSE_SET_STATUS: 3860e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tnew status: 0x%08x", 38777c78c17SMaxime Coquelin req.s.status); 388ad67c65eSMaxime Coquelin old_status = dev->status; 38977c78c17SMaxime Coquelin dev->status = req.s.status; 390da79cc7fSMaxime Coquelin dev->reconnect_log->status = dev->status; 39177c78c17SMaxime Coquelin resp.result = VDUSE_REQ_RESULT_OK; 39277c78c17SMaxime Coquelin break; 393400f40a3SMaxime Coquelin case VDUSE_UPDATE_IOTLB: 3940e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "\tIOVA range: %" PRIx64 " - %" PRIx64, 395400f40a3SMaxime Coquelin (uint64_t)req.iova.start, (uint64_t)req.iova.last); 396400f40a3SMaxime Coquelin vhost_user_iotlb_cache_remove(dev, req.iova.start, 397400f40a3SMaxime Coquelin req.iova.last - req.iova.start + 1); 398400f40a3SMaxime Coquelin resp.result = VDUSE_REQ_RESULT_OK; 399400f40a3SMaxime Coquelin break; 40051d018fdSMaxime Coquelin default: 40151d018fdSMaxime Coquelin resp.result = VDUSE_REQ_RESULT_FAILED; 40251d018fdSMaxime Coquelin break; 40351d018fdSMaxime Coquelin } 40451d018fdSMaxime Coquelin 40551d018fdSMaxime Coquelin resp.request_id = req.request_id; 40651d018fdSMaxime Coquelin 40751d018fdSMaxime Coquelin ret = write(dev->vduse_dev_fd, &resp, sizeof(resp)); 40851d018fdSMaxime Coquelin if (ret != sizeof(resp)) { 4090e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write response %s", 41051d018fdSMaxime Coquelin strerror(errno)); 41151d018fdSMaxime Coquelin return; 41251d018fdSMaxime Coquelin } 413a9120db8SMaxime Coquelin 414ad67c65eSMaxime Coquelin if ((old_status ^ dev->status) & VIRTIO_DEVICE_STATUS_DRIVER_OK) { 415a9120db8SMaxime Coquelin if (dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK) 416da79cc7fSMaxime Coquelin vduse_device_start(dev, false); 417ad67c65eSMaxime Coquelin else 418ad67c65eSMaxime Coquelin vduse_device_stop(dev); 419ad67c65eSMaxime Coquelin } 420a9120db8SMaxime Coquelin 4210e21c7c0SDavid Marchand VHOST_CONFIG_LOG(dev->ifname, INFO, "Request %s (%u) handled successfully", 42251d018fdSMaxime Coquelin vduse_req_id_to_str(req.type), req.type); 42351d018fdSMaxime Coquelin } 42451d018fdSMaxime Coquelin 425da79cc7fSMaxime Coquelin static char vduse_reconnect_dir[PATH_MAX]; 426da79cc7fSMaxime Coquelin static bool vduse_reconnect_path_set; 427da79cc7fSMaxime Coquelin 428da79cc7fSMaxime Coquelin static int 429da79cc7fSMaxime Coquelin vduse_reconnect_path_init(void) 430da79cc7fSMaxime Coquelin { 431da79cc7fSMaxime Coquelin const char *directory; 432da79cc7fSMaxime Coquelin int ret; 433da79cc7fSMaxime Coquelin 43455977694SMaxime Coquelin if (vduse_reconnect_path_set == true) 43555977694SMaxime Coquelin return 0; 43655977694SMaxime Coquelin 437da79cc7fSMaxime Coquelin /* from RuntimeDirectory= see systemd.exec */ 438da79cc7fSMaxime Coquelin directory = getenv("RUNTIME_DIRECTORY"); 439da79cc7fSMaxime Coquelin if (directory == NULL) { 440da79cc7fSMaxime Coquelin /* 441da79cc7fSMaxime Coquelin * Used standard convention defined in 442da79cc7fSMaxime Coquelin * XDG Base Directory Specification and 443da79cc7fSMaxime Coquelin * Filesystem Hierarchy Standard. 444da79cc7fSMaxime Coquelin */ 445da79cc7fSMaxime Coquelin if (getuid() == 0) 446da79cc7fSMaxime Coquelin directory = "/var/run"; 447da79cc7fSMaxime Coquelin else 448da79cc7fSMaxime Coquelin directory = getenv("XDG_RUNTIME_DIR") ? : "/tmp"; 449da79cc7fSMaxime Coquelin } 450da79cc7fSMaxime Coquelin 451da79cc7fSMaxime Coquelin ret = snprintf(vduse_reconnect_dir, sizeof(vduse_reconnect_dir), "%s/vduse", 452da79cc7fSMaxime Coquelin directory); 453da79cc7fSMaxime Coquelin if (ret < 0 || ret == sizeof(vduse_reconnect_dir)) { 454da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG("vduse", ERR, "Error creating VDUSE reconnect path name"); 455da79cc7fSMaxime Coquelin return -1; 456da79cc7fSMaxime Coquelin } 457da79cc7fSMaxime Coquelin 458da79cc7fSMaxime Coquelin ret = mkdir(vduse_reconnect_dir, 0700); 459da79cc7fSMaxime Coquelin if (ret < 0 && errno != EEXIST) { 460da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG("vduse", ERR, "Error creating '%s': %s", 461da79cc7fSMaxime Coquelin vduse_reconnect_dir, strerror(errno)); 462da79cc7fSMaxime Coquelin return -1; 463da79cc7fSMaxime Coquelin } 464da79cc7fSMaxime Coquelin 465da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG("vduse", INFO, "Created VDUSE reconnect directory in %s", 466da79cc7fSMaxime Coquelin vduse_reconnect_dir); 467da79cc7fSMaxime Coquelin 46855977694SMaxime Coquelin vduse_reconnect_path_set = true; 46955977694SMaxime Coquelin 470da79cc7fSMaxime Coquelin return 0; 471da79cc7fSMaxime Coquelin } 472da79cc7fSMaxime Coquelin 47355977694SMaxime Coquelin static int 474a2a05e55SMaxime Coquelin vduse_reconnect_log_map(struct virtio_net *dev, bool create) 47555977694SMaxime Coquelin { 47655977694SMaxime Coquelin char reco_file[PATH_MAX]; 47755977694SMaxime Coquelin int fd, ret; 478a2a05e55SMaxime Coquelin const char *name = dev->ifname + strlen("/dev/vduse/"); 47955977694SMaxime Coquelin 48055977694SMaxime Coquelin if (vduse_reconnect_path_init() < 0) { 481a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to initialize reconnect path"); 48255977694SMaxime Coquelin return -1; 48355977694SMaxime Coquelin } 48455977694SMaxime Coquelin 485a2a05e55SMaxime Coquelin ret = snprintf(reco_file, sizeof(reco_file), "%s/%s", vduse_reconnect_dir, name); 48655977694SMaxime Coquelin if (ret < 0 || ret == sizeof(reco_file)) { 487a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to create vduse reconnect path name"); 48855977694SMaxime Coquelin return -1; 48955977694SMaxime Coquelin } 49055977694SMaxime Coquelin 49155977694SMaxime Coquelin if (create) { 49255977694SMaxime Coquelin fd = open(reco_file, O_CREAT | O_EXCL | O_RDWR, 0600); 49355977694SMaxime Coquelin if (fd < 0) { 49455977694SMaxime Coquelin if (errno == EEXIST) { 495a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Reconnect file %s exists but not the device", 49655977694SMaxime Coquelin reco_file); 49755977694SMaxime Coquelin } else { 498a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to open reconnect file %s (%s)", 49955977694SMaxime Coquelin reco_file, strerror(errno)); 50055977694SMaxime Coquelin } 50155977694SMaxime Coquelin return -1; 50255977694SMaxime Coquelin } 50355977694SMaxime Coquelin 504a2a05e55SMaxime Coquelin ret = ftruncate(fd, sizeof(*dev->reconnect_log)); 50555977694SMaxime Coquelin if (ret < 0) { 506a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to truncate reconnect file %s (%s)", 50755977694SMaxime Coquelin reco_file, strerror(errno)); 50855977694SMaxime Coquelin goto out_close; 50955977694SMaxime Coquelin } 51055977694SMaxime Coquelin } else { 51155977694SMaxime Coquelin fd = open(reco_file, O_RDWR, 0600); 51255977694SMaxime Coquelin if (fd < 0) { 51355977694SMaxime Coquelin if (errno == ENOENT) 514a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Missing reconnect file (%s)", reco_file); 51555977694SMaxime Coquelin else 516a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to open reconnect file %s (%s)", 51755977694SMaxime Coquelin reco_file, strerror(errno)); 51855977694SMaxime Coquelin return -1; 51955977694SMaxime Coquelin } 52055977694SMaxime Coquelin } 52155977694SMaxime Coquelin 522a2a05e55SMaxime Coquelin dev->reconnect_log = mmap(NULL, sizeof(*dev->reconnect_log), PROT_READ | PROT_WRITE, 523a2a05e55SMaxime Coquelin MAP_SHARED, fd, 0); 524a2a05e55SMaxime Coquelin if (dev->reconnect_log == MAP_FAILED) { 525a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to mmap reconnect file %s (%s)", 52655977694SMaxime Coquelin reco_file, strerror(errno)); 52755977694SMaxime Coquelin ret = -1; 52855977694SMaxime Coquelin goto out_close; 52955977694SMaxime Coquelin } 53055977694SMaxime Coquelin ret = 0; 53155977694SMaxime Coquelin 53255977694SMaxime Coquelin out_close: 53355977694SMaxime Coquelin close(fd); 53455977694SMaxime Coquelin 53555977694SMaxime Coquelin return ret; 53655977694SMaxime Coquelin } 53755977694SMaxime Coquelin 53869d2e256SMaxime Coquelin static int 539a2a05e55SMaxime Coquelin vduse_reconnect_log_check(struct virtio_net *dev, uint64_t features, uint32_t total_queues) 54069d2e256SMaxime Coquelin { 541a2a05e55SMaxime Coquelin if (dev->reconnect_log->version != VHOST_RECONNECT_VERSION) { 542a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, 54369d2e256SMaxime Coquelin "Version mismatch between backend (0x%x) & reconnection file (0x%x)", 544a2a05e55SMaxime Coquelin VHOST_RECONNECT_VERSION, dev->reconnect_log->version); 54569d2e256SMaxime Coquelin return -1; 54669d2e256SMaxime Coquelin } 54769d2e256SMaxime Coquelin 548a2a05e55SMaxime Coquelin if ((dev->reconnect_log->features & features) != dev->reconnect_log->features) { 549a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, 55069d2e256SMaxime Coquelin "Features mismatch between backend (0x%" PRIx64 ") & reconnection file (0x%" PRIx64 ")", 551a2a05e55SMaxime Coquelin features, dev->reconnect_log->features); 55269d2e256SMaxime Coquelin return -1; 55369d2e256SMaxime Coquelin } 55469d2e256SMaxime Coquelin 555a2a05e55SMaxime Coquelin if (dev->reconnect_log->nr_vrings != total_queues) { 556a2a05e55SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, 55769d2e256SMaxime Coquelin "Queues number mismatch between backend (%u) and reconnection file (%u)", 558a2a05e55SMaxime Coquelin total_queues, dev->reconnect_log->nr_vrings); 55969d2e256SMaxime Coquelin return -1; 56069d2e256SMaxime Coquelin } 56169d2e256SMaxime Coquelin 56269d2e256SMaxime Coquelin return 0; 56369d2e256SMaxime Coquelin } 56469d2e256SMaxime Coquelin 565da79cc7fSMaxime Coquelin static void 566da79cc7fSMaxime Coquelin vduse_reconnect_handler(int fd, void *arg, int *remove) 567da79cc7fSMaxime Coquelin { 568da79cc7fSMaxime Coquelin struct virtio_net *dev = arg; 569da79cc7fSMaxime Coquelin 570da79cc7fSMaxime Coquelin vduse_device_start(dev, true); 571da79cc7fSMaxime Coquelin 572da79cc7fSMaxime Coquelin close(fd); 573da79cc7fSMaxime Coquelin *remove = 1; 574da79cc7fSMaxime Coquelin } 575da79cc7fSMaxime Coquelin 576952e4945SMaxime Coquelin static int 577952e4945SMaxime Coquelin vduse_reconnect_start_device(struct virtio_net *dev) 578952e4945SMaxime Coquelin { 579952e4945SMaxime Coquelin int fd, ret; 580952e4945SMaxime Coquelin 581952e4945SMaxime Coquelin /* 582952e4945SMaxime Coquelin * Make vduse_device_start() being executed in the same 583952e4945SMaxime Coquelin * context for both reconnection and fresh startup. 584952e4945SMaxime Coquelin */ 585952e4945SMaxime Coquelin fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC); 586952e4945SMaxime Coquelin if (fd < 0) { 587952e4945SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to create reconnect efd: %s", 588952e4945SMaxime Coquelin strerror(errno)); 589952e4945SMaxime Coquelin ret = -1; 590952e4945SMaxime Coquelin goto out_err; 591952e4945SMaxime Coquelin } 592952e4945SMaxime Coquelin 593952e4945SMaxime Coquelin ret = fdset_add(vduse.fdset, fd, vduse_reconnect_handler, NULL, dev); 594952e4945SMaxime Coquelin if (ret) { 595952e4945SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to add reconnect efd %d to vduse fdset", 596952e4945SMaxime Coquelin fd); 597952e4945SMaxime Coquelin goto out_err_close; 598952e4945SMaxime Coquelin } 599952e4945SMaxime Coquelin 600952e4945SMaxime Coquelin ret = eventfd_write(fd, (eventfd_t)1); 601952e4945SMaxime Coquelin if (ret < 0) { 602952e4945SMaxime Coquelin VHOST_CONFIG_LOG(dev->ifname, ERR, "Failed to write to reconnect eventfd"); 603952e4945SMaxime Coquelin goto out_err_fdset; 604952e4945SMaxime Coquelin } 605952e4945SMaxime Coquelin 606952e4945SMaxime Coquelin return 0; 607952e4945SMaxime Coquelin 608952e4945SMaxime Coquelin out_err_fdset: 609952e4945SMaxime Coquelin fdset_del(vduse.fdset, fd); 610952e4945SMaxime Coquelin out_err_close: 611952e4945SMaxime Coquelin close(fd); 612952e4945SMaxime Coquelin out_err: 613952e4945SMaxime Coquelin return ret; 614952e4945SMaxime Coquelin } 615952e4945SMaxime Coquelin 6160adb8eccSMaxime Coquelin int 617927d2aefSMaxime Coquelin vduse_device_create(const char *path, bool compliant_ol_flags) 6180adb8eccSMaxime Coquelin { 61955977694SMaxime Coquelin int control_fd, dev_fd, vid, ret; 620653327e1SMaxime Coquelin uint32_t i, max_queue_pairs, total_queues; 6210adb8eccSMaxime Coquelin struct virtio_net *dev; 62225bdd1c3SAli Alnubani struct virtio_net_config vnet_config = {{ 0 }}; 6230adb8eccSMaxime Coquelin uint64_t ver = VHOST_VDUSE_API_VERSION; 6244789eb43SMaxime Coquelin uint64_t features; 6250adb8eccSMaxime Coquelin const char *name = path + strlen("/dev/vduse/"); 626da79cc7fSMaxime Coquelin bool reconnect = false; 6270adb8eccSMaxime Coquelin 628e68a6feaSMaxime Coquelin if (vduse.fdset == NULL) { 629e68a6feaSMaxime Coquelin vduse.fdset = fdset_init("vduse-evt"); 630e68a6feaSMaxime Coquelin if (vduse.fdset == NULL) { 6317945769cSMaxime Coquelin VHOST_CONFIG_LOG(path, ERR, "failed to init VDUSE fdset"); 6327945769cSMaxime Coquelin return -1; 6337945769cSMaxime Coquelin } 63451d018fdSMaxime Coquelin } 63551d018fdSMaxime Coquelin 6360adb8eccSMaxime Coquelin control_fd = open(VDUSE_CTRL_PATH, O_RDWR); 6370adb8eccSMaxime Coquelin if (control_fd < 0) { 6380e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to open %s: %s", 6390adb8eccSMaxime Coquelin VDUSE_CTRL_PATH, strerror(errno)); 6400adb8eccSMaxime Coquelin return -1; 6410adb8eccSMaxime Coquelin } 6420adb8eccSMaxime Coquelin 6430adb8eccSMaxime Coquelin if (ioctl(control_fd, VDUSE_SET_API_VERSION, &ver)) { 6440e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to set API version: %" PRIu64 ": %s", 6450adb8eccSMaxime Coquelin ver, strerror(errno)); 6460adb8eccSMaxime Coquelin ret = -1; 6470adb8eccSMaxime Coquelin goto out_ctrl_close; 6480adb8eccSMaxime Coquelin } 6490adb8eccSMaxime Coquelin 6504789eb43SMaxime Coquelin ret = rte_vhost_driver_get_features(path, &features); 6514789eb43SMaxime Coquelin if (ret < 0) { 6520e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to get backend features"); 65347458d13SMaxime Coquelin goto out_ctrl_close; 6544789eb43SMaxime Coquelin } 6554789eb43SMaxime Coquelin 656653327e1SMaxime Coquelin ret = rte_vhost_driver_get_queue_num(path, &max_queue_pairs); 657653327e1SMaxime Coquelin if (ret < 0) { 6580e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to get max queue pairs"); 65947458d13SMaxime Coquelin goto out_ctrl_close; 660653327e1SMaxime Coquelin } 661653327e1SMaxime Coquelin 6620e21c7c0SDavid Marchand VHOST_CONFIG_LOG(path, INFO, "VDUSE max queue pairs: %u", max_queue_pairs); 663653327e1SMaxime Coquelin total_queues = max_queue_pairs * 2; 664653327e1SMaxime Coquelin 665653327e1SMaxime Coquelin if (max_queue_pairs == 1) 666653327e1SMaxime Coquelin features &= ~(RTE_BIT64(VIRTIO_NET_F_CTRL_VQ) | RTE_BIT64(VIRTIO_NET_F_MQ)); 667653327e1SMaxime Coquelin else 668653327e1SMaxime Coquelin total_queues += 1; /* Includes ctrl queue */ 669653327e1SMaxime Coquelin 67029ab97ddSMaxime Coquelin dev_fd = open(path, O_RDWR); 67129ab97ddSMaxime Coquelin if (dev_fd >= 0) { 672da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(name, INFO, "Device already exists, reconnecting..."); 673da79cc7fSMaxime Coquelin reconnect = true; 67429ab97ddSMaxime Coquelin } else if (errno == ENOENT) { 67547458d13SMaxime Coquelin struct vduse_dev_config *dev_config; 67647458d13SMaxime Coquelin 677da79cc7fSMaxime Coquelin dev_config = malloc(offsetof(struct vduse_dev_config, config) + 678da79cc7fSMaxime Coquelin sizeof(vnet_config)); 679da79cc7fSMaxime Coquelin if (!dev_config) { 680da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(name, ERR, "Failed to allocate VDUSE config"); 681da79cc7fSMaxime Coquelin ret = -1; 682a2a05e55SMaxime Coquelin goto out_ctrl_close; 683da79cc7fSMaxime Coquelin } 684da79cc7fSMaxime Coquelin 685653327e1SMaxime Coquelin vnet_config.max_virtqueue_pairs = max_queue_pairs; 6860adb8eccSMaxime Coquelin memset(dev_config, 0, sizeof(struct vduse_dev_config)); 6870adb8eccSMaxime Coquelin 688da79cc7fSMaxime Coquelin rte_strscpy(dev_config->name, name, VDUSE_NAME_MAX - 1); 6890adb8eccSMaxime Coquelin dev_config->device_id = VIRTIO_ID_NET; 6900adb8eccSMaxime Coquelin dev_config->vendor_id = 0; 691653327e1SMaxime Coquelin dev_config->features = features; 692653327e1SMaxime Coquelin dev_config->vq_num = total_queues; 6930adb8eccSMaxime Coquelin dev_config->vq_align = sysconf(_SC_PAGE_SIZE); 694653327e1SMaxime Coquelin dev_config->config_size = sizeof(struct virtio_net_config); 695653327e1SMaxime Coquelin memcpy(dev_config->config, &vnet_config, sizeof(vnet_config)); 6960adb8eccSMaxime Coquelin 6970adb8eccSMaxime Coquelin ret = ioctl(control_fd, VDUSE_CREATE_DEV, dev_config); 69847458d13SMaxime Coquelin free(dev_config); 69947458d13SMaxime Coquelin dev_config = NULL; 7000adb8eccSMaxime Coquelin if (ret < 0) { 7010e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to create VDUSE device: %s", 7020adb8eccSMaxime Coquelin strerror(errno)); 703a2a05e55SMaxime Coquelin goto out_ctrl_close; 7040adb8eccSMaxime Coquelin } 7050adb8eccSMaxime Coquelin 7060adb8eccSMaxime Coquelin dev_fd = open(path, O_RDWR); 7070adb8eccSMaxime Coquelin if (dev_fd < 0) { 70829ab97ddSMaxime Coquelin VHOST_CONFIG_LOG(name, ERR, "Failed to open newly created device %s: %s", 70929ab97ddSMaxime Coquelin path, strerror(errno)); 71029ab97ddSMaxime Coquelin ret = -1; 711a2a05e55SMaxime Coquelin goto out_ctrl_close; 71229ab97ddSMaxime Coquelin } 71329ab97ddSMaxime Coquelin } else { 7140e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to open device %s: %s", 7150adb8eccSMaxime Coquelin path, strerror(errno)); 7160adb8eccSMaxime Coquelin ret = -1; 71729ab97ddSMaxime Coquelin goto out_ctrl_close; 7180adb8eccSMaxime Coquelin } 7190adb8eccSMaxime Coquelin 7200adb8eccSMaxime Coquelin ret = fcntl(dev_fd, F_SETFL, O_NONBLOCK); 7210adb8eccSMaxime Coquelin if (ret < 0) { 7220e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to set chardev as non-blocking: %s", 7230adb8eccSMaxime Coquelin strerror(errno)); 724a2a05e55SMaxime Coquelin goto out_dev_close; 7250adb8eccSMaxime Coquelin } 7260adb8eccSMaxime Coquelin 7270adb8eccSMaxime Coquelin vid = vhost_new_device(&vduse_backend_ops); 7280adb8eccSMaxime Coquelin if (vid < 0) { 7290e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to create new Vhost device"); 7300adb8eccSMaxime Coquelin ret = -1; 731a2a05e55SMaxime Coquelin goto out_dev_close; 7320adb8eccSMaxime Coquelin } 7330adb8eccSMaxime Coquelin 7340adb8eccSMaxime Coquelin dev = get_device(vid); 7350adb8eccSMaxime Coquelin if (!dev) { 7360adb8eccSMaxime Coquelin ret = -1; 73729ab97ddSMaxime Coquelin goto out_dev_destroy; 7380adb8eccSMaxime Coquelin } 7390adb8eccSMaxime Coquelin 7400adb8eccSMaxime Coquelin strncpy(dev->ifname, path, IF_NAME_SZ - 1); 7410adb8eccSMaxime Coquelin dev->vduse_ctrl_fd = control_fd; 7420adb8eccSMaxime Coquelin dev->vduse_dev_fd = dev_fd; 743a2a05e55SMaxime Coquelin 744a2a05e55SMaxime Coquelin ret = vduse_reconnect_log_map(dev, !reconnect); 745a2a05e55SMaxime Coquelin if (ret < 0) 746a2a05e55SMaxime Coquelin goto out_dev_destroy; 747a2a05e55SMaxime Coquelin 748a2a05e55SMaxime Coquelin if (reconnect) { 749a2a05e55SMaxime Coquelin ret = vduse_reconnect_log_check(dev, features, total_queues); 750a2a05e55SMaxime Coquelin if (ret < 0) 751a2a05e55SMaxime Coquelin goto out_log_unmap; 752a2a05e55SMaxime Coquelin 753da79cc7fSMaxime Coquelin dev->status = dev->reconnect_log->status; 754a2a05e55SMaxime Coquelin } else { 755a2a05e55SMaxime Coquelin dev->reconnect_log->version = VHOST_RECONNECT_VERSION; 756a2a05e55SMaxime Coquelin dev->reconnect_log->nr_vrings = total_queues; 757a2a05e55SMaxime Coquelin memcpy(&dev->reconnect_log->config, &vnet_config, sizeof(vnet_config)); 758a2a05e55SMaxime Coquelin } 759da79cc7fSMaxime Coquelin 760927d2aefSMaxime Coquelin vhost_setup_virtio_net(dev->vid, true, compliant_ol_flags, true, true); 7610adb8eccSMaxime Coquelin 762653327e1SMaxime Coquelin for (i = 0; i < total_queues; i++) { 7630adb8eccSMaxime Coquelin struct vduse_vq_config vq_cfg = { 0 }; 764da79cc7fSMaxime Coquelin struct vhost_virtqueue *vq; 7650adb8eccSMaxime Coquelin 7660adb8eccSMaxime Coquelin ret = alloc_vring_queue(dev, i); 7670adb8eccSMaxime Coquelin if (ret) { 7680e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to alloc vring %d metadata", i); 769a2a05e55SMaxime Coquelin goto out_log_unmap; 7700adb8eccSMaxime Coquelin } 7710adb8eccSMaxime Coquelin 772da79cc7fSMaxime Coquelin vq = dev->virtqueue[i]; 773a2a05e55SMaxime Coquelin vq->reconnect_log = &dev->reconnect_log->vring[i]; 774da79cc7fSMaxime Coquelin 775da79cc7fSMaxime Coquelin if (reconnect) 776da79cc7fSMaxime Coquelin continue; 777da79cc7fSMaxime Coquelin 7780adb8eccSMaxime Coquelin vq_cfg.index = i; 7790adb8eccSMaxime Coquelin vq_cfg.max_size = 1024; 7800adb8eccSMaxime Coquelin 7810adb8eccSMaxime Coquelin ret = ioctl(dev->vduse_dev_fd, VDUSE_VQ_SETUP, &vq_cfg); 7820adb8eccSMaxime Coquelin if (ret) { 7830e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to set-up VQ %d", i); 784a2a05e55SMaxime Coquelin goto out_log_unmap; 7850adb8eccSMaxime Coquelin } 7860adb8eccSMaxime Coquelin } 7870adb8eccSMaxime Coquelin 788653327e1SMaxime Coquelin dev->cvq = dev->virtqueue[max_queue_pairs * 2]; 789653327e1SMaxime Coquelin 790e68a6feaSMaxime Coquelin ret = fdset_add(vduse.fdset, dev->vduse_dev_fd, vduse_events_handler, NULL, dev); 79151d018fdSMaxime Coquelin if (ret) { 7920e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to add fd %d to vduse fdset", 79351d018fdSMaxime Coquelin dev->vduse_dev_fd); 794a2a05e55SMaxime Coquelin goto out_log_unmap; 79551d018fdSMaxime Coquelin } 79651d018fdSMaxime Coquelin 797da79cc7fSMaxime Coquelin if (reconnect && dev->status & VIRTIO_DEVICE_STATUS_DRIVER_OK) { 798952e4945SMaxime Coquelin ret = vduse_reconnect_start_device(dev); 799952e4945SMaxime Coquelin if (ret) 800a2a05e55SMaxime Coquelin goto out_log_unmap; 801da79cc7fSMaxime Coquelin } 802da79cc7fSMaxime Coquelin 8030adb8eccSMaxime Coquelin return 0; 8040adb8eccSMaxime Coquelin 805a2a05e55SMaxime Coquelin out_log_unmap: 806a2a05e55SMaxime Coquelin munmap(dev->reconnect_log, sizeof(*dev->reconnect_log)); 8070adb8eccSMaxime Coquelin out_dev_destroy: 8080adb8eccSMaxime Coquelin vhost_destroy_device(vid); 8090adb8eccSMaxime Coquelin out_dev_close: 8100adb8eccSMaxime Coquelin if (dev_fd >= 0) 8110adb8eccSMaxime Coquelin close(dev_fd); 8120adb8eccSMaxime Coquelin ioctl(control_fd, VDUSE_DESTROY_DEV, name); 8130adb8eccSMaxime Coquelin out_ctrl_close: 8140adb8eccSMaxime Coquelin close(control_fd); 8150adb8eccSMaxime Coquelin 8160adb8eccSMaxime Coquelin return ret; 8170adb8eccSMaxime Coquelin } 8180adb8eccSMaxime Coquelin 8190adb8eccSMaxime Coquelin int 8200adb8eccSMaxime Coquelin vduse_device_destroy(const char *path) 8210adb8eccSMaxime Coquelin { 8220adb8eccSMaxime Coquelin const char *name = path + strlen("/dev/vduse/"); 8230adb8eccSMaxime Coquelin struct virtio_net *dev; 8240adb8eccSMaxime Coquelin int vid, ret; 8250adb8eccSMaxime Coquelin 8260adb8eccSMaxime Coquelin for (vid = 0; vid < RTE_MAX_VHOST_DEVICE; vid++) { 8270adb8eccSMaxime Coquelin dev = vhost_devices[vid]; 8280adb8eccSMaxime Coquelin 8290adb8eccSMaxime Coquelin if (dev == NULL) 8300adb8eccSMaxime Coquelin continue; 8310adb8eccSMaxime Coquelin 8320adb8eccSMaxime Coquelin if (!strcmp(path, dev->ifname)) 8330adb8eccSMaxime Coquelin break; 8340adb8eccSMaxime Coquelin } 8350adb8eccSMaxime Coquelin 8360adb8eccSMaxime Coquelin if (vid == RTE_MAX_VHOST_DEVICE) 8370adb8eccSMaxime Coquelin return -1; 8380adb8eccSMaxime Coquelin 839da79cc7fSMaxime Coquelin if (dev->reconnect_log) 840da79cc7fSMaxime Coquelin munmap(dev->reconnect_log, sizeof(*dev->reconnect_log)); 841da79cc7fSMaxime Coquelin 842ad67c65eSMaxime Coquelin vduse_device_stop(dev); 843653327e1SMaxime Coquelin 844e68a6feaSMaxime Coquelin fdset_del(vduse.fdset, dev->vduse_dev_fd); 84551d018fdSMaxime Coquelin 8460adb8eccSMaxime Coquelin if (dev->vduse_dev_fd >= 0) { 8470adb8eccSMaxime Coquelin close(dev->vduse_dev_fd); 8480adb8eccSMaxime Coquelin dev->vduse_dev_fd = -1; 8490adb8eccSMaxime Coquelin } 8500adb8eccSMaxime Coquelin 8510adb8eccSMaxime Coquelin if (dev->vduse_ctrl_fd >= 0) { 852da79cc7fSMaxime Coquelin char reconnect_file[PATH_MAX]; 853da79cc7fSMaxime Coquelin 8540adb8eccSMaxime Coquelin ret = ioctl(dev->vduse_ctrl_fd, VDUSE_DESTROY_DEV, name); 855da79cc7fSMaxime Coquelin if (ret) { 8560e21c7c0SDavid Marchand VHOST_CONFIG_LOG(name, ERR, "Failed to destroy VDUSE device: %s", 8570adb8eccSMaxime Coquelin strerror(errno)); 858da79cc7fSMaxime Coquelin } else { 859da79cc7fSMaxime Coquelin /* 860da79cc7fSMaxime Coquelin * VDUSE device was no more attached to the vDPA bus, 861da79cc7fSMaxime Coquelin * so we can remove the reconnect file. 862da79cc7fSMaxime Coquelin */ 863da79cc7fSMaxime Coquelin ret = snprintf(reconnect_file, sizeof(reconnect_file), "%s/%s", 864da79cc7fSMaxime Coquelin vduse_reconnect_dir, name); 865da79cc7fSMaxime Coquelin if (ret < 0 || ret == sizeof(reconnect_file)) 866da79cc7fSMaxime Coquelin VHOST_CONFIG_LOG(name, ERR, 867da79cc7fSMaxime Coquelin "Failed to create vduse reconnect path name"); 868da79cc7fSMaxime Coquelin else 869da79cc7fSMaxime Coquelin unlink(reconnect_file); 870da79cc7fSMaxime Coquelin } 871da79cc7fSMaxime Coquelin 8720adb8eccSMaxime Coquelin close(dev->vduse_ctrl_fd); 8730adb8eccSMaxime Coquelin dev->vduse_ctrl_fd = -1; 8740adb8eccSMaxime Coquelin } 8750adb8eccSMaxime Coquelin 8760adb8eccSMaxime Coquelin vhost_destroy_device(vid); 8770adb8eccSMaxime Coquelin 8780adb8eccSMaxime Coquelin return 0; 8790adb8eccSMaxime Coquelin } 880