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 17*86388a3aSMaxime Coquelin struct vhost_kernel_data { 18*86388a3aSMaxime Coquelin int *vhostfds; 19*86388a3aSMaxime Coquelin int *tapfds; 20*86388a3aSMaxime Coquelin }; 21*86388a3aSMaxime 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 { 104*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 105*86388a3aSMaxime Coquelin 106*86388a3aSMaxime Coquelin return vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_OWNER, NULL); 10706856cabSMaxime Coquelin } 10806856cabSMaxime Coquelin 109cc0151b3SMaxime Coquelin static int 110cc0151b3SMaxime Coquelin vhost_kernel_get_features(struct virtio_user_dev *dev, uint64_t *features) 111cc0151b3SMaxime Coquelin { 112cc0151b3SMaxime Coquelin int ret; 113cc0151b3SMaxime Coquelin unsigned int tap_features; 114*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 115cc0151b3SMaxime Coquelin 116*86388a3aSMaxime Coquelin ret = vhost_kernel_ioctl(data->vhostfds[0], VHOST_GET_FEATURES, features); 117cc0151b3SMaxime Coquelin if (ret < 0) { 118cc0151b3SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get features"); 119cc0151b3SMaxime Coquelin return -1; 120cc0151b3SMaxime Coquelin } 121cc0151b3SMaxime Coquelin 122cc0151b3SMaxime Coquelin ret = tap_support_features(&tap_features); 123cc0151b3SMaxime Coquelin if (ret < 0) { 124cc0151b3SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to get TAP features"); 125cc0151b3SMaxime Coquelin return -1; 126cc0151b3SMaxime Coquelin } 127cc0151b3SMaxime Coquelin 128cc0151b3SMaxime Coquelin /* with tap as the backend, all these features are supported 129cc0151b3SMaxime Coquelin * but not claimed by vhost-net, so we add them back when 130cc0151b3SMaxime Coquelin * reporting to upper layer. 131cc0151b3SMaxime Coquelin */ 132cc0151b3SMaxime Coquelin if (tap_features & IFF_VNET_HDR) { 133cc0151b3SMaxime Coquelin *features |= VHOST_KERNEL_GUEST_OFFLOADS_MASK; 134cc0151b3SMaxime Coquelin *features |= VHOST_KERNEL_HOST_OFFLOADS_MASK; 135cc0151b3SMaxime Coquelin } 136cc0151b3SMaxime Coquelin 137cc0151b3SMaxime Coquelin /* vhost_kernel will not declare this feature, but it does 138cc0151b3SMaxime Coquelin * support multi-queue. 139cc0151b3SMaxime Coquelin */ 140cc0151b3SMaxime Coquelin if (tap_features & IFF_MULTI_QUEUE) 141cc0151b3SMaxime Coquelin *features |= (1ull << VIRTIO_NET_F_MQ); 142cc0151b3SMaxime Coquelin 143cc0151b3SMaxime Coquelin return 0; 144cc0151b3SMaxime Coquelin } 145cc0151b3SMaxime Coquelin 146cc0151b3SMaxime Coquelin static int 147cc0151b3SMaxime Coquelin vhost_kernel_set_features(struct virtio_user_dev *dev, uint64_t features) 148cc0151b3SMaxime Coquelin { 149*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 150*86388a3aSMaxime Coquelin 151cc0151b3SMaxime Coquelin /* We don't need memory protection here */ 152cc0151b3SMaxime Coquelin features &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM); 153cc0151b3SMaxime Coquelin /* VHOST kernel does not know about below flags */ 154cc0151b3SMaxime Coquelin features &= ~VHOST_KERNEL_GUEST_OFFLOADS_MASK; 155cc0151b3SMaxime Coquelin features &= ~VHOST_KERNEL_HOST_OFFLOADS_MASK; 156cc0151b3SMaxime Coquelin features &= ~(1ULL << VIRTIO_NET_F_MQ); 157cc0151b3SMaxime Coquelin 158*86388a3aSMaxime Coquelin return vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_FEATURES, &features); 159cc0151b3SMaxime Coquelin } 160cc0151b3SMaxime Coquelin 161746c346dSAnatoly Burakov static int 16275728654STiwei Bie add_memseg_list(const struct rte_memseg_list *msl, void *arg) 163746c346dSAnatoly Burakov { 16475728654STiwei Bie struct vhost_memory_kernel *vm = arg; 165746c346dSAnatoly Burakov struct vhost_memory_region *mr; 166746c346dSAnatoly Burakov void *start_addr; 16775728654STiwei Bie uint64_t len; 168746c346dSAnatoly Burakov 1695282bb1cSAnatoly Burakov if (msl->external) 1705282bb1cSAnatoly Burakov return 0; 1715282bb1cSAnatoly Burakov 17275728654STiwei Bie if (vm->nregions >= max_regions) 173746c346dSAnatoly Burakov return -1; 174746c346dSAnatoly Burakov 17575728654STiwei Bie start_addr = msl->base_va; 17675728654STiwei Bie len = msl->page_sz * msl->memseg_arr.len; 17775728654STiwei Bie 17875728654STiwei Bie mr = &vm->regions[vm->nregions++]; 179746c346dSAnatoly Burakov 180746c346dSAnatoly Burakov mr->guest_phys_addr = (uint64_t)(uintptr_t)start_addr; 181746c346dSAnatoly Burakov mr->userspace_addr = (uint64_t)(uintptr_t)start_addr; 182746c346dSAnatoly Burakov mr->memory_size = len; 18375728654STiwei Bie mr->mmap_offset = 0; /* flags_padding */ 18475728654STiwei Bie 18575728654STiwei Bie PMD_DRV_LOG(DEBUG, "index=%u addr=%p len=%" PRIu64, 18675728654STiwei Bie vm->nregions - 1, start_addr, len); 187746c346dSAnatoly Burakov 188746c346dSAnatoly Burakov return 0; 189746c346dSAnatoly Burakov } 190746c346dSAnatoly Burakov 19175728654STiwei Bie /* By default, vhost kernel module allows 64 regions, but DPDK may 19275728654STiwei Bie * have much more memory regions. Below function will treat each 19375728654STiwei Bie * contiguous memory space reserved by DPDK as one region. 194e3b43481SJianfeng Tan */ 195539d910cSMaxime Coquelin static int 196539d910cSMaxime Coquelin vhost_kernel_set_memory_table(struct virtio_user_dev *dev) 197e3b43481SJianfeng Tan { 198*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 199e3b43481SJianfeng Tan struct vhost_memory_kernel *vm; 200539d910cSMaxime Coquelin int ret; 201e3b43481SJianfeng Tan 202e3b43481SJianfeng Tan vm = malloc(sizeof(struct vhost_memory_kernel) + 203e3b43481SJianfeng Tan max_regions * 204e3b43481SJianfeng Tan sizeof(struct vhost_memory_region)); 2051e9057a9SJianfeng Tan if (!vm) 206539d910cSMaxime Coquelin goto err; 207e3b43481SJianfeng Tan 20875728654STiwei Bie vm->nregions = 0; 20975728654STiwei Bie vm->padding = 0; 210e3b43481SJianfeng Tan 2117ff26957STiwei Bie /* 2127ff26957STiwei Bie * The memory lock has already been taken by memory subsystem 2137ff26957STiwei Bie * or virtio_user_start_device(). 2147ff26957STiwei Bie */ 215539d910cSMaxime Coquelin ret = rte_memseg_list_walk_thread_unsafe(add_memseg_list, vm); 216539d910cSMaxime Coquelin if (ret < 0) 217539d910cSMaxime Coquelin goto err_free; 218539d910cSMaxime Coquelin 219*86388a3aSMaxime Coquelin ret = vhost_kernel_ioctl(data->vhostfds[0], VHOST_SET_MEM_TABLE, vm); 220539d910cSMaxime Coquelin if (ret < 0) 221539d910cSMaxime Coquelin goto err_free; 222539d910cSMaxime Coquelin 223e3b43481SJianfeng Tan free(vm); 224539d910cSMaxime Coquelin 225539d910cSMaxime Coquelin return 0; 226539d910cSMaxime Coquelin err_free: 227539d910cSMaxime Coquelin free(vm); 228539d910cSMaxime Coquelin err: 229539d910cSMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set memory table"); 230539d910cSMaxime Coquelin return -1; 231e3b43481SJianfeng Tan } 232e3b43481SJianfeng Tan 233ab9098d2SMaxime Coquelin static int 234ab9098d2SMaxime Coquelin vhost_kernel_set_vring(struct virtio_user_dev *dev, uint64_t req, struct vhost_vring_state *state) 235ab9098d2SMaxime Coquelin { 236ab9098d2SMaxime Coquelin int ret, fd; 237ab9098d2SMaxime Coquelin unsigned int index = state->index; 238*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 239ab9098d2SMaxime Coquelin 240ab9098d2SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 241*86388a3aSMaxime Coquelin fd = data->vhostfds[state->index / 2]; 242ab9098d2SMaxime Coquelin state->index %= 2; 243ab9098d2SMaxime Coquelin 244ab9098d2SMaxime Coquelin ret = vhost_kernel_ioctl(fd, req, state); 245ab9098d2SMaxime Coquelin if (ret < 0) { 246ab9098d2SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring (request %" PRIu64 ")", req); 247ab9098d2SMaxime Coquelin return -1; 248ab9098d2SMaxime Coquelin } 249ab9098d2SMaxime Coquelin 250ab9098d2SMaxime Coquelin /* restore index back to queue index */ 251ab9098d2SMaxime Coquelin state->index = index; 252ab9098d2SMaxime Coquelin 253ab9098d2SMaxime Coquelin return 0; 254ab9098d2SMaxime Coquelin } 255ab9098d2SMaxime Coquelin 256ab9098d2SMaxime Coquelin static int 257ab9098d2SMaxime Coquelin vhost_kernel_set_vring_num(struct virtio_user_dev *dev, struct vhost_vring_state *state) 258ab9098d2SMaxime Coquelin { 259ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_SET_VRING_NUM, state); 260ab9098d2SMaxime Coquelin } 261ab9098d2SMaxime Coquelin 262ab9098d2SMaxime Coquelin static int 263ab9098d2SMaxime Coquelin vhost_kernel_set_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 264ab9098d2SMaxime Coquelin { 265ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_SET_VRING_BASE, state); 266ab9098d2SMaxime Coquelin } 267ab9098d2SMaxime Coquelin 268ab9098d2SMaxime Coquelin static int 269ab9098d2SMaxime Coquelin vhost_kernel_get_vring_base(struct virtio_user_dev *dev, struct vhost_vring_state *state) 270ab9098d2SMaxime Coquelin { 271ab9098d2SMaxime Coquelin return vhost_kernel_set_vring(dev, VHOST_GET_VRING_BASE, state); 272ab9098d2SMaxime Coquelin } 273ab9098d2SMaxime Coquelin 274ce399c36SMaxime Coquelin static int 275ce399c36SMaxime Coquelin vhost_kernel_set_vring_file(struct virtio_user_dev *dev, uint64_t req, 276ce399c36SMaxime Coquelin struct vhost_vring_file *file) 277ce399c36SMaxime Coquelin { 278ce399c36SMaxime Coquelin int ret, fd; 279ce399c36SMaxime Coquelin unsigned int index = file->index; 280*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 281ce399c36SMaxime Coquelin 282ce399c36SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 283*86388a3aSMaxime Coquelin fd = data->vhostfds[file->index / 2]; 284ce399c36SMaxime Coquelin file->index %= 2; 285ce399c36SMaxime Coquelin 286ce399c36SMaxime Coquelin ret = vhost_kernel_ioctl(fd, req, file); 287ce399c36SMaxime Coquelin if (ret < 0) { 288ce399c36SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring file (request %" PRIu64 ")", req); 289ce399c36SMaxime Coquelin return -1; 290ce399c36SMaxime Coquelin } 291ce399c36SMaxime Coquelin 292ce399c36SMaxime Coquelin /* restore index back to queue index */ 293ce399c36SMaxime Coquelin file->index = index; 294ce399c36SMaxime Coquelin 295ce399c36SMaxime Coquelin return 0; 296ce399c36SMaxime Coquelin } 297ce399c36SMaxime Coquelin 298ce399c36SMaxime Coquelin static int 299ce399c36SMaxime Coquelin vhost_kernel_set_vring_kick(struct virtio_user_dev *dev, struct vhost_vring_file *file) 300ce399c36SMaxime Coquelin { 301ce399c36SMaxime Coquelin return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_KICK, file); 302ce399c36SMaxime Coquelin } 303ce399c36SMaxime Coquelin 304ce399c36SMaxime Coquelin static int 305ce399c36SMaxime Coquelin vhost_kernel_set_vring_call(struct virtio_user_dev *dev, struct vhost_vring_file *file) 306ce399c36SMaxime Coquelin { 307ce399c36SMaxime Coquelin return vhost_kernel_set_vring_file(dev, VHOST_SET_VRING_CALL, file); 308ce399c36SMaxime Coquelin } 309ce399c36SMaxime Coquelin 310dc65db73SMaxime Coquelin static int 311dc65db73SMaxime Coquelin vhost_kernel_set_vring_addr(struct virtio_user_dev *dev, struct vhost_vring_addr *addr) 312dc65db73SMaxime Coquelin { 313dc65db73SMaxime Coquelin int ret, fd; 314dc65db73SMaxime Coquelin unsigned int index = addr->index; 315*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 316dc65db73SMaxime Coquelin 317dc65db73SMaxime Coquelin /* Convert from queue index to queue-pair & offset */ 318*86388a3aSMaxime Coquelin fd = data->vhostfds[addr->index / 2]; 319dc65db73SMaxime Coquelin addr->index %= 2; 320dc65db73SMaxime Coquelin 321dc65db73SMaxime Coquelin ret = vhost_kernel_ioctl(fd, VHOST_SET_VRING_ADDR, addr); 322dc65db73SMaxime Coquelin if (ret < 0) { 323dc65db73SMaxime Coquelin PMD_DRV_LOG(ERR, "Failed to set vring address"); 324dc65db73SMaxime Coquelin return -1; 325dc65db73SMaxime Coquelin } 326dc65db73SMaxime Coquelin 327dc65db73SMaxime Coquelin /* restore index back to queue index */ 328dc65db73SMaxime Coquelin addr->index = index; 329dc65db73SMaxime Coquelin 330dc65db73SMaxime Coquelin return 0; 331dc65db73SMaxime Coquelin } 332dc65db73SMaxime Coquelin 3338723c894SMaxime Coquelin static int 3348723c894SMaxime Coquelin vhost_kernel_get_status(struct virtio_user_dev *dev __rte_unused, uint8_t *status __rte_unused) 3358723c894SMaxime Coquelin { 3368723c894SMaxime Coquelin return -ENOTSUP; 3378723c894SMaxime Coquelin } 3388723c894SMaxime Coquelin 3398723c894SMaxime Coquelin static int 3408723c894SMaxime Coquelin vhost_kernel_set_status(struct virtio_user_dev *dev __rte_unused, uint8_t status __rte_unused) 3418723c894SMaxime Coquelin { 3428723c894SMaxime Coquelin return -ENOTSUP; 3438723c894SMaxime Coquelin } 3448723c894SMaxime Coquelin 345e3b43481SJianfeng Tan /** 346e3b43481SJianfeng Tan * Set up environment to talk with a vhost kernel backend. 347e3b43481SJianfeng Tan * 348e3b43481SJianfeng Tan * @return 349e3b43481SJianfeng Tan * - (-1) if fail to set up; 350e3b43481SJianfeng Tan * - (>=0) if successful. 351e3b43481SJianfeng Tan */ 352e3b43481SJianfeng Tan static int 353e3b43481SJianfeng Tan vhost_kernel_setup(struct virtio_user_dev *dev) 354e3b43481SJianfeng Tan { 355e3b43481SJianfeng Tan int vhostfd; 356*86388a3aSMaxime Coquelin uint32_t q, i; 357*86388a3aSMaxime Coquelin struct vhost_kernel_data *data; 358*86388a3aSMaxime Coquelin 359*86388a3aSMaxime Coquelin data = malloc(sizeof(*data)); 360*86388a3aSMaxime Coquelin if (!data) { 361*86388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost-kernel data", dev->path); 362*86388a3aSMaxime Coquelin return -1; 363*86388a3aSMaxime Coquelin } 364*86388a3aSMaxime Coquelin 365*86388a3aSMaxime Coquelin data->vhostfds = malloc(dev->max_queue_pairs * sizeof(int)); 366*86388a3aSMaxime Coquelin if (!data->vhostfds) { 367*86388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate Vhost FDs", dev->path); 368*86388a3aSMaxime Coquelin goto err_data; 369*86388a3aSMaxime Coquelin } 370*86388a3aSMaxime Coquelin data->tapfds = malloc(dev->max_queue_pairs * sizeof(int)); 371*86388a3aSMaxime Coquelin if (!data->tapfds) { 372*86388a3aSMaxime Coquelin PMD_INIT_LOG(ERR, "(%s) Failed to allocate TAP FDs", dev->path); 373*86388a3aSMaxime Coquelin goto err_vhostfds; 374*86388a3aSMaxime Coquelin } 375*86388a3aSMaxime Coquelin 376*86388a3aSMaxime Coquelin for (q = 0; q < dev->max_queue_pairs; ++q) { 377*86388a3aSMaxime Coquelin data->vhostfds[q] = -1; 378*86388a3aSMaxime Coquelin data->tapfds[q] = -1; 379*86388a3aSMaxime Coquelin } 380e3b43481SJianfeng Tan 381e3b43481SJianfeng Tan get_vhost_kernel_max_regions(); 382e3b43481SJianfeng Tan 383e3b43481SJianfeng Tan for (i = 0; i < dev->max_queue_pairs; ++i) { 384e3b43481SJianfeng Tan vhostfd = open(dev->path, O_RDWR); 385e3b43481SJianfeng Tan if (vhostfd < 0) { 386*86388a3aSMaxime Coquelin PMD_DRV_LOG(ERR, "fail to open %s, %s", dev->path, strerror(errno)); 387*86388a3aSMaxime Coquelin goto err_tapfds; 388*86388a3aSMaxime Coquelin } 389*86388a3aSMaxime Coquelin 390*86388a3aSMaxime Coquelin data->vhostfds[i] = vhostfd; 391*86388a3aSMaxime Coquelin } 392*86388a3aSMaxime Coquelin 393*86388a3aSMaxime Coquelin dev->backend_data = data; 394*86388a3aSMaxime Coquelin 395*86388a3aSMaxime Coquelin return 0; 396*86388a3aSMaxime Coquelin 397*86388a3aSMaxime Coquelin err_tapfds: 398*86388a3aSMaxime Coquelin for (i = 0; i < dev->max_queue_pairs; i++) 399*86388a3aSMaxime Coquelin if (data->vhostfds[i] >= 0) 400*86388a3aSMaxime Coquelin close(data->vhostfds[i]); 401*86388a3aSMaxime Coquelin 402*86388a3aSMaxime Coquelin free(data->tapfds); 403*86388a3aSMaxime Coquelin err_vhostfds: 404*86388a3aSMaxime Coquelin free(data->vhostfds); 405*86388a3aSMaxime Coquelin err_data: 406*86388a3aSMaxime Coquelin free(data); 407*86388a3aSMaxime Coquelin 408e3b43481SJianfeng Tan return -1; 409e3b43481SJianfeng Tan } 410e3b43481SJianfeng Tan 411e3b43481SJianfeng Tan static int 412*86388a3aSMaxime Coquelin vhost_kernel_destroy(struct virtio_user_dev *dev) 413748e5ea5SMaxime Coquelin { 414*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 415*86388a3aSMaxime Coquelin uint32_t i; 416*86388a3aSMaxime Coquelin 417*86388a3aSMaxime Coquelin if (!data) 418*86388a3aSMaxime Coquelin return 0; 419*86388a3aSMaxime Coquelin 420*86388a3aSMaxime Coquelin for (i = 0; i < dev->max_queue_pairs; ++i) { 421*86388a3aSMaxime Coquelin if (data->vhostfds[i] >= 0) 422*86388a3aSMaxime Coquelin close(data->vhostfds[i]); 423*86388a3aSMaxime Coquelin if (data->tapfds[i] >= 0) 424*86388a3aSMaxime Coquelin close(data->tapfds[i]); 425*86388a3aSMaxime Coquelin } 426*86388a3aSMaxime Coquelin 427*86388a3aSMaxime Coquelin free(data->vhostfds); 428*86388a3aSMaxime Coquelin free(data->tapfds); 429*86388a3aSMaxime Coquelin free(data); 430*86388a3aSMaxime Coquelin dev->backend_data = NULL; 431*86388a3aSMaxime Coquelin 432748e5ea5SMaxime Coquelin return 0; 433748e5ea5SMaxime Coquelin } 434748e5ea5SMaxime Coquelin 435748e5ea5SMaxime Coquelin static int 436e3b43481SJianfeng Tan vhost_kernel_set_backend(int vhostfd, int tapfd) 437e3b43481SJianfeng Tan { 438e3b43481SJianfeng Tan struct vhost_vring_file f; 439e3b43481SJianfeng Tan 440e3b43481SJianfeng Tan f.fd = tapfd; 441e3b43481SJianfeng Tan f.index = 0; 442e3b43481SJianfeng Tan if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) { 443e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s", 444e3b43481SJianfeng Tan strerror(errno)); 445e3b43481SJianfeng Tan return -1; 446e3b43481SJianfeng Tan } 447e3b43481SJianfeng Tan 448e3b43481SJianfeng Tan f.index = 1; 449e3b43481SJianfeng Tan if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) { 450e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s", 451e3b43481SJianfeng Tan strerror(errno)); 452e3b43481SJianfeng Tan return -1; 453e3b43481SJianfeng Tan } 454e3b43481SJianfeng Tan 455e3b43481SJianfeng Tan return 0; 456e3b43481SJianfeng Tan } 457e3b43481SJianfeng Tan 458e3b43481SJianfeng Tan static int 459e3b43481SJianfeng Tan vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev, 460e3b43481SJianfeng Tan uint16_t pair_idx, 461e3b43481SJianfeng Tan int enable) 462e3b43481SJianfeng Tan { 463e3b43481SJianfeng Tan int hdr_size; 464e3b43481SJianfeng Tan int vhostfd; 465e3b43481SJianfeng Tan int tapfd; 466be7a4707SJianfeng Tan int req_mq = (dev->max_queue_pairs > 1); 467*86388a3aSMaxime Coquelin struct vhost_kernel_data *data = dev->backend_data; 468e3b43481SJianfeng Tan 469*86388a3aSMaxime Coquelin vhostfd = data->vhostfds[pair_idx]; 470e3b43481SJianfeng Tan 47147ac9661STiwei Bie if (dev->qp_enabled[pair_idx] == enable) 472e3b43481SJianfeng Tan return 0; 47347ac9661STiwei Bie 47447ac9661STiwei Bie if (!enable) { 475*86388a3aSMaxime Coquelin tapfd = data->tapfds[pair_idx]; 47647ac9661STiwei Bie if (vhost_kernel_set_backend(vhostfd, -1) < 0) { 47747ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel"); 47847ac9661STiwei Bie return -1; 47947ac9661STiwei Bie } 48047ac9661STiwei Bie if (req_mq && vhost_kernel_tap_set_queue(tapfd, false) < 0) { 48147ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to disable tap for vhost kernel"); 48247ac9661STiwei Bie return -1; 48347ac9661STiwei Bie } 48447ac9661STiwei Bie dev->qp_enabled[pair_idx] = false; 48547ac9661STiwei Bie return 0; 48647ac9661STiwei Bie } 48747ac9661STiwei Bie 488*86388a3aSMaxime Coquelin if (data->tapfds[pair_idx] >= 0) { 489*86388a3aSMaxime Coquelin tapfd = data->tapfds[pair_idx]; 49047ac9661STiwei Bie if (vhost_kernel_tap_set_offload(tapfd, dev->features) == -1) 49147ac9661STiwei Bie return -1; 49247ac9661STiwei Bie if (req_mq && vhost_kernel_tap_set_queue(tapfd, true) < 0) { 49347ac9661STiwei Bie PMD_DRV_LOG(ERR, "fail to enable tap for vhost kernel"); 49447ac9661STiwei Bie return -1; 49547ac9661STiwei Bie } 49647ac9661STiwei Bie goto set_backend; 497e3b43481SJianfeng Tan } 498e3b43481SJianfeng Tan 499e3b43481SJianfeng Tan if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) || 500e3b43481SJianfeng Tan (dev->features & (1ULL << VIRTIO_F_VERSION_1))) 501e3b43481SJianfeng Tan hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf); 502e3b43481SJianfeng Tan else 503e3b43481SJianfeng Tan hdr_size = sizeof(struct virtio_net_hdr); 504e3b43481SJianfeng Tan 505791b43e0SNing Li tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq, 5061db4d233SEric Zhang (char *)dev->mac_addr, dev->features); 507e3b43481SJianfeng Tan if (tapfd < 0) { 508e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel"); 509e3b43481SJianfeng Tan return -1; 510e3b43481SJianfeng Tan } 511e3b43481SJianfeng Tan 512*86388a3aSMaxime Coquelin data->tapfds[pair_idx] = tapfd; 51347ac9661STiwei Bie 51447ac9661STiwei Bie set_backend: 515e3b43481SJianfeng Tan if (vhost_kernel_set_backend(vhostfd, tapfd) < 0) { 516e3b43481SJianfeng Tan PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel"); 517e3b43481SJianfeng Tan return -1; 518e3b43481SJianfeng Tan } 519e3b43481SJianfeng Tan 52047ac9661STiwei Bie dev->qp_enabled[pair_idx] = true; 521e3b43481SJianfeng Tan return 0; 522e3b43481SJianfeng Tan } 523e3b43481SJianfeng Tan 5245b75b63cSMaxime Coquelin static int 5255b75b63cSMaxime Coquelin vhost_kernel_get_backend_features(uint64_t *features) 5265b75b63cSMaxime Coquelin { 5275b75b63cSMaxime Coquelin *features = 0; 5285b75b63cSMaxime Coquelin 5295b75b63cSMaxime Coquelin return 0; 5305b75b63cSMaxime Coquelin } 5315b75b63cSMaxime Coquelin 53294973531SMaxime Coquelin static int 53394973531SMaxime Coquelin vhost_kernel_update_link_state(struct virtio_user_dev *dev __rte_unused) 53494973531SMaxime Coquelin { 53594973531SMaxime Coquelin /* Nothing to update (Maybe get TAP interface link state?) */ 53694973531SMaxime Coquelin return 0; 53794973531SMaxime Coquelin } 53894973531SMaxime Coquelin 53994973531SMaxime Coquelin static int 54094973531SMaxime Coquelin vhost_kernel_get_intr_fd(struct virtio_user_dev *dev __rte_unused) 54194973531SMaxime Coquelin { 54294973531SMaxime Coquelin /* No link state interrupt with Vhost-kernel */ 54394973531SMaxime Coquelin return -1; 54494973531SMaxime Coquelin } 54594973531SMaxime Coquelin 546520dd992SFerruh Yigit struct virtio_user_backend_ops virtio_ops_kernel = { 547e3b43481SJianfeng Tan .setup = vhost_kernel_setup, 548748e5ea5SMaxime Coquelin .destroy = vhost_kernel_destroy, 5495b75b63cSMaxime Coquelin .get_backend_features = vhost_kernel_get_backend_features, 55006856cabSMaxime Coquelin .set_owner = vhost_kernel_set_owner, 551cc0151b3SMaxime Coquelin .get_features = vhost_kernel_get_features, 552cc0151b3SMaxime Coquelin .set_features = vhost_kernel_set_features, 553539d910cSMaxime Coquelin .set_memory_table = vhost_kernel_set_memory_table, 554ab9098d2SMaxime Coquelin .set_vring_num = vhost_kernel_set_vring_num, 555ab9098d2SMaxime Coquelin .set_vring_base = vhost_kernel_set_vring_base, 556ab9098d2SMaxime Coquelin .get_vring_base = vhost_kernel_get_vring_base, 557ce399c36SMaxime Coquelin .set_vring_call = vhost_kernel_set_vring_call, 558ce399c36SMaxime Coquelin .set_vring_kick = vhost_kernel_set_vring_kick, 559dc65db73SMaxime Coquelin .set_vring_addr = vhost_kernel_set_vring_addr, 5608723c894SMaxime Coquelin .get_status = vhost_kernel_get_status, 5618723c894SMaxime Coquelin .set_status = vhost_kernel_set_status, 56294973531SMaxime Coquelin .enable_qp = vhost_kernel_enable_queue_pair, 56394973531SMaxime Coquelin .update_link_state = vhost_kernel_update_link_state, 56494973531SMaxime Coquelin .get_intr_fd = vhost_kernel_get_intr_fd, 565e3b43481SJianfeng Tan }; 566