From cc0151b34dee25aac9364c7e52c930efb6c10adb Mon Sep 17 00:00:00 2001 From: Maxime Coquelin Date: Tue, 26 Jan 2021 11:16:22 +0100 Subject: [PATCH] net/virtio: add virtio-user features ops This patch introduces new callbacks for getting and setting Virtio features, and implements them for the different backend types. Signed-off-by: Maxime Coquelin Reviewed-by: Chenbo Xia --- drivers/net/virtio/virtio_user/vhost.h | 2 + drivers/net/virtio/virtio_user/vhost_kernel.c | 150 +++++++++--------- .../net/virtio/virtio_user/vhost_kernel_tap.c | 23 +++ .../net/virtio/virtio_user/vhost_kernel_tap.h | 1 + drivers/net/virtio/virtio_user/vhost_user.c | 64 +++++++- drivers/net/virtio/virtio_user/vhost_vdpa.c | 38 +++-- .../net/virtio/virtio_user/virtio_user_dev.c | 5 +- drivers/net/virtio/virtio_user_ethdev.c | 3 +- 8 files changed, 190 insertions(+), 96 deletions(-) diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h index 5413ec6778..13a88c7671 100644 --- a/drivers/net/virtio/virtio_user/vhost.h +++ b/drivers/net/virtio/virtio_user/vhost.h @@ -110,6 +110,8 @@ struct virtio_user_dev; struct virtio_user_backend_ops { int (*setup)(struct virtio_user_dev *dev); int (*set_owner)(struct virtio_user_dev *dev); + int (*get_features)(struct virtio_user_dev *dev, uint64_t *features); + int (*set_features)(struct virtio_user_dev *dev, uint64_t features); int (*send_request)(struct virtio_user_dev *dev, enum vhost_user_request req, void *arg); diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c index b79dcad179..e46039e649 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel.c +++ b/drivers/net/virtio/virtio_user/vhost_kernel.c @@ -38,6 +38,28 @@ struct vhost_memory_kernel { #define VHOST_SET_VRING_ERR _IOW(VHOST_VIRTIO, 0x22, struct vhost_vring_file) #define VHOST_NET_SET_BACKEND _IOW(VHOST_VIRTIO, 0x30, struct vhost_vring_file) +/* with below features, vhost kernel does not need to do the checksum and TSO, + * these info will be passed to virtio_user through virtio net header. + */ +#define VHOST_KERNEL_GUEST_OFFLOADS_MASK \ + ((1ULL << VIRTIO_NET_F_GUEST_CSUM) | \ + (1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ + (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ + (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ + (1ULL << VIRTIO_NET_F_GUEST_UFO)) + +/* with below features, when flows from virtio_user to vhost kernel + * (1) if flows goes up through the kernel networking stack, it does not need + * to verify checksum, which can save CPU cycles; + * (2) if flows goes through a Linux bridge and outside from an interface + * (kernel driver), checksum and TSO will be done by GSO in kernel or even + * offloaded into real physical device. + */ +#define VHOST_KERNEL_HOST_OFFLOADS_MASK \ + ((1ULL << VIRTIO_NET_F_HOST_TSO4) | \ + (1ULL << VIRTIO_NET_F_HOST_TSO6) | \ + (1ULL << VIRTIO_NET_F_CSUM)) + static uint64_t max_regions = 64; static void @@ -77,10 +99,57 @@ vhost_kernel_set_owner(struct virtio_user_dev *dev) return vhost_kernel_ioctl(dev->vhostfds[0], VHOST_SET_OWNER, NULL); } +static int +vhost_kernel_get_features(struct virtio_user_dev *dev, uint64_t *features) +{ + int ret; + unsigned int tap_features; + + ret = vhost_kernel_ioctl(dev->vhostfds[0], VHOST_GET_FEATURES, features); + if (ret < 0) { + PMD_DRV_LOG(ERR, "Failed to get features"); + return -1; + } + + ret = tap_support_features(&tap_features); + if (ret < 0) { + PMD_DRV_LOG(ERR, "Failed to get TAP features"); + return -1; + } + + /* with tap as the backend, all these features are supported + * but not claimed by vhost-net, so we add them back when + * reporting to upper layer. + */ + if (tap_features & IFF_VNET_HDR) { + *features |= VHOST_KERNEL_GUEST_OFFLOADS_MASK; + *features |= VHOST_KERNEL_HOST_OFFLOADS_MASK; + } + + /* vhost_kernel will not declare this feature, but it does + * support multi-queue. + */ + if (tap_features & IFF_MULTI_QUEUE) + *features |= (1ull << VIRTIO_NET_F_MQ); + + return 0; +} + +static int +vhost_kernel_set_features(struct virtio_user_dev *dev, uint64_t features) +{ + /* We don't need memory protection here */ + features &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM); + /* VHOST kernel does not know about below flags */ + features &= ~VHOST_KERNEL_GUEST_OFFLOADS_MASK; + features &= ~VHOST_KERNEL_HOST_OFFLOADS_MASK; + features &= ~(1ULL << VIRTIO_NET_F_MQ); + + return vhost_kernel_ioctl(dev->vhostfds[0], VHOST_SET_FEATURES, &features); +} + static uint64_t vhost_req_user_to_kernel[] = { [VHOST_USER_RESET_OWNER] = VHOST_RESET_OWNER, - [VHOST_USER_SET_FEATURES] = VHOST_SET_FEATURES, - [VHOST_USER_GET_FEATURES] = VHOST_GET_FEATURES, [VHOST_USER_SET_VRING_CALL] = VHOST_SET_VRING_CALL, [VHOST_USER_SET_VRING_NUM] = VHOST_SET_VRING_NUM, [VHOST_USER_SET_VRING_BASE] = VHOST_SET_VRING_BASE, @@ -150,51 +219,6 @@ prepare_vhost_memory_kernel(void) return vm; } -/* with below features, vhost kernel does not need to do the checksum and TSO, - * these info will be passed to virtio_user through virtio net header. - */ -#define VHOST_KERNEL_GUEST_OFFLOADS_MASK \ - ((1ULL << VIRTIO_NET_F_GUEST_CSUM) | \ - (1ULL << VIRTIO_NET_F_GUEST_TSO4) | \ - (1ULL << VIRTIO_NET_F_GUEST_TSO6) | \ - (1ULL << VIRTIO_NET_F_GUEST_ECN) | \ - (1ULL << VIRTIO_NET_F_GUEST_UFO)) - -/* with below features, when flows from virtio_user to vhost kernel - * (1) if flows goes up through the kernel networking stack, it does not need - * to verify checksum, which can save CPU cycles; - * (2) if flows goes through a Linux bridge and outside from an interface - * (kernel driver), checksum and TSO will be done by GSO in kernel or even - * offloaded into real physical device. - */ -#define VHOST_KERNEL_HOST_OFFLOADS_MASK \ - ((1ULL << VIRTIO_NET_F_HOST_TSO4) | \ - (1ULL << VIRTIO_NET_F_HOST_TSO6) | \ - (1ULL << VIRTIO_NET_F_CSUM)) - -static unsigned int -tap_support_features(void) -{ - int tapfd; - unsigned int tap_features; - - tapfd = open(PATH_NET_TUN, O_RDWR); - if (tapfd < 0) { - PMD_DRV_LOG(ERR, "fail to open %s: %s", - PATH_NET_TUN, strerror(errno)); - return -1; - } - - if (ioctl(tapfd, TUNGETFEATURES, &tap_features) == -1) { - PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno)); - close(tapfd); - return -1; - } - - close(tapfd); - return tap_features; -} - static int vhost_kernel_send_request(struct virtio_user_dev *dev, enum vhost_user_request req, @@ -206,7 +230,6 @@ vhost_kernel_send_request(struct virtio_user_dev *dev, struct vhost_memory_kernel *vm = NULL; int vhostfd; unsigned int queue_sel; - unsigned int features; PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]); @@ -219,17 +242,6 @@ vhost_kernel_send_request(struct virtio_user_dev *dev, arg = (void *)vm; } - if (req_kernel == VHOST_SET_FEATURES) { - /* We don't need memory protection here */ - *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM); - - /* VHOST kernel does not know about below flags */ - *(uint64_t *)arg &= ~VHOST_KERNEL_GUEST_OFFLOADS_MASK; - *(uint64_t *)arg &= ~VHOST_KERNEL_HOST_OFFLOADS_MASK; - - *(uint64_t *)arg &= ~(1ULL << VIRTIO_NET_F_MQ); - } - switch (req_kernel) { case VHOST_SET_VRING_NUM: case VHOST_SET_VRING_ADDR: @@ -259,24 +271,6 @@ vhost_kernel_send_request(struct virtio_user_dev *dev, ret = ioctl(vhostfd, req_kernel, arg); } - if (!ret && req_kernel == VHOST_GET_FEATURES) { - features = tap_support_features(); - /* with tap as the backend, all these features are supported - * but not claimed by vhost-net, so we add them back when - * reporting to upper layer. - */ - if (features & IFF_VNET_HDR) { - *((uint64_t *)arg) |= VHOST_KERNEL_GUEST_OFFLOADS_MASK; - *((uint64_t *)arg) |= VHOST_KERNEL_HOST_OFFLOADS_MASK; - } - - /* vhost_kernel will not declare this feature, but it does - * support multi-queue. - */ - if (features & IFF_MULTI_QUEUE) - *(uint64_t *)arg |= (1ull << VIRTIO_NET_F_MQ); - } - if (vm) free(vm); @@ -407,6 +401,8 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev, struct virtio_user_backend_ops virtio_ops_kernel = { .setup = vhost_kernel_setup, .set_owner = vhost_kernel_set_owner, + .get_features = vhost_kernel_get_features, + .set_features = vhost_kernel_set_features, .send_request = vhost_kernel_send_request, .enable_qp = vhost_kernel_enable_queue_pair }; diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c index eade702c5c..99096bdf39 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c +++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c @@ -18,6 +18,29 @@ #include "../virtio_logs.h" #include "../virtio.h" + +int +tap_support_features(unsigned int *tap_features) +{ + int tapfd; + + tapfd = open(PATH_NET_TUN, O_RDWR); + if (tapfd < 0) { + PMD_DRV_LOG(ERR, "fail to open %s: %s", + PATH_NET_TUN, strerror(errno)); + return -1; + } + + if (ioctl(tapfd, TUNGETFEATURES, tap_features) == -1) { + PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno)); + close(tapfd); + return -1; + } + + close(tapfd); + return 0; +} + int vhost_kernel_tap_set_offload(int fd, uint64_t features) { diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h index 5c4447b296..ed03fce21e 100644 --- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h +++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h @@ -43,5 +43,6 @@ int vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq, const char *mac, uint64_t features); int vhost_kernel_tap_set_offload(int fd, uint64_t features); int vhost_kernel_tap_set_queue(int fd, bool attach); +int tap_support_features(unsigned int *tap_features); #endif diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c index ea3bd4ca10..8e5bf332d6 100644 --- a/drivers/net/virtio/virtio_user/vhost_user.c +++ b/drivers/net/virtio/virtio_user/vhost_user.c @@ -143,6 +143,64 @@ vhost_user_set_owner(struct virtio_user_dev *dev) return 0; } +static int +vhost_user_get_features(struct virtio_user_dev *dev, uint64_t *features) +{ + int ret; + struct vhost_user_msg msg = { + .request = VHOST_USER_GET_FEATURES, + .flags = VHOST_USER_VERSION, + }; + + ret = vhost_user_write(dev->vhostfd, &msg, NULL, 0); + if (ret < 0) + goto err; + + ret = vhost_user_read(dev->vhostfd, &msg); + if (ret < 0) + goto err; + + if (msg.request != VHOST_USER_GET_FEATURES) { + PMD_DRV_LOG(ERR, "Unexpected request type (%d)", msg.request); + goto err; + } + + if (msg.size != sizeof(*features)) { + PMD_DRV_LOG(ERR, "Unexpected payload size (%u)", msg.size); + goto err; + } + + *features = msg.payload.u64; + + return 0; +err: + PMD_DRV_LOG(ERR, "Failed to get backend features"); + + return -1; +} + +static int +vhost_user_set_features(struct virtio_user_dev *dev, uint64_t features) +{ + int ret; + struct vhost_user_msg msg = { + .request = VHOST_USER_SET_FEATURES, + .flags = VHOST_USER_VERSION, + .size = sizeof(features), + .payload.u64 = features, + }; + + msg.payload.u64 |= dev->device_features & (1ULL << VHOST_USER_F_PROTOCOL_FEATURES); + + ret = vhost_user_write(dev->vhostfd, &msg, NULL, 0); + if (ret < 0) { + PMD_DRV_LOG(ERR, "Failed to set features"); + return -1; + } + + return 0; +} + struct walk_arg { struct vhost_memory *vm; int *fds; @@ -249,8 +307,6 @@ static struct vhost_user_msg m; const char * const vhost_msg_strings[] = { [VHOST_USER_RESET_OWNER] = "VHOST_RESET_OWNER", - [VHOST_USER_SET_FEATURES] = "VHOST_SET_FEATURES", - [VHOST_USER_GET_FEATURES] = "VHOST_GET_FEATURES", [VHOST_USER_SET_VRING_CALL] = "VHOST_SET_VRING_CALL", [VHOST_USER_SET_VRING_NUM] = "VHOST_SET_VRING_NUM", [VHOST_USER_SET_VRING_BASE] = "VHOST_SET_VRING_BASE", @@ -299,7 +355,6 @@ vhost_user_sock(struct virtio_user_dev *dev, (1ULL << VHOST_USER_PROTOCOL_F_STATUS)))) return -ENOTSUP; /* Fallthrough */ - case VHOST_USER_GET_FEATURES: case VHOST_USER_GET_PROTOCOL_FEATURES: need_reply = 1; break; @@ -398,7 +453,6 @@ vhost_user_sock(struct virtio_user_dev *dev, } switch (req) { - case VHOST_USER_GET_FEATURES: case VHOST_USER_GET_STATUS: case VHOST_USER_GET_PROTOCOL_FEATURES: if (msg.size != sizeof(m.payload.u64)) { @@ -536,6 +590,8 @@ vhost_user_enable_queue_pair(struct virtio_user_dev *dev, struct virtio_user_backend_ops virtio_ops_user = { .setup = vhost_user_setup, .set_owner = vhost_user_set_owner, + .get_features = vhost_user_get_features, + .set_features = vhost_user_set_features, .send_request = vhost_user_sock, .enable_qp = vhost_user_enable_queue_pair }; diff --git a/drivers/net/virtio/virtio_user/vhost_vdpa.c b/drivers/net/virtio/virtio_user/vhost_vdpa.c index d9bc213e0d..22a329526a 100644 --- a/drivers/net/virtio/virtio_user/vhost_vdpa.c +++ b/drivers/net/virtio/virtio_user/vhost_vdpa.c @@ -40,8 +40,6 @@ static uint64_t vhost_req_user_to_vdpa[] = { [VHOST_USER_RESET_OWNER] = VHOST_RESET_OWNER, - [VHOST_USER_SET_FEATURES] = VHOST_SET_FEATURES, - [VHOST_USER_GET_FEATURES] = VHOST_GET_FEATURES, [VHOST_USER_SET_VRING_CALL] = VHOST_SET_VRING_CALL, [VHOST_USER_SET_VRING_NUM] = VHOST_SET_VRING_NUM, [VHOST_USER_SET_VRING_BASE] = VHOST_SET_VRING_BASE, @@ -107,6 +105,32 @@ vhost_vdpa_set_owner(struct virtio_user_dev *dev) return vhost_vdpa_ioctl(dev->vhostfd, VHOST_SET_OWNER, NULL); } +static int +vhost_vdpa_get_features(struct virtio_user_dev *dev, uint64_t *features) +{ + int ret; + + ret = vhost_vdpa_ioctl(dev->vhostfd, VHOST_GET_FEATURES, features); + if (ret) { + PMD_DRV_LOG(ERR, "Failed to get features"); + return -1; + } + + /* Multiqueue not supported for now */ + *features &= ~(1ULL << VIRTIO_NET_F_MQ); + + return 0; +} + +static int +vhost_vdpa_set_features(struct virtio_user_dev *dev, uint64_t features) +{ + /* WORKAROUND */ + features |= 1ULL << VIRTIO_F_IOMMU_PLATFORM; + + return vhost_vdpa_ioctl(dev->vhostfd, VHOST_SET_FEATURES, &features); +} + static int vhost_vdpa_iotlb_batch_begin(struct virtio_user_dev *dev) { @@ -343,14 +367,6 @@ vhost_vdpa_send_request(struct virtio_user_dev *dev, if (req_vdpa == VHOST_SET_MEM_TABLE) return vhost_vdpa_dma_map_all(dev); - if (req_vdpa == VHOST_SET_FEATURES) { - /* WORKAROUND */ - *(uint64_t *)arg |= 1ULL << VIRTIO_F_IOMMU_PLATFORM; - - /* Multiqueue not supported for now */ - *(uint64_t *)arg &= ~(1ULL << VIRTIO_NET_F_MQ); - } - switch (req_vdpa) { case VHOST_SET_VRING_NUM: case VHOST_SET_VRING_ADDR: @@ -429,6 +445,8 @@ vhost_vdpa_enable_queue_pair(struct virtio_user_dev *dev, struct virtio_user_backend_ops virtio_ops_vdpa = { .setup = vhost_vdpa_setup, .set_owner = vhost_vdpa_set_owner, + .get_features = vhost_vdpa_get_features, + .set_features = vhost_vdpa_set_features, .send_request = vhost_vdpa_send_request, .enable_qp = vhost_vdpa_enable_queue_pair, .dma_map = vhost_vdpa_dma_map_batch, diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c index f4b97d8489..1f3bbae663 100644 --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c @@ -141,7 +141,7 @@ virtio_user_dev_set_features(struct virtio_user_dev *dev) /* Strip VIRTIO_NET_F_CTRL_VQ, as devices do not really need to know */ features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ); features &= ~(1ull << VIRTIO_NET_F_STATUS); - ret = dev->ops->send_request(dev, VHOST_USER_SET_FEATURES, &features); + ret = dev->ops->set_features(dev, features); if (ret < 0) goto error; PMD_DRV_LOG(INFO, "set features: %" PRIx64, features); @@ -496,8 +496,7 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues, return -1; } - if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES, - &dev->device_features) < 0) { + if (dev->ops->get_features(dev, &dev->device_features) < 0) { PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno)); return -1; diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c index 093f244ec9..8bbf4cf809 100644 --- a/drivers/net/virtio/virtio_user_ethdev.c +++ b/drivers/net/virtio/virtio_user_ethdev.c @@ -85,8 +85,7 @@ virtio_user_server_reconnect(struct virtio_user_dev *dev) virtio_set_status(hw, VIRTIO_CONFIG_STATUS_DRIVER); - if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES, - &dev->device_features) < 0) { + if (dev->ops->get_features(dev, &dev->device_features) < 0) { PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno)); return -1;