15566a3e3SBruce Richardson /* SPDX-License-Identifier: BSD-3-Clause 25566a3e3SBruce Richardson * Copyright(c) 2016 Intel Corporation 3e3b43481SJianfeng Tan */ 4e3b43481SJianfeng Tan 5e3b43481SJianfeng Tan #include <sys/types.h> 6e3b43481SJianfeng Tan #include <sys/stat.h> 7e3b43481SJianfeng Tan #include <fcntl.h> 8e3b43481SJianfeng Tan #include <unistd.h> 906856cabSMaxime Coquelin #include <errno.h> 10e3b43481SJianfeng Tan 11e3b43481SJianfeng Tan #include <rte_memory.h> 12e3b43481SJianfeng Tan 13e3b43481SJianfeng Tan #include "vhost.h" 14e3b43481SJianfeng Tan #include "virtio_user_dev.h" 15e3b43481SJianfeng Tan #include "vhost_kernel_tap.h" 16e3b43481SJianfeng Tan 1786388a3aSMaxime Coquelin struct vhost_kernel_data { 1886388a3aSMaxime Coquelin int *vhostfds; 1986388a3aSMaxime Coquelin int *tapfds; 2086388a3aSMaxime Coquelin }; 2186388a3aSMaxime Coquelin 22e3b43481SJianfeng Tan struct vhost_memory_kernel { 23e3b43481SJianfeng Tan uint32_t nregions; 24e3b43481SJianfeng Tan uint32_t padding; 25e3b43481SJianfeng Tan struct vhost_memory_region regions[0]; 26e3b43481SJianfeng Tan }; 27e3b43481SJianfeng Tan 28e3b43481SJianfeng Tan /* vhost kernel ioctls */ 29e3b43481SJianfeng Tan #define VHOST_VIRTIO 0xAF 30e3b43481SJianfeng Tan #define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64) 31e3b43481SJianfeng Tan #define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64) 32e3b43481SJianfeng Tan #define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01) 33e3b43481SJianfeng Tan #define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02) 34e3b43481SJianfeng Tan #define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel) 35e3b43481SJianfeng Tan #define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64) 36e3b43481SJianfeng Tan #define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int) 37e3b43481SJianfeng Tan #define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state) 38e3b43481SJianfeng Tan #define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr) 39e3b43481SJianfeng Tan #define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state) 40e3b43481SJianfeng Tan #define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state) 41e3b43481SJianfeng Tan #define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file) 42e3b43481SJianfeng Tan #define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file) 43e3b43481SJianfeng Tan #define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) 44e3b43481SJianfeng Tan #define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file) 45e3b43481SJianfeng Tan 46cc0151b3SMaxime Coquelin /* with below features, vhost kernel does not need to do the checksum and TSO, 47cc0151b3SMaxime Coquelin * these info will be passed to virtio_user through virtio net header. 48cc0151b3SMaxime Coquelin */ 49cc0151b3SMaxime Coquelin #define VHOST_KERNEL_GUEST_OFFLOADS_MASK \ 50cc0151b3SMaxime Coquelin ((1ULL << VIRTIO_NET_F_GUEST_CSUM) | \ 51cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ 52cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ 53cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ 54cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_GUEST_UFO)) 55cc0151b3SMaxime Coquelin 56cc0151b3SMaxime Coquelin /* with below features, when flows from virtio_user to vhost kernel 57cc0151b3SMaxime Coquelin * (1) if flows goes up through the kernel networking stack, it does not need 58cc0151b3SMaxime Coquelin * to verify checksum, which can save CPU cycles; 59cc0151b3SMaxime Coquelin * (2) if flows goes through a Linux bridge and outside from an interface 60cc0151b3SMaxime Coquelin * (kernel driver), checksum and TSO will be done by GSO in kernel or even 61cc0151b3SMaxime Coquelin * offloaded into real physical device. 62cc0151b3SMaxime Coquelin */ 63cc0151b3SMaxime Coquelin #define VHOST_KERNEL_HOST_OFFLOADS_MASK \ 64cc0151b3SMaxime Coquelin ((1ULL << VIRTIO_NET_F_HOST_TSO4) | \ 65cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_HOST_TSO6) | \ 66cc0151b3SMaxime Coquelin (1ULL << VIRTIO_NET_F_CSUM)) 67cc0151b3SMaxime Coquelin 68e3b43481SJianfeng Tan static uint64_t max_regions = 64; 69e3b43481SJianfeng Tan 70e3b43481SJianfeng Tan static void 71e3b43481SJianfeng Tan get_vhost_kernel_max_regions(void) 72e3b43481SJianfeng Tan { 73e3b43481SJianfeng Tan int fd; 74e3b43481SJianfeng Tan char buf[20] = {'\0'}; 75e3b43481SJianfeng Tan 76e3b43481SJianfeng Tan fd = open("/sys/module/vhost/parameters/max_mem_regions", O_RDONLY); 77e3b43481SJianfeng Tan if (fd < 0) 78e3b43481SJianfeng Tan return; 79e3b43481SJianfeng Tan 80e3b43481SJianfeng Tan if (read(fd, buf, sizeof(buf) - 1) > 0) 81e3b43481SJianfeng Tan max_regions = strtoull(buf, NULL, 10); 82e3b43481SJianfeng Tan 83e3b43481SJianfeng Tan close(fd); 84e3b43481SJianfeng Tan } 85e3b43481SJianfeng Tan 8606856cabSMaxime Coquelin static int 8706856cabSMaxime Coquelin vhost_kernel_ioctl(int fd, uint64_t request, void *arg) 8806856cabSMaxime Coquelin { 8906856cabSMaxime Coquelin int ret; 9006856cabSMaxime Coquelin 9106856cabSMaxime Coquelin ret = ioctl(fd, request, arg); 9206856cabSMaxime Coquelin if (ret) { 9306856cabSMaxime Coquelin PMD_DRV_LOG(ERR, "Vhost-kernel ioctl %"PRIu64" failed (%s)", 9406856cabSMaxime Coquelin request, strerror(errno)); 9506856cabSMaxime Coquelin return -1; 9606856cabSMaxime Coquelin } 9706856cabSMaxime Coquelin 9806856cabSMaxime Coquelin return 0; 9906856cabSMaxime Coquelin } 10006856cabSMaxime Coquelin 10106856cabSMaxime Coquelin static int 10206856cabSMaxime Coquelin vhost_kernel_set_owner(struct virtio_user_dev *dev) 10306856cabSMaxime Coquelin { 1043c503b24SThierry Herbelot int ret; 1053c503b24SThierry Herbelot uint32_t i; 10686388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 10786388a3aSMaxime Coquelin 1083c503b24SThierry Herbelot for (i = 0; i < dev->max_queue_pairs; ++i) { 1093c503b24SThierry Herbelot if (data->vhostfds[i] < 0) 1103c503b24SThierry Herbelot continue; 1113c503b24SThierry Herbelot 1123c503b24SThierry Herbelot ret = vhost_kernel_ioctl(data->vhostfds[i], VHOST_SET_OWNER, NULL); 1133c503b24SThierry Herbelot if (ret < 0) 1143c503b24SThierry Herbelot return ret; 1153c503b24SThierry Herbelot } 1163c503b24SThierry Herbelot 1173c503b24SThierry Herbelot return 0; 11806856cabSMaxime Coquelin } 11906856cabSMaxime Coquelin 120cc0151b3SMaxime Coquelin static int 121cc0151b3SMaxime Coquelin vhost_kernel_get_features(struct virtio_user_dev *dev, uint64_t *features) 122cc0151b3SMaxime Coquelin { 123cc0151b3SMaxime Coquelin int ret; 124cc0151b3SMaxime Coquelin unsigned int tap_features; 12586388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 126cc0151b3SMaxime Coquelin 12786388a3aSMaxime Coquelin ret = vhost_kernel_ioctl(data->vhostfds[0], VHOST_GET_FEATURES, features); 128cc0151b3SMaxime Coquelin if (ret < 0) { 129cc0151b3SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get features"); 130cc0151b3SMaxime Coquelin return -1; 131cc0151b3SMaxime Coquelin } 132cc0151b3SMaxime Coquelin 133cc0151b3SMaxime Coquelin ret = tap_support_features(&tap_features); 134cc0151b3SMaxime Coquelin if (ret < 0) { 135cc0151b3SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get TAP features"); 136cc0151b3SMaxime Coquelin return -1; 137cc0151b3SMaxime Coquelin } 138cc0151b3SMaxime Coquelin 139cc0151b3SMaxime Coquelin /* with tap as the backend, all these features are supported 140cc0151b3SMaxime Coquelin * but not claimed by vhost-net, so we add them back when 141cc0151b3SMaxime Coquelin * reporting to upper layer. 142cc0151b3SMaxime Coquelin */ 143cc0151b3SMaxime Coquelin if (tap_features & IFF_VNET_HDR) { 144cc0151b3SMaxime Coquelin *features |= VHOST_KERNEL_GUEST_OFFLOADS_MASK; 145cc0151b3SMaxime Coquelin *features |= VHOST_KERNEL_HOST_OFFLOADS_MASK; 146cc0151b3SMaxime Coquelin } 147cc0151b3SMaxime Coquelin 148cc0151b3SMaxime Coquelin /* vhost_kernel will not declare this feature, but it does 149cc0151b3SMaxime Coquelin * support multi-queue. 150cc0151b3SMaxime Coquelin */ 151cc0151b3SMaxime Coquelin if (tap_features & IFF_MULTI_QUEUE) 152cc0151b3SMaxime Coquelin *features |= (1ull << VIRTIO_NET_F_MQ); 153cc0151b3SMaxime Coquelin 154cc0151b3SMaxime Coquelin return 0; 155cc0151b3SMaxime Coquelin } 156cc0151b3SMaxime Coquelin 157cc0151b3SMaxime Coquelin static int 158cc0151b3SMaxime Coquelin vhost_kernel_set_features(struct virtio_user_dev *dev, uint64_t features) 159cc0151b3SMaxime Coquelin { 16086388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 161*cf412ff7SThierry Herbelot uint32_t i; 162*cf412ff7SThierry Herbelot int ret; 16386388a3aSMaxime Coquelin 164cc0151b3SMaxime Coquelin /* We don't need memory protection here */ 165cc0151b3SMaxime Coquelin features &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM); 166cc0151b3SMaxime Coquelin /* VHOST kernel does not know about below flags */ 167cc0151b3SMaxime Coquelin features &= ~VHOST_KERNEL_GUEST_OFFLOADS_MASK; 168cc0151b3SMaxime Coquelin features &= ~VHOST_KERNEL_HOST_OFFLOADS_MASK; 169cc0151b3SMaxime Coquelin features &= ~(1ULL << VIRTIO_NET_F_MQ); 170cc0151b3SMaxime Coquelin 171*cf412ff7SThierry Herbelot for (i = 0; i < dev->max_queue_pairs; ++i) { 172*cf412ff7SThierry Herbelot if (data->vhostfds[i] < 0) 173*cf412ff7SThierry Herbelot continue; 174*cf412ff7SThierry Herbelot 175*cf412ff7SThierry Herbelot ret = vhost_kernel_ioctl(data->vhostfds[i], VHOST_SET_FEATURES, &features); 176*cf412ff7SThierry Herbelot if (ret < 0) 177*cf412ff7SThierry Herbelot return ret; 178*cf412ff7SThierry Herbelot } 179*cf412ff7SThierry Herbelot 180*cf412ff7SThierry Herbelot return 0; 181cc0151b3SMaxime Coquelin } 182cc0151b3SMaxime Coquelin 183746c346dSAnatoly Burakov static int 18475728654STiwei Bie add_memseg_list(const struct rte_memseg_list *msl, void *arg) 185746c346dSAnatoly Burakov { 18675728654STiwei Bie struct vhost_memory_kernel *vm = arg; 187746c346dSAnatoly Burakov struct vhost_memory_region *mr; 188746c346dSAnatoly Burakov void *start_addr; 18975728654STiwei Bie uint64_t len; 190746c346dSAnatoly Burakov 1915282bb1cSAnatoly Burakov if (msl->external) 1925282bb1cSAnatoly Burakov return 0; 1935282bb1cSAnatoly Burakov 19475728654STiwei Bie if (vm->nregions >= max_regions) 195746c346dSAnatoly Burakov return -1; 196746c346dSAnatoly Burakov 19775728654STiwei Bie start_addr = msl->base_va; 19875728654STiwei Bie len = msl->page_sz * msl->memseg_arr.len; 19975728654STiwei Bie 20075728654STiwei Bie mr = &vm->regions[vm->nregions++]; 201746c346dSAnatoly Burakov 202746c346dSAnatoly Burakov mr->guest_phys_addr = (uint64_t)(uintptr_t)start_addr; 203746c346dSAnatoly Burakov mr->userspace_addr = (uint64_t)(uintptr_t)start_addr; 204746c346dSAnatoly Burakov mr->memory_size = len; 20575728654STiwei Bie mr->mmap_offset = 0; /* flags_padding */ 20675728654STiwei Bie 20775728654STiwei Bie PMD_DRV_LOG(DEBUG, "index=%u addr=%p len=%" PRIu64, 20875728654STiwei Bie vm->nregions - 1, start_addr, len); 209746c346dSAnatoly Burakov 210746c346dSAnatoly Burakov return 0; 211746c346dSAnatoly Burakov } 212746c346dSAnatoly Burakov 21375728654STiwei Bie /* By default, vhost kernel module allows 64 regions, but DPDK may 21475728654STiwei Bie * have much more memory regions. Below function will treat each 21575728654STiwei Bie * contiguous memory space reserved by DPDK as one region. 216e3b43481SJianfeng Tan */ 217539d910cSMaxime Coquelin static int 218539d910cSMaxime Coquelin vhost_kernel_set_memory_table(struct virtio_user_dev *dev) 219e3b43481SJianfeng Tan { 220b01e1e9aSThierry Herbelot uint32_t i; 22186388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 222e3b43481SJianfeng Tan struct vhost_memory_kernel *vm; 223539d910cSMaxime Coquelin int ret; 224e3b43481SJianfeng Tan 225e3b43481SJianfeng Tan vm = malloc(sizeof(struct vhost_memory_kernel) + 226e3b43481SJianfeng Tan max_regions * 227e3b43481SJianfeng Tan sizeof(struct vhost_memory_region)); 2281e9057a9SJianfeng Tan if (!vm) 229539d910cSMaxime Coquelin goto err; 230e3b43481SJianfeng Tan 23175728654STiwei Bie vm->nregions = 0; 23275728654STiwei Bie vm->padding = 0; 233e3b43481SJianfeng Tan 2347ff26957STiwei Bie /* 2357ff26957STiwei Bie * The memory lock has already been taken by memory subsystem 2367ff26957STiwei Bie * or virtio_user_start_device(). 2377ff26957STiwei Bie */ 238539d910cSMaxime Coquelin ret = rte_memseg_list_walk_thread_unsafe(add_memseg_list, vm); 239539d910cSMaxime Coquelin if (ret < 0) 240539d910cSMaxime Coquelin goto err_free; 241539d910cSMaxime Coquelin 242b01e1e9aSThierry Herbelot for (i = 0; i < dev->max_queue_pairs; ++i) { 243b01e1e9aSThierry Herbelot if (data->vhostfds[i] < 0) 244b01e1e9aSThierry Herbelot continue; 245b01e1e9aSThierry Herbelot 246b01e1e9aSThierry Herbelot ret = vhost_kernel_ioctl(data->vhostfds[i], VHOST_SET_MEM_TABLE, vm); 247539d910cSMaxime Coquelin if (ret < 0) 248539d910cSMaxime Coquelin goto err_free; 249b01e1e9aSThierry Herbelot } 250539d910cSMaxime Coquelin 251e3b43481SJianfeng Tan free(vm); 252539d910cSMaxime Coquelin 253539d910cSMaxime Coquelin return 0; 254539d910cSMaxime Coquelin err_free: 255539d910cSMaxime Coquelin free(vm); 256539d910cSMaxime Coquelin err: 257539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set memory table"); 258539d910cSMaxime Coquelin return -1; 259e3b43481SJianfeng Tan } 260e3b43481SJianfeng Tan 261ab9098d2SMaxime Coquelin static int 262ab9098d2SMaxime Coquelin vhost_kernel_set_vring(struct virtio_user_dev *dev, uint64_t req, struct vhost_vring_state *state) 263ab9098d2SMaxime Coquelin { 264ab9098d2SMaxime Coquelin int ret, fd; 265ab9098d2SMaxime Coquelin unsigned int index = state->index; 26686388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 267ab9098d2SMaxime Coquelin 268ab9098d2SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 26986388a3aSMaxime Coquelin fd = data->vhostfds[state->index / 2]; 270ab9098d2SMaxime Coquelin state->index %= 2; 271ab9098d2SMaxime Coquelin 272ab9098d2SMaxime Coquelin ret = vhost_kernel_ioctl(fd, req, state); 273ab9098d2SMaxime Coquelin if (ret < 0) { 274ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring (request %" PRIu64 ")", req); 275ab9098d2SMaxime Coquelin return -1; 276ab9098d2SMaxime Coquelin } 277ab9098d2SMaxime Coquelin 278ab9098d2SMaxime Coquelin /* restore index back to queue index */ 279ab9098d2SMaxime Coquelin state->index = index; 280ab9098d2SMaxime Coquelin 281ab9098d2SMaxime Coquelin return 0; 282ab9098d2SMaxime Coquelin } 283ab9098d2SMaxime Coquelin 284ab9098d2SMaxime Coquelin static int 285ab9098d2SMaxime Coquelin vhost_kernel_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state) 286ab9098d2SMaxime Coquelin { 287ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_SET_VRING_NUM, state); 288ab9098d2SMaxime Coquelin } 289ab9098d2SMaxime Coquelin 290ab9098d2SMaxime Coquelin static int 291ab9098d2SMaxime Coquelin vhost_kernel_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 292ab9098d2SMaxime Coquelin { 293ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_SET_VRING_BASE, state); 294ab9098d2SMaxime Coquelin } 295ab9098d2SMaxime Coquelin 296ab9098d2SMaxime Coquelin static int 297ab9098d2SMaxime Coquelin vhost_kernel_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 298ab9098d2SMaxime Coquelin { 299ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_GET_VRING_BASE, state); 300ab9098d2SMaxime Coquelin } 301ab9098d2SMaxime Coquelin 302ce399c36SMaxime Coquelin static int 303ce399c36SMaxime Coquelin vhost_kernel_set_vring_file(struct virtio_user_dev *dev, uint64_t req, 304ce399c36SMaxime Coquelin struct vhost_vring_file *file) 305ce399c36SMaxime Coquelin { 306ce399c36SMaxime Coquelin int ret, fd; 307ce399c36SMaxime Coquelin unsigned int index = file->index; 30886388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 309ce399c36SMaxime Coquelin 310ce399c36SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 31186388a3aSMaxime Coquelin fd = data->vhostfds[file->index / 2]; 312ce399c36SMaxime Coquelin file->index %= 2; 313ce399c36SMaxime Coquelin 314ce399c36SMaxime Coquelin ret = vhost_kernel_ioctl(fd, req, file); 315ce399c36SMaxime Coquelin if (ret < 0) { 316ce399c36SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring file (request %" PRIu64 ")", req); 317ce399c36SMaxime Coquelin return -1; 318ce399c36SMaxime Coquelin } 319ce399c36SMaxime Coquelin 320ce399c36SMaxime Coquelin /* restore index back to queue index */ 321ce399c36SMaxime Coquelin file->index = index; 322ce399c36SMaxime Coquelin 323ce399c36SMaxime Coquelin return 0; 324ce399c36SMaxime Coquelin } 325ce399c36SMaxime Coquelin 326ce399c36SMaxime Coquelin static int 327ce399c36SMaxime Coquelin vhost_kernel_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file) 328ce399c36SMaxime Coquelin { 329ce399c36SMaxime Coquelin return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_KICK, file); 330ce399c36SMaxime Coquelin } 331ce399c36SMaxime Coquelin 332ce399c36SMaxime Coquelin static int 333ce399c36SMaxime Coquelin vhost_kernel_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file) 334ce399c36SMaxime Coquelin { 335ce399c36SMaxime Coquelin return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_CALL, file); 336ce399c36SMaxime Coquelin } 337ce399c36SMaxime Coquelin 338dc65db73SMaxime Coquelin static int 339dc65db73SMaxime Coquelin vhost_kernel_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr) 340dc65db73SMaxime Coquelin { 341dc65db73SMaxime Coquelin int ret, fd; 342dc65db73SMaxime Coquelin unsigned int index = addr->index; 34386388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 344dc65db73SMaxime Coquelin 345dc65db73SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 34686388a3aSMaxime Coquelin fd = data->vhostfds[addr->index / 2]; 347dc65db73SMaxime Coquelin addr->index %= 2; 348dc65db73SMaxime Coquelin 349dc65db73SMaxime Coquelin ret = vhost_kernel_ioctl(fd, VHOST_SET_VRING_ADDR, addr); 350dc65db73SMaxime Coquelin if (ret < 0) { 351dc65db73SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring address"); 352dc65db73SMaxime Coquelin return -1; 353dc65db73SMaxime Coquelin } 354dc65db73SMaxime Coquelin 355dc65db73SMaxime Coquelin /* restore index back to queue index */ 356dc65db73SMaxime Coquelin addr->index = index; 357dc65db73SMaxime Coquelin 358dc65db73SMaxime Coquelin return 0; 359dc65db73SMaxime Coquelin } 360dc65db73SMaxime Coquelin 3618723c894SMaxime Coquelin static int 3628723c894SMaxime Coquelin vhost_kernel_get_status(struct virtio_user_dev *dev __rte_unused, uint8_t *status __rte_unused) 3638723c894SMaxime Coquelin { 3648723c894SMaxime Coquelin return -ENOTSUP; 3658723c894SMaxime Coquelin } 3668723c894SMaxime Coquelin 3678723c894SMaxime Coquelin static int 3688723c894SMaxime Coquelin vhost_kernel_set_status(struct virtio_user_dev *dev __rte_unused, uint8_t status __rte_unused) 3698723c894SMaxime Coquelin { 3708723c894SMaxime Coquelin return -ENOTSUP; 3718723c894SMaxime Coquelin } 3728723c894SMaxime Coquelin 373e3b43481SJianfeng Tan /** 374e3b43481SJianfeng Tan * Set up environment to talk with a vhost kernel backend. 375e3b43481SJianfeng Tan * 376e3b43481SJianfeng Tan * @return 377e3b43481SJianfeng Tan * - (-1) if fail to set up; 378e3b43481SJianfeng Tan * - (>=0) if successful. 379e3b43481SJianfeng Tan */ 380e3b43481SJianfeng Tan static int 381e3b43481SJianfeng Tan vhost_kernel_setup(struct virtio_user_dev *dev) 382e3b43481SJianfeng Tan { 383e3b43481SJianfeng Tan int vhostfd; 38486388a3aSMaxime Coquelin uint32_t q, i; 38586388a3aSMaxime Coquelin struct vhost_kernel_data *data; 38686388a3aSMaxime Coquelin 38786388a3aSMaxime Coquelin data = malloc(sizeof(*data)); 38886388a3aSMaxime Coquelin if (!data) { 38986388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost-kernel data", dev->path); 39086388a3aSMaxime Coquelin return -1; 39186388a3aSMaxime Coquelin } 39286388a3aSMaxime Coquelin 39386388a3aSMaxime Coquelin data->vhostfds = malloc(dev->max_queue_pairs * sizeof(int)); 39486388a3aSMaxime Coquelin if (!data->vhostfds) { 39586388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost FDs", dev->path); 39686388a3aSMaxime Coquelin goto err_data; 39786388a3aSMaxime Coquelin } 39886388a3aSMaxime Coquelin data->tapfds = malloc(dev->max_queue_pairs * sizeof(int)); 39986388a3aSMaxime Coquelin if (!data->tapfds) { 40086388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate TAP FDs", dev->path); 40186388a3aSMaxime Coquelin goto err_vhostfds; 40286388a3aSMaxime Coquelin } 40386388a3aSMaxime Coquelin 40486388a3aSMaxime Coquelin for (q = 0; q < dev->max_queue_pairs; ++q) { 40586388a3aSMaxime Coquelin data->vhostfds[q] = -1; 40686388a3aSMaxime Coquelin data->tapfds[q] = -1; 40786388a3aSMaxime Coquelin } 408e3b43481SJianfeng Tan 409e3b43481SJianfeng Tan get_vhost_kernel_max_regions(); 410e3b43481SJianfeng Tan 411e3b43481SJianfeng Tan for (i = 0; i < dev->max_queue_pairs; ++i) { 412e3b43481SJianfeng Tan vhostfd = open(dev->path, O_RDWR); 413e3b43481SJianfeng Tan if (vhostfd < 0) { 41486388a3aSMaxime Coquelin PMD_DRV_LOG(ERR, "fail to open %s, %s", dev->path, strerror(errno)); 41586388a3aSMaxime Coquelin goto err_tapfds; 41686388a3aSMaxime Coquelin } 41786388a3aSMaxime Coquelin 41886388a3aSMaxime Coquelin data->vhostfds[i] = vhostfd; 41986388a3aSMaxime Coquelin } 42086388a3aSMaxime Coquelin 42186388a3aSMaxime Coquelin dev->backend_data = data; 42286388a3aSMaxime Coquelin 42386388a3aSMaxime Coquelin return 0; 42486388a3aSMaxime Coquelin 42586388a3aSMaxime Coquelin err_tapfds: 42686388a3aSMaxime Coquelin for (i = 0; i < dev->max_queue_pairs; i++) 42786388a3aSMaxime Coquelin if (data->vhostfds[i] >= 0) 42886388a3aSMaxime Coquelin close(data->vhostfds[i]); 42986388a3aSMaxime Coquelin 43086388a3aSMaxime Coquelin free(data->tapfds); 43186388a3aSMaxime Coquelin err_vhostfds: 43286388a3aSMaxime Coquelin free(data->vhostfds); 43386388a3aSMaxime Coquelin err_data: 43486388a3aSMaxime Coquelin free(data); 43586388a3aSMaxime Coquelin 436e3b43481SJianfeng Tan return -1; 437e3b43481SJianfeng Tan } 438e3b43481SJianfeng Tan 439e3b43481SJianfeng Tan static int 44086388a3aSMaxime Coquelin vhost_kernel_destroy(struct virtio_user_dev *dev) 441748e5ea5SMaxime Coquelin { 44286388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 44386388a3aSMaxime Coquelin uint32_t i; 44486388a3aSMaxime Coquelin 44586388a3aSMaxime Coquelin if (!data) 44686388a3aSMaxime Coquelin return 0; 44786388a3aSMaxime Coquelin 44886388a3aSMaxime Coquelin for (i = 0; i < dev->max_queue_pairs; ++i) { 44986388a3aSMaxime Coquelin if (data->vhostfds[i] >= 0) 45086388a3aSMaxime Coquelin close(data->vhostfds[i]); 45186388a3aSMaxime Coquelin if (data->tapfds[i] >= 0) 45286388a3aSMaxime Coquelin close(data->tapfds[i]); 45386388a3aSMaxime Coquelin } 45486388a3aSMaxime Coquelin 45586388a3aSMaxime Coquelin free(data->vhostfds); 45686388a3aSMaxime Coquelin free(data->tapfds); 45786388a3aSMaxime Coquelin free(data); 45886388a3aSMaxime Coquelin dev->backend_data = NULL; 45986388a3aSMaxime Coquelin 460748e5ea5SMaxime Coquelin return 0; 461748e5ea5SMaxime Coquelin } 462748e5ea5SMaxime Coquelin 463748e5ea5SMaxime Coquelin static int 464e3b43481SJianfeng Tan vhost_kernel_set_backend(int vhostfd, int tapfd) 465e3b43481SJianfeng Tan { 466e3b43481SJianfeng Tan struct vhost_vring_file f; 467e3b43481SJianfeng Tan 468e3b43481SJianfeng Tan f.fd = tapfd; 469e3b43481SJianfeng Tan f.index = 0; 470e3b43481SJianfeng Tan if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) { 471e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s", 472e3b43481SJianfeng Tan strerror(errno)); 473e3b43481SJianfeng Tan return -1; 474e3b43481SJianfeng Tan } 475e3b43481SJianfeng Tan 476e3b43481SJianfeng Tan f.index = 1; 477e3b43481SJianfeng Tan if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) { 478e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s", 479e3b43481SJianfeng Tan strerror(errno)); 480e3b43481SJianfeng Tan return -1; 481e3b43481SJianfeng Tan } 482e3b43481SJianfeng Tan 483e3b43481SJianfeng Tan return 0; 484e3b43481SJianfeng Tan } 485e3b43481SJianfeng Tan 486e3b43481SJianfeng Tan static int 487e3b43481SJianfeng Tan vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev, 488e3b43481SJianfeng Tan uint16_t pair_idx, 489e3b43481SJianfeng Tan int enable) 490e3b43481SJianfeng Tan { 491e3b43481SJianfeng Tan int hdr_size; 492e3b43481SJianfeng Tan int vhostfd; 493e3b43481SJianfeng Tan int tapfd; 494be7a4707SJianfeng Tan int req_mq = (dev->max_queue_pairs > 1); 49586388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 496e3b43481SJianfeng Tan 49786388a3aSMaxime Coquelin vhostfd = data->vhostfds[pair_idx]; 498e3b43481SJianfeng Tan 49947ac9661STiwei Bie if (dev->qp_enabled[pair_idx] == enable) 500e3b43481SJianfeng Tan return 0; 50147ac9661STiwei Bie 50247ac9661STiwei Bie if (!enable) { 50386388a3aSMaxime Coquelin tapfd = data->tapfds[pair_idx]; 50447ac9661STiwei Bie if (vhost_kernel_set_backend(vhostfd, -1) < 0) { 50547ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel"); 50647ac9661STiwei Bie return -1; 50747ac9661STiwei Bie } 50847ac9661STiwei Bie if (req_mq && vhost_kernel_tap_set_queue(tapfd, false) < 0) { 50947ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to disable tap for vhost kernel"); 51047ac9661STiwei Bie return -1; 51147ac9661STiwei Bie } 51247ac9661STiwei Bie dev->qp_enabled[pair_idx] = false; 51347ac9661STiwei Bie return 0; 51447ac9661STiwei Bie } 51547ac9661STiwei Bie 51686388a3aSMaxime Coquelin if (data->tapfds[pair_idx] >= 0) { 51786388a3aSMaxime Coquelin tapfd = data->tapfds[pair_idx]; 51847ac9661STiwei Bie if (vhost_kernel_tap_set_offload(tapfd, dev->features) == -1) 51947ac9661STiwei Bie return -1; 52047ac9661STiwei Bie if (req_mq && vhost_kernel_tap_set_queue(tapfd, true) < 0) { 52147ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to enable tap for vhost kernel"); 52247ac9661STiwei Bie return -1; 52347ac9661STiwei Bie } 52447ac9661STiwei Bie goto set_backend; 525e3b43481SJianfeng Tan } 526e3b43481SJianfeng Tan 527e3b43481SJianfeng Tan if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) || 528e3b43481SJianfeng Tan (dev->features & (1ULL << VIRTIO_F_VERSION_1))) 529e3b43481SJianfeng Tan hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf); 530e3b43481SJianfeng Tan else 531e3b43481SJianfeng Tan hdr_size = sizeof(struct virtio_net_hdr); 532e3b43481SJianfeng Tan 533791b43e0SNing Li tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq, 5341db4d233SEric Zhang (char *)dev->mac_addr, dev->features); 535e3b43481SJianfeng Tan if (tapfd < 0) { 536e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel"); 537e3b43481SJianfeng Tan return -1; 538e3b43481SJianfeng Tan } 539e3b43481SJianfeng Tan 54086388a3aSMaxime Coquelin data->tapfds[pair_idx] = tapfd; 54147ac9661STiwei Bie 54247ac9661STiwei Bie set_backend: 543e3b43481SJianfeng Tan if (vhost_kernel_set_backend(vhostfd, tapfd) < 0) { 544e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel"); 545e3b43481SJianfeng Tan return -1; 546e3b43481SJianfeng Tan } 547e3b43481SJianfeng Tan 54847ac9661STiwei Bie dev->qp_enabled[pair_idx] = true; 549e3b43481SJianfeng Tan return 0; 550e3b43481SJianfeng Tan } 551e3b43481SJianfeng Tan 5525b75b63cSMaxime Coquelin static int 5535b75b63cSMaxime Coquelin vhost_kernel_get_backend_features(uint64_t *features) 5545b75b63cSMaxime Coquelin { 5555b75b63cSMaxime Coquelin *features = 0; 5565b75b63cSMaxime Coquelin 5575b75b63cSMaxime Coquelin return 0; 5585b75b63cSMaxime Coquelin } 5595b75b63cSMaxime Coquelin 56094973531SMaxime Coquelin static int 56194973531SMaxime Coquelin vhost_kernel_update_link_state(struct virtio_user_dev *dev __rte_unused) 56294973531SMaxime Coquelin { 56394973531SMaxime Coquelin /* Nothing to update (Maybe get TAP interface link state?) */ 56494973531SMaxime Coquelin return 0; 56594973531SMaxime Coquelin } 56694973531SMaxime Coquelin 56794973531SMaxime Coquelin static int 56894973531SMaxime Coquelin vhost_kernel_get_intr_fd(struct virtio_user_dev *dev __rte_unused) 56994973531SMaxime Coquelin { 57094973531SMaxime Coquelin /* No link state interrupt with Vhost-kernel */ 57194973531SMaxime Coquelin return -1; 57294973531SMaxime Coquelin } 57394973531SMaxime Coquelin 574520dd992SFerruh Yigit struct virtio_user_backend_ops virtio_ops_kernel = { 575e3b43481SJianfeng Tan .setup = vhost_kernel_setup, 576748e5ea5SMaxime Coquelin .destroy = vhost_kernel_destroy, 5775b75b63cSMaxime Coquelin .get_backend_features = vhost_kernel_get_backend_features, 57806856cabSMaxime Coquelin .set_owner = vhost_kernel_set_owner, 579cc0151b3SMaxime Coquelin .get_features = vhost_kernel_get_features, 580cc0151b3SMaxime Coquelin .set_features = vhost_kernel_set_features, 581539d910cSMaxime Coquelin .set_memory_table = vhost_kernel_set_memory_table, 582ab9098d2SMaxime Coquelin .set_vring_num = vhost_kernel_set_vring_num, 583ab9098d2SMaxime Coquelin .set_vring_base = vhost_kernel_set_vring_base, 584ab9098d2SMaxime Coquelin .get_vring_base = vhost_kernel_get_vring_base, 585ce399c36SMaxime Coquelin .set_vring_call = vhost_kernel_set_vring_call, 586ce399c36SMaxime Coquelin .set_vring_kick = vhost_kernel_set_vring_kick, 587dc65db73SMaxime Coquelin .set_vring_addr = vhost_kernel_set_vring_addr, 5888723c894SMaxime Coquelin .get_status = vhost_kernel_get_status, 5898723c894SMaxime Coquelin .set_status = vhost_kernel_set_status, 59094973531SMaxime Coquelin .enable_qp = vhost_kernel_enable_queue_pair, 59194973531SMaxime Coquelin .update_link_state = vhost_kernel_update_link_state, 59294973531SMaxime Coquelin .get_intr_fd = vhost_kernel_get_intr_fd, 593e3b43481SJianfeng Tan }; 594