* [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path
@ 2016-12-02 14:31 Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer Jianfeng Tan
` (6 more replies)
0 siblings, 7 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-02 14:31 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
as the backend). The path to go with a vhost-kernel backend has been
dropped for bad performance comparing to vhost-user and code simplicity.
But after a second thought, virtio_user + vhost-kernel is a good
candidate as an exceptional path, such as KNI, which exchanges packets
with kernel networking stack.
- maintenance: vhost-net (kernel) is upstreamed and extensively used
kernel module. We don't need any out-of-tree module like KNI.
- performance: as with KNI, this solution would use one or more
kthreads to send/receive packets from user space DPDK applications,
which has little impact on user space polling thread (except that
it might enter into kernel space to wake up those kthreads if
necessary.
- features: vhost-net is born to be a networking solution, which has
lots of networking related featuers, like multi queue, tso, multi-seg
mbuf, etc.
Known issues for current version:
- Multiqueue not supported yet.
- Offloading is completely enabled yet; to enhance, we will translate
the virtio header info into mbuf metadata when receiving packets
from kernel; and translate mbuf metadata info into virtio header
when sending packets to kernel.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Jianfeng Tan (3):
net/virtio_user: add vhost layer
net/virtio_user: add vhost kernel support
net/virtio_user: fix wrongly set features
drivers/net/virtio/Makefile | 2 +
drivers/net/virtio/virtio_user/vhost.c | 181 ++++++++++
drivers/net/virtio/virtio_user/vhost.h | 73 ++--
drivers/net/virtio/virtio_user/vhost_kernel.c | 413 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost_user.c | 81 +++--
drivers/net/virtio/virtio_user/virtio_user_dev.c | 49 ++-
drivers/net/virtio/virtio_user/virtio_user_dev.h | 3 +-
drivers/net/virtio/virtio_user_ethdev.c | 4 +-
8 files changed, 708 insertions(+), 98 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost.c
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
@ 2016-12-02 14:31 ` Jianfeng Tan
2016-12-08 7:21 ` Yuanhan Liu
2016-12-02 14:31 ` [dpdk-dev] [PATCH 2/3] net/virtio_user: add vhost kernel support Jianfeng Tan
` (5 subsequent siblings)
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-02 14:31 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
To support vhost kernel as the backend of net_virtio_user in comming
patches, we abstract a vhost layer to hide all vhost_user functions.
- Move vhost_user specific structs and macros into vhost_user.c,
and only keep common definitions in vhost.h;
- Add a struct vhost_internal, and an array to store vhost_user and
vhost_kernel backends; in multiqueue case, vhost_user has only
one vhost FD, but vhost_kernel would have multiple FDs (equal to
# of queues), so we turn to use an id to index the backend info;
- Add a struct vhost_ops depending on different type of backends.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_user/vhost.c | 162 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost.h | 68 +++++-----
drivers/net/virtio/virtio_user/vhost_user.c | 81 ++++++++----
drivers/net/virtio/virtio_user/virtio_user_dev.c | 47 +++----
drivers/net/virtio/virtio_user/virtio_user_dev.h | 2 +-
6 files changed, 266 insertions(+), 95 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost.c
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 97972a6..17f7129 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -59,6 +59,7 @@ SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_rxtx_simple_neon.c
endif
ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
diff --git a/drivers/net/virtio/virtio_user/vhost.c b/drivers/net/virtio/virtio_user/vhost.c
new file mode 100644
index 0000000..09e2e92
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost.c
@@ -0,0 +1,162 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "vhost.h"
+
+#define VHOST_MAX_DEVICES 8
+
+static struct vhost_internal internals[VHOST_MAX_DEVICES];
+
+static inline int
+vhost_internal_valid_vid(int vid)
+{
+ if (vid < 0 || vid >= VHOST_MAX_DEVICES)
+ return 0;
+
+ if (!internals[vid].ops)
+ return 0;
+
+ return 1;
+}
+
+static int
+vhost_internal_alloc(void)
+{
+ int i;
+
+ for (i = 0; i < VHOST_MAX_DEVICES; ++i)
+ if (internals[i].ops == NULL)
+ break;
+
+ if (i >= VHOST_MAX_DEVICES)
+ return -1;
+
+ internals[i].features = 0;
+
+ internals[i].vhostfd = -1;
+
+ return 0;
+}
+
+static void
+vhost_internal_free(int id)
+{
+ internals[id].ops = NULL;
+
+ if (internals[id].vhostfd >= 0)
+ close(internals[id].vhostfd);
+}
+
+
+static int
+is_vhost_user_by_type(const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == -1)
+ return 0;
+
+ return S_ISSOCK(sb.st_mode);
+}
+
+int
+vhost_setup(const char *path)
+{
+ int ret = -1;
+ int vid = vhost_internal_alloc();
+
+ if (vid < 0) {
+ PMD_DRV_LOG(ERR, "exceeding %d devices", VHOST_MAX_DEVICES);
+ return -1;
+ }
+
+ if (is_vhost_user_by_type(path)) {
+ ret = vhost_ops_user.setup(&internals[vid], path);
+ internals[vid].ops = &vhost_ops_user;
+ }
+
+ if (ret < 0)
+ vhost_internal_free(vid);
+
+ return vid;
+}
+
+static const char * const vhost_msg_strings[] = {
+ [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
+ [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
+ [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
+ [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
+ [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL",
+ [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM",
+ [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE",
+ [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE",
+ [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR",
+ [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK",
+ [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
+ [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE",
+ NULL,
+};
+
+int
+vhost_call(int vid, enum vhost_user_request req, void *arg)
+{
+ RTE_SET_USED(vhost_msg_strings);
+ PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
+
+ if (!vhost_internal_valid_vid(vid))
+ return -1;
+
+ return internals[vid].ops->control(&internals[vid], req, arg);
+}
+
+int
+vhost_enable_queue_pair(int vid, uint16_t pair_idx, int enable)
+{
+ if (!vhost_internal_valid_vid(vid))
+ return -1;
+
+ return internals[vid].ops->enable_qp(&internals[vid], pair_idx, enable);
+}
+
+void
+vhost_close(int vid)
+{
+ if (!vhost_internal_valid_vid(vid))
+ return;
+
+ vhost_internal_free(vid);
+}
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 7adb55f..b476ecc 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -42,8 +42,6 @@
#include "../virtio_logs.h"
#include "../virtqueue.h"
-#define VHOST_MEMORY_MAX_NREGIONS 8
-
struct vhost_vring_state {
unsigned int index;
unsigned int num;
@@ -105,42 +103,38 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-struct vhost_memory {
- uint32_t nregions;
- uint32_t padding;
- struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
+struct vhost_ops;
+
+struct vhost_internal {
+ /* for both vhost-user and vhost-kernel */
+ uint64_t features;
+ struct vhost_ops *ops;
+
+ /* for vhost-user */
+ int vhostfd;
+
+ /* for vhost-kernel */
+};
+
+typedef int (*vhost_setup_t)(struct vhost_internal *internal,
+ const char *path);
+typedef int (*vhost_control_t)(struct vhost_internal *internal,
+ enum vhost_user_request req,
+ void *arg);
+typedef int (*vhost_enable_qp_t)(struct vhost_internal *internal,
+ uint16_t pair_idx,
+ int enable);
+
+struct vhost_ops {
+ vhost_setup_t setup;
+ vhost_control_t control;
+ vhost_enable_qp_t enable_qp;
};
-struct vhost_user_msg {
- enum vhost_user_request request;
-
-#define VHOST_USER_VERSION_MASK 0x3
-#define VHOST_USER_REPLY_MASK (0x1 << 2)
- uint32_t flags;
- uint32_t size; /* the following payload size */
- union {
-#define VHOST_USER_VRING_IDX_MASK 0xff
-#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
- uint64_t u64;
- struct vhost_vring_state state;
- struct vhost_vring_addr addr;
- struct vhost_memory memory;
- } payload;
- int fds[VHOST_MEMORY_MAX_NREGIONS];
-} __attribute((packed));
-
-#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
-#define VHOST_USER_PAYLOAD_SIZE \
- (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
-
-/* The version of the protocol we support */
-#define VHOST_USER_VERSION 0x1
-
-#define VHOST_USER_F_PROTOCOL_FEATURES 30
-#define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
-
-int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
-int vhost_user_setup(const char *path);
-int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
+struct vhost_ops vhost_ops_user;
+int vhost_setup(const char *path);
+int vhost_call(int vid, enum vhost_user_request req, void *arg);
+int vhost_enable_queue_pair(int vid, uint16_t pair_idx, int enable);
+void vhost_close(int vid);
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 082e821..afd18ec 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -42,6 +42,38 @@
#include "vhost.h"
+/* The version of the protocol we support */
+#define VHOST_USER_VERSION 0x1
+#define VHOST_MEMORY_MAX_NREGIONS 8
+
+struct vhost_memory {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
+};
+
+struct vhost_user_msg {
+ enum vhost_user_request request;
+
+#define VHOST_USER_VERSION_MASK 0x3
+#define VHOST_USER_REPLY_MASK (0x1 << 2)
+ uint32_t flags;
+ uint32_t size; /* the following payload size */
+ union {
+#define VHOST_USER_VRING_IDX_MASK 0xff
+#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
+ uint64_t u64;
+ struct vhost_vring_state state;
+ struct vhost_vring_addr addr;
+ struct vhost_memory memory;
+ } payload;
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+} __attribute((packed));
+
+#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
+#define VHOST_USER_PAYLOAD_SIZE \
+ (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
+
static int
vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
{
@@ -223,24 +255,10 @@ prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
static struct vhost_user_msg m;
-static const char * const vhost_msg_strings[] = {
- [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
- [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
- [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
- [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
- [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL",
- [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM",
- [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE",
- [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE",
- [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR",
- [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK",
- [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
- [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE",
- NULL,
-};
-
-int
-vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
+static int
+vhost_user_sock(struct vhost_internal *internal,
+ enum vhost_user_request req,
+ void *arg)
{
struct vhost_user_msg msg;
struct vhost_vring_file *file = 0;
@@ -248,11 +266,9 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
int fds[VHOST_MEMORY_MAX_NREGIONS];
int fd_num = 0;
int i, len;
+ int vhostfd = internal->vhostfd;
RTE_SET_USED(m);
- RTE_SET_USED(vhost_msg_strings);
-
- PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
msg.request = req;
msg.flags = VHOST_USER_VERSION;
@@ -323,8 +339,8 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
len = VHOST_USER_HDR_SIZE + msg.size;
if (vhost_user_write(vhostfd, &msg, len, fds, fd_num) < 0) {
- PMD_DRV_LOG(ERR, "%s failed: %s",
- vhost_msg_strings[req], strerror(errno));
+ PMD_DRV_LOG(ERR, "fail to write vhost user msg: %s",
+ strerror(errno));
return -1;
}
@@ -378,8 +394,8 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
* - (-1) if fail to set up;
* - (>=0) if successful, and it is the fd to vhostfd.
*/
-int
-vhost_user_setup(const char *path)
+static int
+vhost_user_setup(struct vhost_internal *internal, const char *path)
{
int fd;
int flag;
@@ -404,11 +420,14 @@ vhost_user_setup(const char *path)
return -1;
}
+ internal->vhostfd = fd;
+
return fd;
}
-int
-vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
+static int
+vhost_user_enable_queue_pair(struct vhost_internal *internal,
+ uint16_t pair_idx, int enable)
{
int i;
@@ -418,10 +437,16 @@ vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
.num = enable,
};
- if (vhost_user_sock(vhostfd,
+ if (vhost_user_sock(internal,
VHOST_USER_SET_VRING_ENABLE, &state))
return -1;
}
return 0;
}
+
+struct vhost_ops vhost_ops_user = {
+ .setup = vhost_user_setup,
+ .control = vhost_user_sock,
+ .enable_qp = vhost_user_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index e239e0e..3aef5f6 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -64,7 +64,7 @@ virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = callfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_CALL, &file);
+ vhost_call(dev->vid, VHOST_USER_SET_VRING_CALL, &file);
dev->callfds[queue_sel] = callfd;
return 0;
@@ -88,12 +88,12 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.index = queue_sel;
state.num = vring->num;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_NUM, &state);
+ vhost_call(dev->vid, VHOST_USER_SET_VRING_NUM, &state);
state.num = 0; /* no reservation */
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_BASE, &state);
+ vhost_call(dev->vid, VHOST_USER_SET_VRING_BASE, &state);
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_ADDR, &addr);
+ vhost_call(dev->vid, VHOST_USER_SET_VRING_ADDR, &addr);
/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
* lastly because vhost depends on this msg to judge if
@@ -106,7 +106,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = kickfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_KICK, &file);
+ vhost_call(dev->vid, VHOST_USER_SET_VRING_KICK, &file);
dev->kickfds[queue_sel] = kickfd;
return 0;
@@ -146,21 +146,18 @@ virtio_user_start_device(struct virtio_user_dev *dev)
if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
goto error;
- /* Step 1: set features
- * Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * and VIRTIO_NET_F_MAC is stripped.
+ /* Step 1: set feature
+ * Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init.
*/
features = dev->features;
- if (dev->max_queue_pairs > 1)
- features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
+ ret = vhost_call(dev->vid, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
PMD_DRV_LOG(INFO, "set features: %" PRIx64, features);
/* Step 2: share memory regions */
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_MEM_TABLE, NULL);
+ ret = vhost_call(dev->vid, VHOST_USER_SET_MEM_TABLE, NULL);
if (ret < 0)
goto error;
@@ -171,7 +168,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 4: enable queues
* we enable the 1st queue pair by default.
*/
- vhost_user_enable_queue_pair(dev->vhostfd, 0, 1);
+ vhost_enable_queue_pair(dev->vid, 0, 1);
return 0;
error:
@@ -181,7 +178,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
int virtio_user_stop_device(struct virtio_user_dev *dev)
{
- return vhost_user_sock(dev->vhostfd, VHOST_USER_RESET_OWNER, NULL);
+ return vhost_call(dev->vid, VHOST_USER_RESET_OWNER, NULL);
}
static inline void
@@ -215,19 +212,18 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
dev->queue_size = queue_size;
dev->mac_specified = 0;
parse_mac(dev, mac);
- dev->vhostfd = -1;
- dev->vhostfd = vhost_user_setup(dev->path);
- if (dev->vhostfd < 0) {
+ dev->vid = vhost_setup(dev->path);
+ if (dev->vid < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL) < 0) {
+ if (vhost_call(dev->vid, VHOST_USER_SET_OWNER, NULL) < 0) {
PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
+ if (vhost_call(dev->vid, VHOST_USER_GET_FEATURES,
&dev->features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
@@ -253,13 +249,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
PMD_INIT_LOG(INFO, "vhost does not support ctrl-q");
}
- if (dev->max_queue_pairs > 1) {
- if (!(dev->features & VHOST_USER_MQ)) {
- PMD_INIT_LOG(ERR, "MQ not supported by the backend");
- return -1;
- }
- }
-
return 0;
}
@@ -273,7 +262,7 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
close(dev->kickfds[i]);
}
- close(dev->vhostfd);
+ vhost_close(dev->vid);
}
static uint8_t
@@ -289,9 +278,9 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
}
for (i = 0; i < q_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 1);
+ ret |= vhost_enable_queue_pair(dev->vid, i, 1);
for (i = q_pairs; i < dev->max_queue_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ ret |= vhost_enable_queue_pair(dev->vid, i, 0);
dev->queue_pairs = q_pairs;
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 33690b5..80efb6e 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -39,7 +39,7 @@
#include "../virtio_ring.h"
struct virtio_user_dev {
- int vhostfd;
+ int vid;
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int mac_specified;
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH 2/3] net/virtio_user: add vhost kernel support
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer Jianfeng Tan
@ 2016-12-02 14:31 ` Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features Jianfeng Tan
` (4 subsequent siblings)
6 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-02 14:31 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
This patch add support vhost kernel support under vhost abstraction
layer.
Three main hook functions are added:
- vhost_kernel_setup() to open char device, each vq pair (Rx and Tx)
would need open it once;
- vhost_kernel_ioctl() to communicate control messages with vhost
kernel module;
- vhost_kernel_enable_queue_pair() to open tap device and set it
as the backend of corresonding vhost fd (that is to say, vq pair).
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_user/vhost.c | 21 +-
drivers/net/virtio/virtio_user/vhost.h | 5 +
drivers/net/virtio/virtio_user/vhost_kernel.c | 413 ++++++++++++++++++++++++++
4 files changed, 439 insertions(+), 1 deletion(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 17f7129..f671f1f 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -61,6 +61,7 @@ endif
ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
endif
diff --git a/drivers/net/virtio/virtio_user/vhost.c b/drivers/net/virtio/virtio_user/vhost.c
index 09e2e92..5384992 100644
--- a/drivers/net/virtio/virtio_user/vhost.c
+++ b/drivers/net/virtio/virtio_user/vhost.c
@@ -56,7 +56,7 @@ vhost_internal_valid_vid(int vid)
static int
vhost_internal_alloc(void)
{
- int i;
+ int i, q;
for (i = 0; i < VHOST_MAX_DEVICES; ++i)
if (internals[i].ops == NULL)
@@ -69,16 +69,32 @@ vhost_internal_alloc(void)
internals[i].vhostfd = -1;
+ for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
+ internals[i].vhostfds[q] = -1;
+ internals[i].tapfds[q] = -1;
+ }
+
return 0;
}
static void
vhost_internal_free(int id)
{
+ int q;
+
internals[id].ops = NULL;
if (internals[id].vhostfd >= 0)
close(internals[id].vhostfd);
+
+ for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
+ if (internals[id].vhostfds[q] >= 0)
+ close(internals[id].vhostfds[q]);
+ if (internals[id].tapfds[q] >= 0)
+ close(internals[id].tapfds[q]);
+ }
+ if (internals[id].ifname)
+ free(internals[id].ifname);
}
@@ -107,6 +123,9 @@ vhost_setup(const char *path)
if (is_vhost_user_by_type(path)) {
ret = vhost_ops_user.setup(&internals[vid], path);
internals[vid].ops = &vhost_ops_user;
+ } else {
+ ret = vhost_ops_kernel.setup(&internals[vid], path);
+ internals[vid].ops = &vhost_ops_kernel;
}
if (ret < 0)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index b476ecc..b6fd092 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -114,6 +114,10 @@ struct vhost_internal {
int vhostfd;
/* for vhost-kernel */
+ char *ifname;
+#define VHOST_KERNEL_MAX_QUEUES 8
+ int vhostfds[VHOST_KERNEL_MAX_QUEUES];
+ int tapfds[VHOST_KERNEL_MAX_QUEUES];
};
typedef int (*vhost_setup_t)(struct vhost_internal *internal,
@@ -132,6 +136,7 @@ struct vhost_ops {
};
struct vhost_ops vhost_ops_user;
+struct vhost_ops vhost_ops_kernel;
int vhost_setup(const char *path);
int vhost_call(int vid, 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
new file mode 100644
index 0000000..9a9e8bd
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -0,0 +1,413 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_eal_memconfig.h>
+
+#include "vhost.h"
+
+struct vhost_memory_kernel {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* vhost kernel ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#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)
+
+/* TUN ioctls */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
+#define TUNSETVNETLE _IOW('T', 220, int)
+#define TUNSETVNETBE _IOW('T', 222, int)
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
+/* Constants */
+#define TUN_DEF_SNDBUF (1ull << 20)
+#define PATH_NET_TUN "/dev/net/tun"
+#define VHOST_KERNEL_MAX_REGIONS 64
+
+static uint64_t vhost_req_user_to_kernel[] = {
+ [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
+ [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,
+ [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
+ [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
+ [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
+ [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
+};
+
+/* By default, vhost kernel module allows 64 regions, but DPDK allows
+ * 256 segments. As a relief, below function merges those virtually
+ * adjacent memsegs into one region.
+ */
+static struct vhost_memory_kernel *
+prepare_vhost_memory_kernel(void)
+{
+ uint32_t i, j, k = 0;
+ struct rte_memseg *seg;
+ struct vhost_memory_region *mr;
+ struct vhost_memory_kernel *vm;
+
+ vm = malloc(sizeof(struct vhost_memory_kernel) +
+ VHOST_KERNEL_MAX_REGIONS *
+ sizeof(struct vhost_memory_region));
+
+ for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
+ seg = &rte_eal_get_configuration()->mem_config->memseg[i];
+ if (!seg->addr)
+ break;
+
+ int new_region = 1;
+
+ for (j = 0; j < k; ++j) {
+ mr = &vm->regions[j];
+
+ if (mr->userspace_addr + mr->memory_size ==
+ (uint64_t)seg->addr) {
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+
+ if ((uint64_t)seg->addr + seg->len ==
+ mr->userspace_addr) {
+ mr->guest_phys_addr = (uint64_t)seg->addr;
+ mr->userspace_addr = (uint64_t)seg->addr;
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+ }
+
+ if (new_region == 0)
+ continue;
+
+ mr = &vm->regions[k++];
+ mr->guest_phys_addr = (uint64_t)seg->addr; /* use vaddr here! */
+ mr->userspace_addr = (uint64_t)seg->addr;
+ mr->memory_size = seg->len;
+ mr->mmap_offset = 0;
+
+ if (k >= VHOST_KERNEL_MAX_REGIONS) {
+ free(vm);
+ return NULL;
+ }
+ }
+
+ vm->nregions = k;
+ vm->padding = 0;
+ return vm;
+}
+
+static const uint64_t 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);
+
+static int
+vhost_kernel_ioctl(struct vhost_internal *internal,
+ enum vhost_user_request req,
+ void *arg)
+{
+ int i, ret = -1;
+ uint64_t req_kernel;
+ struct vhost_memory_kernel *vm = NULL;
+
+ req_kernel = vhost_req_user_to_kernel[req];
+
+ if (req_kernel == VHOST_SET_MEM_TABLE) {
+ vm = prepare_vhost_memory_kernel();
+ if (!vm)
+ return -1;
+ arg = (void *)vm;
+ }
+
+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
+ if (req_kernel == VHOST_SET_FEATURES)
+ *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
+
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
+ if (internal->vhostfds[i] < 0)
+ continue;
+
+ ret = ioctl(internal->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+
+ if (!ret && req_kernel == VHOST_SET_FEATURES)
+ internal->features = *((uint64_t *)arg);
+
+ /* 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 (!ret && req_kernel == VHOST_GET_FEATURES)
+ *((uint64_t *)arg) |= guest_offloads_mask;
+
+ if (vm)
+ free(vm);
+
+ return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost kernel backend.
+ * @param path
+ * - The path to vhost net (kernel) character file.
+ *
+ * @return
+ * - (-1) if fail to set up;
+ * - (>=0) if successful.
+ */
+static int
+vhost_kernel_setup(struct vhost_internal *internal, const char *path)
+{
+ int vhostfd;
+ uint32_t q;
+ uint32_t queue_pairs = 1;
+
+ for (q = 0; q < queue_pairs; ++q) {
+ vhostfd = open(path, O_RDWR);
+ if (vhostfd < 0) {
+ PMD_DRV_LOG(ERR, "fail to open %s, %s",
+ path, strerror(errno));
+ return -1;
+ }
+
+ internal->vhostfds[q] = vhostfd;
+ }
+
+ return 0;
+}
+
+static int
+_vhost_kernel_enable_queue_pair(int vhostfd, int tapfd)
+{
+ struct vhost_vring_file f;
+
+ f.fd = tapfd;
+ f.index = 0;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ f.index = 1;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_enable_queue_pair(struct vhost_internal *internal,
+ uint16_t pair_idx,
+ int enable)
+{
+ unsigned int offload =
+ TUN_F_CSUM |
+ TUN_F_TSO4 |
+ TUN_F_TSO6 |
+ TUN_F_TSO_ECN |
+ TUN_F_UFO;
+ unsigned int features;
+ int sndbuf = TUN_DEF_SNDBUF;
+ struct ifreq ifr;
+ int hdr_size;
+ int vhostfd;
+ int tapfd;
+ int req_mq;
+
+
+ vhostfd = internal->vhostfds[pair_idx];
+
+ if (!enable) {
+ if (internal->tapfds[pair_idx]) {
+ close(internal->tapfds[pair_idx]);
+ internal->tapfds[pair_idx] = -1;
+ }
+ return _vhost_kernel_enable_queue_pair(vhostfd, -1);
+ }
+
+ if ((internal->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
+ (internal->features & (1ULL << VIRTIO_F_VERSION_1)))
+ hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ else
+ hdr_size = sizeof(struct virtio_net_hdr);
+
+ req_mq = !!(internal->features & (1ULL << VIRTIO_NET_F_MQ));
+
+ /* TODO:
+ * 1. get/set offload capability, tap_probe_has_ufo, tap_fd_set_offload
+ * 2. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
+ * 3. get number of memory regions from vhost module parameter
+ * max_mem_regions, supported in newer version linux kernel
+ */
+ 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;
+ }
+
+ /* Construct ifr */
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ if (ioctl(tapfd, TUNGETFEATURES, &features) == -1) {
+ PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
+ goto error;
+ }
+ if (features & IFF_ONE_QUEUE)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+
+ /* Let tap instead of vhost-net handle vnet header, as the latter does
+ * not support offloading. And in this case, we should not set feature
+ * bit VHOST_NET_F_VIRTIO_NET_HDR.
+ */
+ if (features & IFF_VNET_HDR) {
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
+ goto error;
+ }
+
+ if (req_mq) {
+ if (features & IFF_MULTI_QUEUE)
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+ else {
+ PMD_DRV_LOG(ERR, "multiqueue not supported by kernel");
+ goto error;
+ }
+ }
+
+ if (internal->ifname)
+ strncpy(ifr.ifr_name, internal->ifname, IFNAMSIZ);
+ else
+ strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
+ if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
+ PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ fcntl(tapfd, F_SETFL, O_NONBLOCK);
+
+ if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (_vhost_kernel_enable_queue_pair(vhostfd, tapfd) < 0)
+ goto error;
+
+ /* Only try best to set offload */
+ if (ioctl(tapfd, TUNSETOFFLOAD, offload) != 0)
+ PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s",
+ strerror(errno));
+
+ internal->tapfds[pair_idx] = tapfd;
+ if (!internal->ifname)
+ internal->ifname = strdup(ifr.ifr_name);
+
+ return 0;
+error:
+ return -1;
+}
+
+struct vhost_ops vhost_ops_kernel = {
+ .setup = vhost_kernel_setup,
+ .control = vhost_kernel_ioctl,
+ .enable_qp = vhost_kernel_enable_queue_pair
+};
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 2/3] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2016-12-02 14:31 ` Jianfeng Tan
2016-12-08 8:32 ` Yuanhan Liu
2016-12-02 14:44 ` [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Thomas Monjalon
` (3 subsequent siblings)
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-02 14:31 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Before the commit 86d59b21468a ("net/virtio: support LRO"), features
in virtio PMD, is decided and properly set at device initialization
and will not be changed. But afterward, features could be changed in
virtio_dev_configure(), and will be re-negotiated if it's changed.
In virtio_user, host features is obtained at device initialization
only once, but we did not store it. So the added feature bits in
re-negotiation will fail.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 2 +-
drivers/net/virtio/virtio_user/virtio_user_dev.h | 1 +
drivers/net/virtio/virtio_user_ethdev.c | 4 ++--
3 files changed, 4 insertions(+), 3 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 3aef5f6..a9157a9 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -224,7 +224,7 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
}
if (vhost_call(dev->vid, VHOST_USER_GET_FEATURES,
- &dev->features) < 0) {
+ &dev->host_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
}
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 80efb6e..d219432 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -47,6 +47,7 @@ struct virtio_user_dev {
uint32_t queue_pairs;
uint32_t queue_size;
uint64_t features;
+ uint64_t host_features;
uint8_t status;
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 406beea..cfe2bfc 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -117,7 +117,7 @@ virtio_user_get_features(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- return dev->features;
+ return dev->host_features;
}
static void
@@ -125,7 +125,7 @@ virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- dev->features = features;
+ dev->features = features & dev->host_features;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
` (2 preceding siblings ...)
2016-12-02 14:31 ` [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features Jianfeng Tan
@ 2016-12-02 14:44 ` Thomas Monjalon
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
` (2 subsequent siblings)
6 siblings, 0 replies; 72+ messages in thread
From: Thomas Monjalon @ 2016-12-02 14:44 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, yuanhan.liu, ferruh.yigit, cunming.liang
2016-12-02 14:31, Jianfeng Tan:
> In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
> as the backend). The path to go with a vhost-kernel backend has been
> dropped for bad performance comparing to vhost-user and code simplicity.
>
> But after a second thought, virtio_user + vhost-kernel is a good
> candidate as an exceptional path, such as KNI, which exchanges packets
> with kernel networking stack.
> - maintenance: vhost-net (kernel) is upstreamed and extensively used
> kernel module. We don't need any out-of-tree module like KNI.
> - performance: as with KNI, this solution would use one or more
> kthreads to send/receive packets from user space DPDK applications,
> which has little impact on user space polling thread (except that
> it might enter into kernel space to wake up those kthreads if
> necessary.
> - features: vhost-net is born to be a networking solution, which has
> lots of networking related featuers, like multi queue, tso, multi-seg
> mbuf, etc.
That's a really interesting trial.
Have I already said that I don't like KNI? ;)
> Known issues for current version:
> - Multiqueue not supported yet.
> - Offloading is completely enabled yet; to enhance, we will translate
Is a "not" missing in this sentence?
> the virtio header info into mbuf metadata when receiving packets
> from kernel; and translate mbuf metadata info into virtio header
> when sending packets to kernel.
So what is the ambition for 17.02?
Do you think it could be integrated?
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer
2016-12-02 14:31 ` [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer Jianfeng Tan
@ 2016-12-08 7:21 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-08 7:21 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 02, 2016 at 02:31:13PM +0000, Jianfeng Tan wrote:
> To support vhost kernel as the backend of net_virtio_user in comming
> patches, we abstract a vhost layer to hide all vhost_user functions.
> - Move vhost_user specific structs and macros into vhost_user.c,
> and only keep common definitions in vhost.h;
Do that in another patch.
> - Add a struct vhost_internal, and an array to store vhost_user and
> vhost_kernel backends; in multiqueue case, vhost_user has only
> one vhost FD, but vhost_kernel would have multiple FDs (equal to
> # of queues), so we turn to use an id to index the backend info;
It's okay to add a struct for that, but what's the point of referencing
it by an id? Instead, you could directly allocate one and return it.
This would also remove the hardcoded limit: 8 devices at most.
> - Add a struct vhost_ops depending on different type of backends.
Firstly, the "control" method is not well named, IMO. Something like
"send_request" is better.
And there is another way to achieve that: instead of introducing one
callback (control, or send_request) for all requests, you could
define one callback for each request. QEMU switched to that way
few releases ago. In that way, you don't have to do the request
translation at least.
Last, please make one patch for each of 3 items. It helps review.
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features
2016-12-02 14:31 ` [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features Jianfeng Tan
@ 2016-12-08 8:32 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-08 8:32 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 02, 2016 at 02:31:15PM +0000, Jianfeng Tan wrote:
> Before the commit 86d59b21468a ("net/virtio: support LRO"), features
> in virtio PMD, is decided and properly set at device initialization
> and will not be changed. But afterward, features could be changed in
> virtio_dev_configure(), and will be re-negotiated if it's changed.
>
> In virtio_user, host features is obtained at device initialization
> only once, but we did not store it. So the added feature bits in
> re-negotiation will fail.
I think you misunderstood the features negotiation logic here, since
the beginning.
The feature negotiation is a work of 3 parts (the virtio device, the
virtio driver and the vhost). Each part has it's own feature bits
to claim what kind of features it supports.
On the init stage, the driver will have a sync with the device, and
that's what the "get_features" and "set_features" for. Once they have
come to an agreement on the negotiated features, it will have a
last sync with the vhost backend, through the GET_FEATURES and
SET_FEATURES request.
Last, we would have a features bits that would work for all of the
3 parts.
That said, you should not mix the driver's features bits with the
device one. It's Okay to introduce host_features, but it should not
be in the driver layer, it should be in the device layer. And naming
it to something like "device_features" is better, IMO.
And maybe you should re-visit the whole virtio_user features negotiation
logic, to see what can be improved.
Besides, I would put the bug fixing patch in the first of a series,
so that it has minimal chance to introduce conflics while backporting
it to a stable branch.
--yliu
>
> Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> ---
> drivers/net/virtio/virtio_user/virtio_user_dev.c | 2 +-
> drivers/net/virtio/virtio_user/virtio_user_dev.h | 1 +
> drivers/net/virtio/virtio_user_ethdev.c | 4 ++--
> 3 files changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> index 3aef5f6..a9157a9 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> @@ -224,7 +224,7 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
> }
>
> if (vhost_call(dev->vid, VHOST_USER_GET_FEATURES,
> - &dev->features) < 0) {
> + &dev->host_features) < 0) {
> PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
> return -1;
> }
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> index 80efb6e..d219432 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> @@ -47,6 +47,7 @@ struct virtio_user_dev {
> uint32_t queue_pairs;
> uint32_t queue_size;
> uint64_t features;
> + uint64_t host_features;
> uint8_t status;
> uint8_t mac_addr[ETHER_ADDR_LEN];
> char path[PATH_MAX];
> diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
> index 406beea..cfe2bfc 100644
> --- a/drivers/net/virtio/virtio_user_ethdev.c
> +++ b/drivers/net/virtio/virtio_user_ethdev.c
> @@ -117,7 +117,7 @@ virtio_user_get_features(struct virtio_hw *hw)
> {
> struct virtio_user_dev *dev = virtio_user_get_dev(hw);
>
> - return dev->features;
> + return dev->host_features;
> }
>
> static void
> @@ -125,7 +125,7 @@ virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
> {
> struct virtio_user_dev *dev = virtio_user_get_dev(hw);
>
> - dev->features = features;
> + dev->features = features & dev->host_features;
> }
>
> static uint8_t
> --
> 2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 0/7] virtio_user as an alternative exception path
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
` (3 preceding siblings ...)
2016-12-02 14:44 ` [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Thomas Monjalon
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
` (6 more replies)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
6 siblings, 7 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
v2: (Lots of them are from yuanhan's comment)
- Add offloding feature.
- Add multiqueue support.
- Add a new patch to postpone the sending of driver ok notification.
- Put fix patch ahead of the whole patch series.
- Split original 0001 patch into 0003 and 0004 patches.
- Remove the original vhost_internal design, just add those into
struct virtio_user_dev for simplicity.
- Reword "control" to "send_request".
- Reword "host_features" to "device_features".
In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
as the backend). The path to go with a vhost-kernel backend has been
dropped for bad performance comparing to vhost-user and code simplicity.
But after a second thought, virtio_user + vhost-kernel is a good
candidate as an exceptional path, such as KNI, which exchanges packets
with kernel networking stack.
- maintenance: vhost-net (kernel) is upstreamed and extensively used
kernel module. We don't need any out-of-tree module like KNI.
- performance: as with KNI, this solution would use one or more
kthreads to send/receive packets from user space DPDK applications,
which has little impact on user space polling thread (except that
it might enter into kernel space to wake up those kthreads if
necessary).
- features: vhost-net is born to be a networking solution, which has
lots of networking related featuers, like multi queue, tso, multi-seg
mbuf, etc.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Jianfeng Tan (7):
net/virtio_user: fix wrongly set features
net/virtio_user: postpone DRIVER OK notification
net/virtio_user: move vhost user specific code
net/virtio_user: abstract virtio user backend ops
net/virtio_user: add vhost kernel support
net/virtio_user: enable offloading
net/virtio_user: enable multiqueue with vhost kernel
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_ethdev.c | 3 +-
drivers/net/virtio/virtio_user/vhost.h | 51 +--
drivers/net/virtio/virtio_user/vhost_kernel.c | 476 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost_user.c | 86 ++--
drivers/net/virtio/virtio_user/virtio_user_dev.c | 122 +++---
drivers/net/virtio/virtio_user/virtio_user_dev.h | 16 +-
drivers/net/virtio/virtio_user_ethdev.c | 4 +-
8 files changed, 642 insertions(+), 117 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 1/7] net/virtio_user: fix wrongly set features
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification Jianfeng Tan
` (5 subsequent siblings)
6 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Before the commit 86d59b21468a ("net/virtio: support LRO"), features
in virtio PMD, is decided and properly set at device initialization
and will not be changed. But afterward, features could be changed in
virtio_dev_configure(), and will be re-negotiated if it's changed.
In virtio_user, device features is obtained at driver probe phase
only once, but we did not store it. So the added feature bits in
re-negotiation will fail.
To fix it, we store it down, and will be used to feature negotiation
either at device initialization phase or device configure phase.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 31 +++++++++++-------------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 +++-
drivers/net/virtio/virtio_user_ethdev.c | 4 +--
3 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index e239e0e..af5d5be 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -228,29 +228,26 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
}
if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
- &dev->features) < 0) {
+ &dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
}
if (dev->mac_specified)
- dev->features |= (1ull << VIRTIO_NET_F_MAC);
+ dev->device_features |= (1ull << VIRTIO_NET_F_MAC);
- if (!cq) {
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
- /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
- dev->features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
- dev->features &= ~(1ull << VIRTIO_NET_F_MQ);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
- } else {
- /* vhost user backend does not need to know ctrl-q, so
- * actually we need add this bit into features. However,
- * DPDK vhost-user does send features with this bit, so we
- * check it instead of OR it for now.
+ if (cq) {
+ /* device does not really need to know anything about CQ,
+ * so if necessary, we just claim to support CQ
*/
- if (!(dev->features & (1ull << VIRTIO_NET_F_CTRL_VQ)))
- PMD_INIT_LOG(INFO, "vhost does not support ctrl-q");
+ dev->device_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
+ } else {
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
+ /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_MQ);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
if (dev->max_queue_pairs > 1) {
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 33690b5..28fc788 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -46,7 +46,10 @@ struct virtio_user_dev {
uint32_t max_queue_pairs;
uint32_t queue_pairs;
uint32_t queue_size;
- uint64_t features;
+ uint64_t features; /* the negotiated features with driver,
+ * and will be sync with device
+ */
+ uint64_t device_features; /* supported features by device */
uint8_t status;
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 406beea..c5db719 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -117,7 +117,7 @@ virtio_user_get_features(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- return dev->features;
+ return dev->device_features;
}
static void
@@ -125,7 +125,7 @@ virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- dev->features = features;
+ dev->features = features & dev->device_features;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
[not found] ` <20161226062719.GA19288@yliu-dev.sh.intel.com>
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
` (4 subsequent siblings)
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
In driver probe phase, we obtain device information; and then virtio
driver will initialize device and stores info, like negotiated
features, in vhost_user layer; finally, vhost_user gets DRIVER_OK
notification from virtio driver, and then sync with backend device.
Previously, DRIVER_OK could be sent twice: 1. when ether layer invokes
eth_device_init to initialize device; 2. when user configures it with
different configuration from that of previous.
Since we can only depend on DRIVER_OK notification to sync with backend
device, we postpone it to virtio_dev_start when everything is settled.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_ethdev.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/drivers/net/virtio/virtio_ethdev.c b/drivers/net/virtio/virtio_ethdev.c
index 079fd6c..d54b1bb 100644
--- a/drivers/net/virtio/virtio_ethdev.c
+++ b/drivers/net/virtio/virtio_ethdev.c
@@ -1276,7 +1276,6 @@ virtio_init_device(struct rte_eth_dev *eth_dev, uint64_t req_features)
ret = virtio_alloc_queues(eth_dev);
if (ret < 0)
return ret;
- vtpci_reinit_complete(hw);
if (pci_dev)
PMD_INIT_LOG(DEBUG, "port %d vendorID=0x%x deviceID=0x%x",
@@ -1474,6 +1473,8 @@ virtio_dev_start(struct rte_eth_dev *dev)
struct virtnet_tx *txvq __rte_unused;
struct virtio_hw *hw = dev->data->dev_private;
+ vtpci_reinit_complete(hw);
+
/* check if lsc interrupt feature is enabled */
if (dev->data->dev_conf.intr_conf.lsc) {
if (!(dev->data->dev_flags & RTE_ETH_DEV_INTR_LSC)) {
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-26 6:28 ` Yuanhan Liu
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
` (3 subsequent siblings)
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
To support vhost kernel as the backend of net_virtio_user in coming
patches, we move vhost_user specific structs and macros into
vhost_user.c, and only keep common definitions in vhost.h.
Besides, remove VHOST_USER_MQ feature check, it will be added back
in following multiqueue patch.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 36 ------------------------
drivers/net/virtio/virtio_user/vhost_user.c | 32 +++++++++++++++++++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 9 ------
3 files changed, 32 insertions(+), 45 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 7adb55f..e54ac35 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -42,8 +42,6 @@
#include "../virtio_logs.h"
#include "../virtqueue.h"
-#define VHOST_MEMORY_MAX_NREGIONS 8
-
struct vhost_vring_state {
unsigned int index;
unsigned int num;
@@ -105,40 +103,6 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-struct vhost_memory {
- uint32_t nregions;
- uint32_t padding;
- struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
-};
-
-struct vhost_user_msg {
- enum vhost_user_request request;
-
-#define VHOST_USER_VERSION_MASK 0x3
-#define VHOST_USER_REPLY_MASK (0x1 << 2)
- uint32_t flags;
- uint32_t size; /* the following payload size */
- union {
-#define VHOST_USER_VRING_IDX_MASK 0xff
-#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
- uint64_t u64;
- struct vhost_vring_state state;
- struct vhost_vring_addr addr;
- struct vhost_memory memory;
- } payload;
- int fds[VHOST_MEMORY_MAX_NREGIONS];
-} __attribute((packed));
-
-#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
-#define VHOST_USER_PAYLOAD_SIZE \
- (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
-
-/* The version of the protocol we support */
-#define VHOST_USER_VERSION 0x1
-
-#define VHOST_USER_F_PROTOCOL_FEATURES 30
-#define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
-
int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
int vhost_user_setup(const char *path);
int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 082e821..295ce16 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -42,6 +42,38 @@
#include "vhost.h"
+/* The version of the protocol we support */
+#define VHOST_USER_VERSION 0x1
+
+#define VHOST_MEMORY_MAX_NREGIONS 8
+struct vhost_memory {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
+};
+
+struct vhost_user_msg {
+ enum vhost_user_request request;
+
+#define VHOST_USER_VERSION_MASK 0x3
+#define VHOST_USER_REPLY_MASK (0x1 << 2)
+ uint32_t flags;
+ uint32_t size; /* the following payload size */
+ union {
+#define VHOST_USER_VRING_IDX_MASK 0xff
+#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
+ uint64_t u64;
+ struct vhost_vring_state state;
+ struct vhost_vring_addr addr;
+ struct vhost_memory memory;
+ } payload;
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+} __attribute((packed));
+
+#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
+#define VHOST_USER_PAYLOAD_SIZE \
+ (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
+
static int
vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
{
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index af5d5be..66a8ffe 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -151,8 +151,6 @@ virtio_user_start_device(struct virtio_user_dev *dev)
* and VIRTIO_NET_F_MAC is stripped.
*/
features = dev->features;
- if (dev->max_queue_pairs > 1)
- features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
@@ -250,13 +248,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
- if (dev->max_queue_pairs > 1) {
- if (!(dev->features & VHOST_USER_MQ)) {
- PMD_INIT_LOG(ERR, "MQ not supported by the backend");
- return -1;
- }
- }
-
return 0;
}
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
` (2 preceding siblings ...)
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-26 6:41 ` Yuanhan Liu
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
` (2 subsequent siblings)
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Add a struct virtio_user_backend_ops to abstract three kinds of backend
operations:
- setup, create the unix socket connection;
- send_request, sync messages with backend;
- enable_qp, enable some queue pair.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 19 +++++--
drivers/net/virtio/virtio_user/vhost_user.c | 54 ++++++++-----------
drivers/net/virtio/virtio_user/virtio_user_dev.c | 66 +++++++++++++++++-------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 7 +++
4 files changed, 93 insertions(+), 53 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index e54ac35..bd67133 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -103,8 +103,21 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
-int vhost_user_setup(const char *path);
-int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
+struct virtio_user_dev;
+typedef int (*vhost_setup_t)(struct virtio_user_dev *dev);
+typedef int (*vhost_send_request_t)(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg);
+typedef int (*vhost_enable_qp_t)(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable);
+
+struct virtio_user_backend_ops {
+ vhost_setup_t setup;
+ vhost_send_request_t send_request;
+ vhost_enable_qp_t enable_qp;
+};
+
+struct virtio_user_backend_ops ops_user;
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 295ce16..faea1b6 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -41,6 +41,7 @@
#include <errno.h>
#include "vhost.h"
+#include "virtio_user_dev.h"
/* The version of the protocol we support */
#define VHOST_USER_VERSION 0x1
@@ -255,24 +256,10 @@ prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
static struct vhost_user_msg m;
-static const char * const vhost_msg_strings[] = {
- [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
- [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
- [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
- [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
- [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL",
- [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM",
- [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE",
- [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE",
- [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR",
- [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK",
- [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
- [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE",
- NULL,
-};
-
-int
-vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
+static int
+vhost_user_sock(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
{
struct vhost_user_msg msg;
struct vhost_vring_file *file = 0;
@@ -280,11 +267,9 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
int fds[VHOST_MEMORY_MAX_NREGIONS];
int fd_num = 0;
int i, len;
+ int vhostfd = dev->vhostfd;
RTE_SET_USED(m);
- RTE_SET_USED(vhost_msg_strings);
-
- PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
msg.request = req;
msg.flags = VHOST_USER_VERSION;
@@ -355,8 +340,8 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
len = VHOST_USER_HDR_SIZE + msg.size;
if (vhost_user_write(vhostfd, &msg, len, fds, fd_num) < 0) {
- PMD_DRV_LOG(ERR, "%s failed: %s",
- vhost_msg_strings[req], strerror(errno));
+ PMD_DRV_LOG(ERR, "fail to write vhost user msg: %s",
+ strerror(errno));
return -1;
}
@@ -403,15 +388,13 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
/**
* Set up environment to talk with a vhost user backend.
- * @param path
- * - The path to vhost user unix socket file.
*
* @return
* - (-1) if fail to set up;
* - (>=0) if successful, and it is the fd to vhostfd.
*/
-int
-vhost_user_setup(const char *path)
+static int
+vhost_user_setup(struct virtio_user_dev *dev)
{
int fd;
int flag;
@@ -429,7 +412,7 @@ vhost_user_setup(const char *path)
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
close(fd);
@@ -439,8 +422,10 @@ vhost_user_setup(const char *path)
return fd;
}
-int
-vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
+static int
+vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
{
int i;
@@ -450,10 +435,15 @@ vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
.num = enable,
};
- if (vhost_user_sock(vhostfd,
- VHOST_USER_SET_VRING_ENABLE, &state))
+ if (vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state))
return -1;
}
return 0;
}
+
+struct virtio_user_backend_ops ops_user = {
+ .setup = vhost_user_setup,
+ .send_request = vhost_user_sock,
+ .enable_qp = vhost_user_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 66a8ffe..a818c29 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -39,6 +39,9 @@
#include <sys/mman.h>
#include <unistd.h>
#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "vhost.h"
#include "virtio_user_dev.h"
@@ -64,7 +67,7 @@ virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = callfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_CALL, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_CALL, &file);
dev->callfds[queue_sel] = callfd;
return 0;
@@ -88,12 +91,12 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.index = queue_sel;
state.num = vring->num;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_NUM, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
state.num = 0; /* no reservation */
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_BASE, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_ADDR, &addr);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_ADDR, &addr);
/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
* lastly because vhost depends on this msg to judge if
@@ -106,7 +109,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = kickfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_KICK, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_KICK, &file);
dev->kickfds[queue_sel] = kickfd;
return 0;
@@ -147,18 +150,17 @@ virtio_user_start_device(struct virtio_user_dev *dev)
goto error;
/* Step 1: set features
- * Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * and VIRTIO_NET_F_MAC is stripped.
+ * Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init.
*/
features = dev->features;
features &= ~(1ull << VIRTIO_NET_F_MAC);
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
PMD_DRV_LOG(INFO, "set features: %" PRIx64, features);
/* Step 2: share memory regions */
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_MEM_TABLE, NULL);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_MEM_TABLE, NULL);
if (ret < 0)
goto error;
@@ -169,7 +171,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 4: enable queues
* we enable the 1st queue pair by default.
*/
- vhost_user_enable_queue_pair(dev->vhostfd, 0, 1);
+ dev->ops->enable_qp(dev, 0, 1);
return 0;
error:
@@ -179,7 +181,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
int virtio_user_stop_device(struct virtio_user_dev *dev)
{
- return vhost_user_sock(dev->vhostfd, VHOST_USER_RESET_OWNER, NULL);
+ return dev->ops->send_request(dev, VHOST_USER_RESET_OWNER, NULL);
}
static inline void
@@ -203,6 +205,36 @@ parse_mac(struct virtio_user_dev *dev, const char *mac)
}
}
+static int
+is_vhost_user_by_type(const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == -1)
+ return 0;
+
+ return S_ISSOCK(sb.st_mode);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+ uint32_t i;
+
+ dev->vhostfd = -1;
+ for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
+ dev->kickfds[i] = -1;
+ dev->callfds[i] = -1;
+ }
+
+ if (is_vhost_user_by_type(dev->path)) {
+ dev->ops = &ops_user;
+ return dev->ops->setup(dev);
+ }
+
+ return -1;
+}
+
int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
int cq, int queue_size, const char *mac)
@@ -213,19 +245,17 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
dev->queue_size = queue_size;
dev->mac_specified = 0;
parse_mac(dev, mac);
- dev->vhostfd = -1;
- dev->vhostfd = vhost_user_setup(dev->path);
- if (dev->vhostfd < 0) {
+ if (virtio_user_dev_setup(dev) < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL) < 0) {
+ if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
+ if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
&dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
@@ -277,9 +307,9 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
}
for (i = 0; i < q_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 1);
+ ret |= dev->ops->enable_qp(dev, i, 1);
for (i = q_pairs; i < dev->max_queue_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ ret |= dev->ops->enable_qp(dev, i, 0);
dev->queue_pairs = q_pairs;
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 28fc788..503a496 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -37,9 +37,15 @@
#include <limits.h>
#include "../virtio_pci.h"
#include "../virtio_ring.h"
+#include "vhost.h"
struct virtio_user_dev {
+ /* for vhost_user backend */
int vhostfd;
+
+ /* for vhost_kernel backend */
+
+ /* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int mac_specified;
@@ -54,6 +60,7 @@ struct virtio_user_dev {
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
struct vring vrings[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
+ struct virtio_user_backend_ops *ops;
};
int virtio_user_start_device(struct virtio_user_dev *dev);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
` (3 preceding siblings ...)
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-26 7:44 ` Yuanhan Liu
2017-01-09 4:39 ` Jason Wang
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
6 siblings, 2 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
This patch add support vhost kernel as the backend for virtio_user.
Three main hook functions are added:
- vhost_kernel_setup() to open char device, each vq pair needs one
vhostfd;
- vhost_kernel_ioctl() to communicate control messages with vhost
kernel module;
- vhost_kernel_enable_queue_pair() to open tap device and set it
as the backend of corresonding vhost fd (that is to say, vq pair).
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_user/vhost.h | 2 +
drivers/net/virtio/virtio_user/vhost_kernel.c | 364 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 21 +-
drivers/net/virtio/virtio_user/virtio_user_dev.h | 4 +
5 files changed, 388 insertions(+), 4 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 97972a6..faeffb2 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -60,6 +60,7 @@ endif
ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
endif
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index bd67133..ffab13a 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -120,4 +120,6 @@ struct virtio_user_backend_ops {
};
struct virtio_user_backend_ops ops_user;
+struct virtio_user_backend_ops ops_kernel;
+
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
new file mode 100644
index 0000000..8984c5c
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -0,0 +1,364 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_eal_memconfig.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+
+struct vhost_memory_kernel {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* vhost kernel ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#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)
+
+/* TUN ioctls */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
+#define TUNSETVNETLE _IOW('T', 220, int)
+#define TUNSETVNETBE _IOW('T', 222, int)
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+
+/* Constants */
+#define TUN_DEF_SNDBUF (1ull << 20)
+#define PATH_NET_TUN "/dev/net/tun"
+#define VHOST_KERNEL_MAX_REGIONS 64
+
+static uint64_t vhost_req_user_to_kernel[] = {
+ [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
+ [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,
+ [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
+ [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
+ [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
+ [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
+};
+
+/* By default, vhost kernel module allows 64 regions, but DPDK allows
+ * 256 segments. As a relief, below function merges those virtually
+ * adjacent memsegs into one region.
+ */
+static struct vhost_memory_kernel *
+prepare_vhost_memory_kernel(void)
+{
+ uint32_t i, j, k = 0;
+ struct rte_memseg *seg;
+ struct vhost_memory_region *mr;
+ struct vhost_memory_kernel *vm;
+
+ vm = malloc(sizeof(struct vhost_memory_kernel) +
+ VHOST_KERNEL_MAX_REGIONS *
+ sizeof(struct vhost_memory_region));
+
+ for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
+ seg = &rte_eal_get_configuration()->mem_config->memseg[i];
+ if (!seg->addr)
+ break;
+
+ int new_region = 1;
+
+ for (j = 0; j < k; ++j) {
+ mr = &vm->regions[j];
+
+ if (mr->userspace_addr + mr->memory_size ==
+ (uint64_t)seg->addr) {
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+
+ if ((uint64_t)seg->addr + seg->len ==
+ mr->userspace_addr) {
+ mr->guest_phys_addr = (uint64_t)seg->addr;
+ mr->userspace_addr = (uint64_t)seg->addr;
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+ }
+
+ if (new_region == 0)
+ continue;
+
+ mr = &vm->regions[k++];
+ mr->guest_phys_addr = (uint64_t)seg->addr; /* use vaddr here! */
+ mr->userspace_addr = (uint64_t)seg->addr;
+ mr->memory_size = seg->len;
+ mr->mmap_offset = 0;
+
+ if (k >= VHOST_KERNEL_MAX_REGIONS) {
+ free(vm);
+ return NULL;
+ }
+ }
+
+ vm->nregions = k;
+ vm->padding = 0;
+ return vm;
+}
+
+static int
+vhost_kernel_ioctl(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
+{
+ int i, ret = -1;
+ uint64_t req_kernel;
+ struct vhost_memory_kernel *vm = NULL;
+
+ req_kernel = vhost_req_user_to_kernel[req];
+
+ if (req_kernel == VHOST_SET_MEM_TABLE) {
+ vm = prepare_vhost_memory_kernel();
+ if (!vm)
+ return -1;
+ arg = (void *)vm;
+ }
+
+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
+ if (req_kernel == VHOST_SET_FEATURES)
+ *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
+
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
+
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+
+ if (vm)
+ free(vm);
+
+ return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost kernel backend.
+ *
+ * @return
+ * - (-1) if fail to set up;
+ * - (>=0) if successful.
+ */
+static int
+vhost_kernel_setup(struct virtio_user_dev *dev)
+{
+ int vhostfd;
+ uint32_t q;
+
+ for (q = 0; q < dev->max_queue_pairs; ++q) {
+ vhostfd = open(dev->path, O_RDWR);
+ if (vhostfd < 0) {
+ PMD_DRV_LOG(ERR, "fail to open %s, %s",
+ dev->path, strerror(errno));
+ return -1;
+ }
+
+ dev->vhostfds[q] = vhostfd;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_set_backend(int vhostfd, int tapfd)
+{
+ struct vhost_vring_file f;
+
+ f.fd = tapfd;
+ f.index = 0;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ f.index = 1;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
+{
+ unsigned int features;
+ int sndbuf = TUN_DEF_SNDBUF;
+ struct ifreq ifr;
+ int hdr_size;
+ int vhostfd;
+ int tapfd;
+
+ vhostfd = dev->vhostfds[pair_idx];
+
+ if (!enable) {
+ if (dev->tapfds[pair_idx]) {
+ close(dev->tapfds[pair_idx]);
+ dev->tapfds[pair_idx] = -1;
+ }
+ return vhost_kernel_set_backend(vhostfd, -1);
+ } else if (dev->tapfds[pair_idx] >= 0) {
+ return 0;
+ }
+
+ if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
+ (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
+ hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ else
+ hdr_size = sizeof(struct virtio_net_hdr);
+
+ /* TODO:
+ * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
+ * 2. get number of memory regions from vhost module parameter
+ * max_mem_regions, supported in newer version linux kernel
+ */
+ 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;
+ }
+
+ /* Construct ifr */
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ if (ioctl(tapfd, TUNGETFEATURES, &features) == -1) {
+ PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
+ goto error;
+ }
+ if (features & IFF_ONE_QUEUE)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+
+ /* Let tap instead of vhost-net handle vnet header, as the latter does
+ * not support offloading. And in this case, we should not set feature
+ * bit VHOST_NET_F_VIRTIO_NET_HDR.
+ */
+ if (features & IFF_VNET_HDR) {
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
+ goto error;
+ }
+
+ if (dev->ifname)
+ strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
+ else
+ strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
+ if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
+ PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ fcntl(tapfd, F_SETFL, O_NONBLOCK);
+
+ if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (vhost_kernel_set_backend(vhostfd, tapfd) < 0)
+ goto error;
+
+ dev->tapfds[pair_idx] = tapfd;
+ if (!dev->ifname)
+ dev->ifname = strdup(ifr.ifr_name);
+
+ return 0;
+error:
+ return -1;
+}
+
+struct virtio_user_backend_ops ops_kernel = {
+ .setup = vhost_kernel_setup,
+ .send_request = vhost_kernel_ioctl,
+ .enable_qp = vhost_kernel_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index a818c29..c718b85 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -219,7 +219,7 @@ is_vhost_user_by_type(const char *path)
static int
virtio_user_dev_setup(struct virtio_user_dev *dev)
{
- uint32_t i;
+ uint32_t i, q;
dev->vhostfd = -1;
for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
@@ -227,12 +227,18 @@ virtio_user_dev_setup(struct virtio_user_dev *dev)
dev->callfds[i] = -1;
}
+ for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
+ dev->vhostfds[q] = -1;
+ dev->tapfds[q] = -1;
+ }
+
if (is_vhost_user_by_type(dev->path)) {
dev->ops = &ops_user;
- return dev->ops->setup(dev);
+ } else {
+ dev->ops = &ops_kernel;
}
- return -1;
+ return dev->ops->setup(dev);
}
int
@@ -284,7 +290,9 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
- uint32_t i;
+ uint32_t i, q;
+
+ dev->ops->send_request(dev, VHOST_USER_RESET_OWNER, NULL);
for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
close(dev->callfds[i]);
@@ -292,6 +300,11 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
}
close(dev->vhostfd);
+
+ for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
+ close(dev->vhostfds[q]);
+ close(dev->tapfds[q]);
+ }
}
static uint8_t
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 503a496..148b2e6 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -44,6 +44,10 @@ struct virtio_user_dev {
int vhostfd;
/* for vhost_kernel backend */
+ char *ifname;
+#define VHOST_KERNEL_MAX_QUEUES 8
+ int vhostfds[VHOST_KERNEL_MAX_QUEUES];
+ int tapfds[VHOST_KERNEL_MAX_QUEUES];
/* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
` (4 preceding siblings ...)
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
2016-12-26 7:53 ` Yuanhan Liu
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
6 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
When used with vhost kernel backend, we can offload at both directions.
- From vhost kernel to virtio_user, the offload is enabled so that
DPDK app can trust the flow is checksum-correct; and if DPDK app
sends it through another port, the checksum needs to be
recalculated or offloaded. It also applies to TSO.
- From virtio_user to vhost_kernel, the offload is enabled so that
kernel can trust the flow is L4-checksum-correct, no need to verify
it; if kernel will consume it, DPDK app should make sure the
l3-checksum is correctly set.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 61 ++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index 8984c5c..fb3c454 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -91,6 +91,13 @@ struct vhost_memory_kernel {
#define IFF_ATTACH_QUEUE 0x0200
#define IFF_DETACH_QUEUE 0x0400
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
/* Constants */
#define TUN_DEF_SNDBUF (1ull << 20)
#define PATH_NET_TUN "/dev/net/tun"
@@ -173,6 +180,28 @@ 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.
+ */
+static const uint64_t 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.
+ */
+static const uint64_t host_offloads_mask =
+ (1ULL << VIRTIO_NET_F_CSUM) |
+ (1ULL << VIRTIO_NET_F_HOST_TSO4) |
+ (1ULL << VIRTIO_NET_F_HOST_TSO6);
+
static int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
@@ -191,10 +220,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
arg = (void *)vm;
}
- /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
- if (req_kernel == VHOST_SET_FEATURES)
+ if (req_kernel == VHOST_SET_FEATURES) {
+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
*(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
+ /* VHOST kernel does not know about below flags */
+ *(uint64_t *)arg &= ~guest_offloads_mask;
+ *(uint64_t *)arg &= ~host_offloads_mask;
+ }
+
for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
if (dev->vhostfds[i] < 0)
continue;
@@ -204,6 +238,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
break;
}
+ if (!ret && req_kernel == VHOST_GET_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.
+ */
+ *((uint64_t *)arg) |= guest_offloads_mask;
+ *((uint64_t *)arg) |= host_offloads_mask;
+ }
+
if (vm)
free(vm);
@@ -271,6 +314,12 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
int hdr_size;
int vhostfd;
int tapfd;
+ unsigned int offload =
+ TUN_F_CSUM |
+ TUN_F_TSO4 |
+ TUN_F_TSO6 |
+ TUN_F_TSO_ECN |
+ TUN_F_UFO;
vhostfd = dev->vhostfds[pair_idx];
@@ -345,6 +394,14 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
goto error;
}
+ /* TODO: before set the offload capabilities, we'd better (1) check
+ * negotiated features to see if necessary to offload; (2) query tap
+ * to see if it supports the offload capabilities.
+ */
+ if (ioctl(tapfd, TUNSETOFFLOAD, offload) != 0)
+ PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s",
+ strerror(errno));
+
if (vhost_kernel_set_backend(vhostfd, tapfd) < 0)
goto error;
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v2 7/7] net/virtio_user: enable multiqueue with vhost kernel
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
` (5 preceding siblings ...)
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading Jianfeng Tan
@ 2016-12-23 7:14 ` Jianfeng Tan
6 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2016-12-23 7:14 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
With vhost kernel, to enable multiqueue, we need backend device
in kernel support multiqueue feature. Specifically, with tap
as the backend, as linux/Documentation/networking/tuntap.txt shows,
we check if tap supports IFF_MULTI_QUEUE feature.
And for vhost kernel, each queue pair has a vhost fd, and with a tap
fd binding this vhost fd. All tap fds are set with the same tap
interface name.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 67 +++++++++++++++++++++---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 1 +
2 files changed, 62 insertions(+), 6 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index fb3c454..67b382e 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -203,6 +203,29 @@ static const uint64_t host_offloads_mask =
(1ULL << VIRTIO_NET_F_HOST_TSO6);
static int
+tap_supporte_mq(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 & IFF_MULTI_QUEUE;
+}
+
+static int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
void *arg)
@@ -210,6 +233,8 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
int i, ret = -1;
uint64_t req_kernel;
struct vhost_memory_kernel *vm = NULL;
+ int vhostfd;
+ unsigned int queue_sel;
req_kernel = vhost_req_user_to_kernel[req];
@@ -229,13 +254,33 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
*(uint64_t *)arg &= ~host_offloads_mask;
}
- for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
- if (dev->vhostfds[i] < 0)
- continue;
+ switch (req_kernel) {
+ case VHOST_SET_VRING_NUM:
+ case VHOST_SET_VRING_ADDR:
+ case VHOST_SET_VRING_BASE:
+ case VHOST_GET_VRING_BASE:
+ case VHOST_SET_VRING_KICK:
+ case VHOST_SET_VRING_CALL:
+ queue_sel = *(unsigned int *)arg;
+ vhostfd = dev->vhostfds[queue_sel / 2];
+ *(unsigned int *)arg = queue_sel % 2;
+ PMD_DRV_LOG(DEBUG, "vhostfd=%d, index=%u",
+ vhostfd, *(unsigned int *)arg);
+ break;
+ default:
+ vhostfd = -1;
+ }
+ if (vhostfd == -1) {
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
- ret = ioctl(dev->vhostfds[i], req_kernel, arg);
- if (ret < 0)
- break;
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ ret = ioctl(vhostfd, req_kernel, arg);
}
if (!ret && req_kernel == VHOST_GET_FEATURES) {
@@ -245,6 +290,12 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
*/
*((uint64_t *)arg) |= guest_offloads_mask;
*((uint64_t *)arg) |= host_offloads_mask;
+
+ /* vhost_kernel will not declare this feature, but it does
+ * support multi-queue.
+ */
+ if (tap_supporte_mq())
+ *(uint64_t *)arg |= (1ull << VIRTIO_NET_F_MQ);
}
if (vm)
@@ -320,6 +371,7 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
TUN_F_TSO6 |
TUN_F_TSO_ECN |
TUN_F_UFO;
+ int req_mq = (dev->max_queue_pairs > 1);
vhostfd = dev->vhostfds[pair_idx];
@@ -373,6 +425,9 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
goto error;
}
+ if (req_mq)
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+
if (dev->ifname)
strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
else
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index c718b85..241f5bb 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -93,6 +93,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.num = vring->num;
dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
+ state.index = queue_sel;
state.num = 0; /* no reservation */
dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
@ 2016-12-26 6:28 ` Yuanhan Liu
2016-12-26 6:58 ` Tan, Jianfeng
0 siblings, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 6:28 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 23, 2016 at 07:14:22AM +0000, Jianfeng Tan wrote:
> To support vhost kernel as the backend of net_virtio_user in coming
> patches, we move vhost_user specific structs and macros into
> vhost_user.c, and only keep common definitions in vhost.h.
Good.
> Besides, remove VHOST_USER_MQ feature check, it will be added back
> in following multiqueue patch.
Why then?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
@ 2016-12-26 6:41 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 6:41 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 23, 2016 at 07:14:23AM +0000, Jianfeng Tan wrote:
> +typedef int (*vhost_setup_t)(struct virtio_user_dev *dev);
> +typedef int (*vhost_send_request_t)(struct virtio_user_dev *dev,
> + enum vhost_user_request req,
> + void *arg);
> +typedef int (*vhost_enable_qp_t)(struct virtio_user_dev *dev,
> + uint16_t pair_idx,
> + int enable);
I will not bother to define new types if they are just gonna be used once.
No strong perference here, just a suggestion.
...
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> index 28fc788..503a496 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> @@ -37,9 +37,15 @@
> #include <limits.h>
> #include "../virtio_pci.h"
> #include "../virtio_ring.h"
> +#include "vhost.h"
>
> struct virtio_user_dev {
> + /* for vhost_user backend */
> int vhostfd;
> +
> + /* for vhost_kernel backend */
> +
> + /* for both vhost_user and vhost_kernel */
By far (till this patch), vhost_kernel is not introduced at all, that I
will not mention it here. That said, I will leave this change to the patch
that actually enables vhost-kernel.
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
[not found] ` <20161226062719.GA19288@yliu-dev.sh.intel.com>
@ 2016-12-26 6:55 ` Tan, Jianfeng
2016-12-26 8:02 ` Yuanhan Liu
0 siblings, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2016-12-26 6:55 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, Yigit, Ferruh, Liang, Cunming
> -----Original Message-----
> From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> Sent: Monday, December 26, 2016 2:27 PM
> To: Tan, Jianfeng
> Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> Subject: Re: [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
>
> On Fri, Dec 23, 2016 at 07:14:21AM +0000, Jianfeng Tan wrote:
> > In driver probe phase, we obtain device information; and then virtio
> > driver will initialize device and stores info, like negotiated
> > features, in vhost_user layer; finally, vhost_user gets DRIVER_OK
> > notification from virtio driver, and then sync with backend device.
> >
> > Previously, DRIVER_OK could be sent twice: 1. when ether layer invokes
> > eth_device_init to initialize device; 2. when user configures it with
> > different configuration from that of previous.
>
> Yes, but that's wrong. Now only 1) will be taken.
>From code in virtio_dev_configure() as below, if hw_ip_checksum or enable_lro is configured, the device will be reset and re-initialized.
if (rxmode->hw_ip_checksum)
req_features |= (1ULL << VIRTIO_NET_F_GUEST_CSUM);
if (rxmode->enable_lro)
req_features |=
(1ULL << VIRTIO_NET_F_GUEST_TSO4) |
(1ULL << VIRTIO_NET_F_GUEST_TSO6);
/* if request features changed, reinit the device */
if (req_features != hw->req_guest_features) {
ret = virtio_init_device(dev, req_features);
if (ret < 0)
return ret;
}
>
> > Since we can only depend on DRIVER_OK notification to sync with backend
> > device, we postpone it to virtio_dev_start when everything is settled.
>
> I don't quite understand what you were going to fix here; you don't
> state it in the commit log after all. Normally, when everything is
> negotiated (when DRIVER_OK is set), we should not set it again, unless
> a reset has been happened.
I agree that DRIVER_OK should be set only if everything is settled. Under the case of hw_ip_checksum or enable_lro, a reset will be sent and the device will be re-initialized.
So my point is that since the configuration might be changed in dev_configure(), why not just send DRIVER_OK after everything is done.
>
> If you look at QEMU, qemu will simply ignore it once vhost has already
> started.
It does not apply here. The configuration is changed, and it cannot be ignored.
Thanks,
Jianfeng
>
> --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code
2016-12-26 6:28 ` Yuanhan Liu
@ 2016-12-26 6:58 ` Tan, Jianfeng
2016-12-26 7:57 ` Yuanhan Liu
0 siblings, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2016-12-26 6:58 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, Yigit, Ferruh, Liang, Cunming
> -----Original Message-----
> From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> Sent: Monday, December 26, 2016 2:28 PM
> To: Tan, Jianfeng
> Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> Subject: Re: [PATCH v2 3/7] net/virtio_user: move vhost user specific code
>
> On Fri, Dec 23, 2016 at 07:14:22AM +0000, Jianfeng Tan wrote:
> > To support vhost kernel as the backend of net_virtio_user in coming
> > patches, we move vhost_user specific structs and macros into
> > vhost_user.c, and only keep common definitions in vhost.h.
>
> Good.
>
> > Besides, remove VHOST_USER_MQ feature check, it will be added back
> > in following multiqueue patch.
>
> Why then?
Only vhost user recognizes this feature bit, and vhost kernel does not. My intension is to put those vhost user specific code inside vhost user. And in fact, I forget to add it back. I should add it back in this patch.
Thanks,
Jianfeng
>
> --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2016-12-26 7:44 ` Yuanhan Liu
2017-01-04 7:22 ` Tan, Jianfeng
2017-01-09 4:39 ` Jason Wang
1 sibling, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 7:44 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 23, 2016 at 07:14:24AM +0000, Jianfeng Tan wrote:
> + * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
^^^^^^^^^
I think it should be 2016.
> +/* By default, vhost kernel module allows 64 regions, but DPDK allows
> + * 256 segments. As a relief, below function merges those virtually
> + * adjacent memsegs into one region.
> + */
> +static struct vhost_memory_kernel *
> +prepare_vhost_memory_kernel(void)
> +{
> + uint32_t i, j, k = 0;
> + struct rte_memseg *seg;
> + struct vhost_memory_region *mr;
> + struct vhost_memory_kernel *vm;
> +
> + vm = malloc(sizeof(struct vhost_memory_kernel) +
> + VHOST_KERNEL_MAX_REGIONS *
> + sizeof(struct vhost_memory_region));
> +
> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
> + if (!seg->addr)
> + break;
> +
> + int new_region = 1;
> +
> + for (j = 0; j < k; ++j) {
> + mr = &vm->regions[j];
> +
> + if (mr->userspace_addr + mr->memory_size ==
> + (uint64_t)seg->addr) {
> + mr->memory_size += seg->len;
> + new_region = 0;
> + break;
> + }
> +
> + if ((uint64_t)seg->addr + seg->len ==
> + mr->userspace_addr) {
> + mr->guest_phys_addr = (uint64_t)seg->addr;
Be careful, such cast would break 32 bit OS build.
> +static int
> +vhost_kernel_ioctl(struct virtio_user_dev *dev,
> + enum vhost_user_request req,
> + void *arg)
> +{
> + int i, ret = -1;
> + uint64_t req_kernel;
> + struct vhost_memory_kernel *vm = NULL;
> +
> + req_kernel = vhost_req_user_to_kernel[req];
> +
> + if (req_kernel == VHOST_SET_MEM_TABLE) {
> + vm = prepare_vhost_memory_kernel();
> + if (!vm)
> + return -1;
> + arg = (void *)vm;
> + }
> +
> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
Because this feature need the vhost IOTLB support from the device
emulation. Patches for QEMU hasn't been merged yet, but it has been
there for a while.
Since we don't have the support yet, for sure it won't work. But
I'm wondering why you have to disable it explicitly?
> + if (req_kernel == VHOST_SET_FEATURES)
> + *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
> +
> + for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
> + if (dev->vhostfds[i] < 0)
> + continue;
> +
> + ret = ioctl(dev->vhostfds[i], req_kernel, arg);
> + if (ret < 0)
> + break;
> + }
> +
> + if (vm)
> + free(vm);
> +
> + return ret;
> +}
> +
> +/**
> + * Set up environment to talk with a vhost kernel backend.
> + *
> + * @return
> + * - (-1) if fail to set up;
> + * - (>=0) if successful.
> + */
> +static int
> +vhost_kernel_setup(struct virtio_user_dev *dev)
> +{
> + int vhostfd;
> + uint32_t q;
Why not simply use 'i'?
> +static int
> +vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
> + uint16_t pair_idx,
> + int enable)
> +{
> + unsigned int features;
Better to rename it to "tap_features" or something? When I first saw
such name, I thought it was about vhost features.
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading Jianfeng Tan
@ 2016-12-26 7:53 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 7:53 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Dec 23, 2016 at 07:14:25AM +0000, Jianfeng Tan wrote:
> When used with vhost kernel backend, we can offload at both directions.
> - From vhost kernel to virtio_user, the offload is enabled so that
> DPDK app can trust the flow is checksum-correct; and if DPDK app
> sends it through another port, the checksum needs to be
> recalculated or offloaded. It also applies to TSO.
> - From virtio_user to vhost_kernel, the offload is enabled so that
> kernel can trust the flow is L4-checksum-correct, no need to verify
> it; if kernel will consume it, DPDK app should make sure the
> l3-checksum is correctly set.
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> ---
> drivers/net/virtio/virtio_user/vhost_kernel.c | 61 ++++++++++++++++++++++++++-
> 1 file changed, 59 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
> index 8984c5c..fb3c454 100644
> --- a/drivers/net/virtio/virtio_user/vhost_kernel.c
> +++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
> @@ -91,6 +91,13 @@ struct vhost_memory_kernel {
> #define IFF_ATTACH_QUEUE 0x0200
> #define IFF_DETACH_QUEUE 0x0400
>
> +/* Features for GSO (TUNSETOFFLOAD). */
> +#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
> +#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
> +#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
> +#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
> +#define TUN_F_UFO 0x10 /* I can handle UFO packets */
> +
> /* Constants */
> #define TUN_DEF_SNDBUF (1ull << 20)
> #define PATH_NET_TUN "/dev/net/tun"
> @@ -173,6 +180,28 @@ 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.
> + */
> +static const uint64_t guest_offloads_mask =
The typical way is to define it as macro?
> + (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);
> +
> @@ -271,6 +314,12 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
> int hdr_size;
> int vhostfd;
> int tapfd;
> + unsigned int offload =
> + TUN_F_CSUM |
> + TUN_F_TSO4 |
> + TUN_F_TSO6 |
> + TUN_F_TSO_ECN |
> + TUN_F_UFO;
Same here?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code
2016-12-26 6:58 ` Tan, Jianfeng
@ 2016-12-26 7:57 ` Yuanhan Liu
2016-12-29 9:40 ` Tan, Jianfeng
0 siblings, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 7:57 UTC (permalink / raw)
To: Tan, Jianfeng; +Cc: dev, Yigit, Ferruh, Liang, Cunming
On Mon, Dec 26, 2016 at 06:58:58AM +0000, Tan, Jianfeng wrote:
>
>
> > -----Original Message-----
> > From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> > Sent: Monday, December 26, 2016 2:28 PM
> > To: Tan, Jianfeng
> > Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> > Subject: Re: [PATCH v2 3/7] net/virtio_user: move vhost user specific code
> >
> > On Fri, Dec 23, 2016 at 07:14:22AM +0000, Jianfeng Tan wrote:
> > > To support vhost kernel as the backend of net_virtio_user in coming
> > > patches, we move vhost_user specific structs and macros into
> > > vhost_user.c, and only keep common definitions in vhost.h.
> >
> > Good.
> >
> > > Besides, remove VHOST_USER_MQ feature check, it will be added back
> > > in following multiqueue patch.
> >
> > Why then?
>
> Only vhost user recognizes this feature bit, and vhost kernel does not. My intension is to put those vhost user specific code inside vhost user.
That's okay. But why you have to remove it and then add it back?
--yliu
> And in fact, I forget to add it back. I should add it back in this patch.
>
> Thanks,
> Jianfeng
> >
> > --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
2016-12-26 6:55 ` Tan, Jianfeng
@ 2016-12-26 8:02 ` Yuanhan Liu
2016-12-26 8:26 ` Tan, Jianfeng
0 siblings, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2016-12-26 8:02 UTC (permalink / raw)
To: Tan, Jianfeng; +Cc: dev, Yigit, Ferruh, Liang, Cunming
On Mon, Dec 26, 2016 at 06:55:16AM +0000, Tan, Jianfeng wrote:
>
>
> > -----Original Message-----
> > From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> > Sent: Monday, December 26, 2016 2:27 PM
> > To: Tan, Jianfeng
> > Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> > Subject: Re: [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
> >
> > On Fri, Dec 23, 2016 at 07:14:21AM +0000, Jianfeng Tan wrote:
> > > In driver probe phase, we obtain device information; and then virtio
> > > driver will initialize device and stores info, like negotiated
> > > features, in vhost_user layer; finally, vhost_user gets DRIVER_OK
> > > notification from virtio driver, and then sync with backend device.
> > >
> > > Previously, DRIVER_OK could be sent twice: 1. when ether layer invokes
> > > eth_device_init to initialize device; 2. when user configures it with
> > > different configuration from that of previous.
> >
> > Yes, but that's wrong. Now only 1) will be taken.
>
> >From code in virtio_dev_configure() as below, if hw_ip_checksum or enable_lro is configured, the device will be reset and re-initialized.
> if (rxmode->hw_ip_checksum)
> req_features |= (1ULL << VIRTIO_NET_F_GUEST_CSUM);
> if (rxmode->enable_lro)
> req_features |=
> (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
> (1ULL << VIRTIO_NET_F_GUEST_TSO6);
>
> /* if request features changed, reinit the device */
> if (req_features != hw->req_guest_features) {
> ret = virtio_init_device(dev, req_features);
> if (ret < 0)
> return ret;
> }
Yes, I know. But virtio_init_device() will call vtpci_reinit_complete()
at the end, doesn't it?
> > > Since we can only depend on DRIVER_OK notification to sync with backend
> > > device, we postpone it to virtio_dev_start when everything is settled.
> >
> > I don't quite understand what you were going to fix here; you don't
> > state it in the commit log after all. Normally, when everything is
> > negotiated (when DRIVER_OK is set), we should not set it again, unless
> > a reset has been happened.
>
> I agree that DRIVER_OK should be set only if everything is settled. Under the case of hw_ip_checksum or enable_lro, a reset will be sent and the device will be re-initialized.
>
> So my point is that since the configuration might be changed in dev_configure(), why not just send DRIVER_OK after everything is done.
>
> >
> > If you look at QEMU, qemu will simply ignore it once vhost has already
> > started.
>
> It does not apply here. The configuration is changed, and it cannot be ignored.
Why it doesn't apply here? What makes virtio_user special? If it works
to QEMU and it doesn't to virtio_user, it basically means the virtio_user
is broken ...
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
2016-12-26 8:02 ` Yuanhan Liu
@ 2016-12-26 8:26 ` Tan, Jianfeng
0 siblings, 0 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2016-12-26 8:26 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, Yigit, Ferruh, Liang, Cunming
As discussed offline, this will lead to many DRIVER OK among dev_stop()/dev_start(). I'll remove this patch.
Thanks,
Jianfeng
> -----Original Message-----
> From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> Sent: Monday, December 26, 2016 4:02 PM
> To: Tan, Jianfeng
> Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> Subject: Re: [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification
>
> On Mon, Dec 26, 2016 at 06:55:16AM +0000, Tan, Jianfeng wrote:
> >
> >
> > > -----Original Message-----
> > > From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> > > Sent: Monday, December 26, 2016 2:27 PM
> > > To: Tan, Jianfeng
> > > Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> > > Subject: Re: [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK
> notification
> > >
> > > On Fri, Dec 23, 2016 at 07:14:21AM +0000, Jianfeng Tan wrote:
> > > > In driver probe phase, we obtain device information; and then virtio
> > > > driver will initialize device and stores info, like negotiated
> > > > features, in vhost_user layer; finally, vhost_user gets DRIVER_OK
> > > > notification from virtio driver, and then sync with backend device.
> > > >
> > > > Previously, DRIVER_OK could be sent twice: 1. when ether layer
> invokes
> > > > eth_device_init to initialize device; 2. when user configures it with
> > > > different configuration from that of previous.
> > >
> > > Yes, but that's wrong. Now only 1) will be taken.
> >
> > >From code in virtio_dev_configure() as below, if hw_ip_checksum or
> enable_lro is configured, the device will be reset and re-initialized.
> > if (rxmode->hw_ip_checksum)
> > req_features |= (1ULL << VIRTIO_NET_F_GUEST_CSUM);
> > if (rxmode->enable_lro)
> > req_features |=
> > (1ULL << VIRTIO_NET_F_GUEST_TSO4) |
> > (1ULL << VIRTIO_NET_F_GUEST_TSO6);
> >
> > /* if request features changed, reinit the device */
> > if (req_features != hw->req_guest_features) {
> > ret = virtio_init_device(dev, req_features);
> > if (ret < 0)
> > return ret;
> > }
>
> Yes, I know. But virtio_init_device() will call vtpci_reinit_complete()
> at the end, doesn't it?
>
> > > > Since we can only depend on DRIVER_OK notification to sync with
> backend
> > > > device, we postpone it to virtio_dev_start when everything is settled.
> > >
> > > I don't quite understand what you were going to fix here; you don't
> > > state it in the commit log after all. Normally, when everything is
> > > negotiated (when DRIVER_OK is set), we should not set it again, unless
> > > a reset has been happened.
> >
> > I agree that DRIVER_OK should be set only if everything is settled. Under
> the case of hw_ip_checksum or enable_lro, a reset will be sent and the
> device will be re-initialized.
> >
> > So my point is that since the configuration might be changed in
> dev_configure(), why not just send DRIVER_OK after everything is done.
> >
> > >
> > > If you look at QEMU, qemu will simply ignore it once vhost has already
> > > started.
> >
> > It does not apply here. The configuration is changed, and it cannot be
> ignored.
>
> Why it doesn't apply here? What makes virtio_user special? If it works
> to QEMU and it doesn't to virtio_user, it basically means the virtio_user
> is broken ...
>
> --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code
2016-12-26 7:57 ` Yuanhan Liu
@ 2016-12-29 9:40 ` Tan, Jianfeng
0 siblings, 0 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2016-12-29 9:40 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, Yigit, Ferruh, Liang, Cunming
Hi Yuanhan,
On 12/26/2016 3:57 PM, Yuanhan Liu wrote:
> On Mon, Dec 26, 2016 at 06:58:58AM +0000, Tan, Jianfeng wrote:
>>
>>> -----Original Message-----
>>> From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
>>> Sent: Monday, December 26, 2016 2:28 PM
>>> To: Tan, Jianfeng
>>> Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
>>> Subject: Re: [PATCH v2 3/7] net/virtio_user: move vhost user specific code
>>>
>>> On Fri, Dec 23, 2016 at 07:14:22AM +0000, Jianfeng Tan wrote:
>>>> To support vhost kernel as the backend of net_virtio_user in coming
>>>> patches, we move vhost_user specific structs and macros into
>>>> vhost_user.c, and only keep common definitions in vhost.h.
>>> Good.
>>>
>>>> Besides, remove VHOST_USER_MQ feature check, it will be added back
>>>> in following multiqueue patch.
>>> Why then?
>> Only vhost user recognizes this feature bit, and vhost kernel does not. My intension is to put those vhost user specific code inside vhost user.
> That's okay. But why you have to remove it and then add it back?
After second thought , I agree that it's necessary to add it back.
Currently, our vhost does not implement the semantics of
VHOST_USER_F_PROTOCOL_FEATURES.
Besides, all other comments from you on this patch series will be
addressed on next version. Thank you very much for those suggestions!
Thanks,
Jianfeng
>
> --yliu
>
>> And in fact, I forget to add it back. I should add it back in this patch.
>>
>> Thanks,
>> Jianfeng
>>> --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
` (4 preceding siblings ...)
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
` (7 more replies)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
6 siblings, 8 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
v3:
- Drop the patch to postpone driver ok sending patch, superseded it
with a bug fix to disable all virtqueues and re-init the device.
(you might wonder why not just send reset owner msg. Under my test,
it causes spinlock deadlock problem when killing the program).
- Avoid compiling error on 32-bit system for pointer convert.
- Fix a bug in patch "abstract virtio user backend ops", vhostfd is
not properly assigned.
- Fix a "MQ cannot be used" bug in v2, which is related to strip
some feature bits that vhost kernel does not recognize.
- Update release note.
v2: (Lots of them are from yuanhan's comment)
- Add offloding feature.
- Add multiqueue support.
- Add a new patch to postpone the sending of driver ok notification.
- Put fix patch ahead of the whole patch series.
- Split original 0001 patch into 0003 and 0004 patches.
- Remove the original vhost_internal design, just add those into
struct virtio_user_dev for simplicity.
- Reword "control" to "send_request".
- Reword "host_features" to "device_features".
In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
as the backend). The path to go with a vhost-kernel backend has been
dropped for bad performance comparing to vhost-user and code simplicity.
But after a second thought, virtio_user + vhost-kernel is a good
candidate as an exceptional path, such as KNI, which exchanges packets
with kernel networking stack.
- maintenance: vhost-net (kernel) is upstreamed and extensively used
kernel module. We don't need any out-of-tree module like KNI.
- performance: as with KNI, this solution would use one or more
kthreads to send/receive packets from user space DPDK applications,
which has little impact on user space polling thread (except that
it might enter into kernel space to wake up those kthreads if
necessary).
- features: vhost-net is born to be a networking solution, which has
lots of networking related featuers, like multi queue, tso, multi-seg
mbuf, etc.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Jianfeng Tan (7):
net/virtio_user: fix wrongly set features
net/virtio_user: fix not properly reset device
net/virtio_user: move vhost user specific code
net/virtio_user: abstract virtio user backend ops
net/virtio_user: add vhost kernel support
net/virtio_user: enable offloading
net/virtio_user: enable multiqueue with vhost kernel
doc/guides/rel_notes/release_17_02.rst | 20 +
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_user/vhost.h | 51 +--
drivers/net/virtio/virtio_user/vhost_kernel.c | 487 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost_user.c | 97 +++--
drivers/net/virtio/virtio_user/virtio_user_dev.c | 138 ++++---
drivers/net/virtio/virtio_user/virtio_user_dev.h | 16 +-
drivers/net/virtio/virtio_user_ethdev.c | 19 +-
8 files changed, 705 insertions(+), 124 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 1/7] net/virtio_user: fix wrongly set features
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device Jianfeng Tan
` (6 subsequent siblings)
7 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan, stable
Before the commit 86d59b21468a ("net/virtio: support LRO"), features
in virtio PMD, is decided and properly set at device initialization
and will not be changed. But afterward, features could be changed in
virtio_dev_configure(), and will be re-negotiated if it's changed.
In virtio_user, device features is obtained at driver probe phase
only once, but we did not store it. So the added feature bits in
re-negotiation will fail.
To fix it, we store it down, and will be used to feature negotiation
either at device initialization phase or device configure phase.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
CC: stable@dpdk.org
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 34 +++++++++++-------------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 +++-
drivers/net/virtio/virtio_user_ethdev.c | 4 +--
3 files changed, 22 insertions(+), 21 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index e239e0e..0d7e17b 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -148,12 +148,13 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 1: set features
* Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * and VIRTIO_NET_F_MAC is stripped.
+ * VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
*/
features = dev->features;
if (dev->max_queue_pairs > 1)
features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
+ features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
@@ -228,29 +229,26 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
}
if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
- &dev->features) < 0) {
+ &dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
}
if (dev->mac_specified)
- dev->features |= (1ull << VIRTIO_NET_F_MAC);
+ dev->device_features |= (1ull << VIRTIO_NET_F_MAC);
- if (!cq) {
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
- /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
- dev->features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
- dev->features &= ~(1ull << VIRTIO_NET_F_MQ);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
- } else {
- /* vhost user backend does not need to know ctrl-q, so
- * actually we need add this bit into features. However,
- * DPDK vhost-user does send features with this bit, so we
- * check it instead of OR it for now.
+ if (cq) {
+ /* device does not really need to know anything about CQ,
+ * so if necessary, we just claim to support CQ
*/
- if (!(dev->features & (1ull << VIRTIO_NET_F_CTRL_VQ)))
- PMD_INIT_LOG(INFO, "vhost does not support ctrl-q");
+ dev->device_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
+ } else {
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
+ /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_MQ);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
if (dev->max_queue_pairs > 1) {
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 33690b5..28fc788 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -46,7 +46,10 @@ struct virtio_user_dev {
uint32_t max_queue_pairs;
uint32_t queue_pairs;
uint32_t queue_size;
- uint64_t features;
+ uint64_t features; /* the negotiated features with driver,
+ * and will be sync with device
+ */
+ uint64_t device_features; /* supported features by device */
uint8_t status;
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 8cb983c..4a5a227 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -117,7 +117,7 @@ virtio_user_get_features(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- return dev->features;
+ return dev->device_features;
}
static void
@@ -125,7 +125,7 @@ virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- dev->features = features;
+ dev->features = features & dev->device_features;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 5:46 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
` (5 subsequent siblings)
7 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan, stable
virtio_user is not properly reset when users call vtpci_reset(),
as it ignores VIRTIO_CONFIG_STATUS_RESET status in
virtio_user_set_status().
This might lead to initialization failure as it starts to re-init
the device before sending RESET messege to backend. Besides, previous
callfds and kickfds are not closed.
To fix it, we add support to disable virtqueues when it's set to
DRIVER OK status, and re-init fields in struct virtio_user_dev.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
Fixes: 37a7eb2ae816 ("net/virtio-user: add device emulation layer")
CC: stable@dpdk.org
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 26 ++++++++++++++++--------
drivers/net/virtio/virtio_user_ethdev.c | 15 ++++++++------
2 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 0d7e17b..a38398b 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -182,7 +182,17 @@ virtio_user_start_device(struct virtio_user_dev *dev)
int virtio_user_stop_device(struct virtio_user_dev *dev)
{
- return vhost_user_sock(dev->vhostfd, VHOST_USER_RESET_OWNER, NULL);
+ uint32_t i;
+
+ for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
+ close(dev->callfds[i]);
+ close(dev->kickfds[i]);
+ }
+
+ for (i = 0; i < dev->max_queue_pairs; ++i)
+ vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+
+ return 0;
}
static inline void
@@ -210,6 +220,8 @@ int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
int cq, int queue_size, const char *mac)
{
+ uint32_t i;
+
snprintf(dev->path, PATH_MAX, "%s", path);
dev->max_queue_pairs = queues;
dev->queue_pairs = 1; /* mq disabled by default */
@@ -218,6 +230,11 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
parse_mac(dev, mac);
dev->vhostfd = -1;
+ for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
+ dev->kickfds[i] = -1;
+ dev->callfds[i] = -1;
+ }
+
dev->vhostfd = vhost_user_setup(dev->path);
if (dev->vhostfd < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
@@ -264,13 +281,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
- uint32_t i;
-
- for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
- close(dev->callfds[i]);
- close(dev->kickfds[i]);
- }
-
close(dev->vhostfd);
}
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 4a5a227..93f5b01 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -87,21 +87,24 @@ virtio_user_write_dev_config(struct virtio_hw *hw, size_t offset,
}
static void
-virtio_user_set_status(struct virtio_hw *hw, uint8_t status)
+virtio_user_reset(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
- virtio_user_start_device(dev);
- dev->status = status;
+ if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+ virtio_user_stop_device(dev);
}
static void
-virtio_user_reset(struct virtio_hw *hw)
+virtio_user_set_status(struct virtio_hw *hw, uint8_t status)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- virtio_user_stop_device(dev);
+ if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+ virtio_user_start_device(dev);
+ else if (status == VIRTIO_CONFIG_STATUS_RESET)
+ virtio_user_reset(hw);
+ dev->status = status;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 6:02 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
` (4 subsequent siblings)
7 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
To support vhost kernel as the backend of net_virtio_user in coming
patches, we move vhost_user specific structs and macros into
vhost_user.c, and only keep common definitions in vhost.h.
Besides, remove VHOST_USER_MQ feature check.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 36 ------------------------
drivers/net/virtio/virtio_user/vhost_user.c | 32 +++++++++++++++++++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 9 ------
3 files changed, 32 insertions(+), 45 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 7adb55f..e54ac35 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -42,8 +42,6 @@
#include "../virtio_logs.h"
#include "../virtqueue.h"
-#define VHOST_MEMORY_MAX_NREGIONS 8
-
struct vhost_vring_state {
unsigned int index;
unsigned int num;
@@ -105,40 +103,6 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-struct vhost_memory {
- uint32_t nregions;
- uint32_t padding;
- struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
-};
-
-struct vhost_user_msg {
- enum vhost_user_request request;
-
-#define VHOST_USER_VERSION_MASK 0x3
-#define VHOST_USER_REPLY_MASK (0x1 << 2)
- uint32_t flags;
- uint32_t size; /* the following payload size */
- union {
-#define VHOST_USER_VRING_IDX_MASK 0xff
-#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
- uint64_t u64;
- struct vhost_vring_state state;
- struct vhost_vring_addr addr;
- struct vhost_memory memory;
- } payload;
- int fds[VHOST_MEMORY_MAX_NREGIONS];
-} __attribute((packed));
-
-#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
-#define VHOST_USER_PAYLOAD_SIZE \
- (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
-
-/* The version of the protocol we support */
-#define VHOST_USER_VERSION 0x1
-
-#define VHOST_USER_F_PROTOCOL_FEATURES 30
-#define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
-
int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
int vhost_user_setup(const char *path);
int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 082e821..295ce16 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -42,6 +42,38 @@
#include "vhost.h"
+/* The version of the protocol we support */
+#define VHOST_USER_VERSION 0x1
+
+#define VHOST_MEMORY_MAX_NREGIONS 8
+struct vhost_memory {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
+};
+
+struct vhost_user_msg {
+ enum vhost_user_request request;
+
+#define VHOST_USER_VERSION_MASK 0x3
+#define VHOST_USER_REPLY_MASK (0x1 << 2)
+ uint32_t flags;
+ uint32_t size; /* the following payload size */
+ union {
+#define VHOST_USER_VRING_IDX_MASK 0xff
+#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
+ uint64_t u64;
+ struct vhost_vring_state state;
+ struct vhost_vring_addr addr;
+ struct vhost_memory memory;
+ } payload;
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+} __attribute((packed));
+
+#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
+#define VHOST_USER_PAYLOAD_SIZE \
+ (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
+
static int
vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
{
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index a38398b..8dd563a 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -151,8 +151,6 @@ virtio_user_start_device(struct virtio_user_dev *dev)
* VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
*/
features = dev->features;
- if (dev->max_queue_pairs > 1)
- features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
@@ -268,13 +266,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
- if (dev->max_queue_pairs > 1) {
- if (!(dev->features & VHOST_USER_MQ)) {
- PMD_INIT_LOG(ERR, "MQ not supported by the backend");
- return -1;
- }
- }
-
return 0;
}
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
` (2 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 6:11 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
` (3 subsequent siblings)
7 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Add a struct virtio_user_backend_ops to abstract three kinds of backend
operations:
- setup, create the unix socket connection;
- send_request, sync messages with backend;
- enable_qp, enable some queue pair.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 17 +++++-
drivers/net/virtio/virtio_user/vhost_user.c | 65 +++++++++++---------
drivers/net/virtio/virtio_user/virtio_user_dev.c | 77 +++++++++++++++---------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 ++
4 files changed, 106 insertions(+), 58 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index e54ac35..515e4fc 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -96,6 +96,8 @@ enum vhost_user_request {
VHOST_USER_MAX
};
+const char * const vhost_msg_strings[VHOST_USER_MAX];
+
struct vhost_memory_region {
uint64_t guest_phys_addr;
uint64_t memory_size; /* bytes */
@@ -103,8 +105,17 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
-int vhost_user_setup(const char *path);
-int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+ int (*setup)(struct virtio_user_dev *dev);
+ int (*send_request)(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg);
+ int (*enable_qp)(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable);
+};
+struct virtio_user_backend_ops ops_user;
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 295ce16..a9ca10f 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -41,6 +41,7 @@
#include <errno.h>
#include "vhost.h"
+#include "virtio_user_dev.h"
/* The version of the protocol we support */
#define VHOST_USER_VERSION 0x1
@@ -255,24 +256,26 @@ prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
static struct vhost_user_msg m;
-static const char * const vhost_msg_strings[] = {
- [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
- [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
- [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
- [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
- [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL",
- [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM",
- [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE",
- [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE",
- [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR",
- [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK",
- [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
- [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE",
+const char * const vhost_msg_strings[] = {
+ [VHOST_USER_SET_OWNER] = "VHOST_SET_OWNER",
+ [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",
+ [VHOST_USER_GET_VRING_BASE] = "VHOST_GET_VRING_BASE",
+ [VHOST_USER_SET_VRING_ADDR] = "VHOST_SET_VRING_ADDR",
+ [VHOST_USER_SET_VRING_KICK] = "VHOST_SET_VRING_KICK",
+ [VHOST_USER_SET_MEM_TABLE] = "VHOST_SET_MEM_TABLE",
+ [VHOST_USER_SET_VRING_ENABLE] = "VHOST_SET_VRING_ENABLE",
NULL,
};
-int
-vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
+static int
+vhost_user_sock(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
{
struct vhost_user_msg msg;
struct vhost_vring_file *file = 0;
@@ -280,9 +283,9 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
int fds[VHOST_MEMORY_MAX_NREGIONS];
int fd_num = 0;
int i, len;
+ int vhostfd = dev->vhostfd;
RTE_SET_USED(m);
- RTE_SET_USED(vhost_msg_strings);
PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
@@ -403,15 +406,13 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
/**
* Set up environment to talk with a vhost user backend.
- * @param path
- * - The path to vhost user unix socket file.
*
* @return
- * - (-1) if fail to set up;
- * - (>=0) if successful, and it is the fd to vhostfd.
+ * - (-1) if fail;
+ * - (0) if succeed.
*/
-int
-vhost_user_setup(const char *path)
+static int
+vhost_user_setup(struct virtio_user_dev *dev)
{
int fd;
int flag;
@@ -429,18 +430,21 @@ vhost_user_setup(const char *path)
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
close(fd);
return -1;
}
- return fd;
+ dev->vhostfd = fd;
+ return 0;
}
-int
-vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
+static int
+vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
{
int i;
@@ -450,10 +454,15 @@ vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
.num = enable,
};
- if (vhost_user_sock(vhostfd,
- VHOST_USER_SET_VRING_ENABLE, &state))
+ if (vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state))
return -1;
}
return 0;
}
+
+struct virtio_user_backend_ops ops_user = {
+ .setup = vhost_user_setup,
+ .send_request = vhost_user_sock,
+ .enable_qp = vhost_user_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 8dd563a..32039a1 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -39,6 +39,9 @@
#include <sys/mman.h>
#include <unistd.h>
#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "vhost.h"
#include "virtio_user_dev.h"
@@ -64,7 +67,7 @@ virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = callfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_CALL, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_CALL, &file);
dev->callfds[queue_sel] = callfd;
return 0;
@@ -88,12 +91,12 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.index = queue_sel;
state.num = vring->num;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_NUM, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
state.num = 0; /* no reservation */
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_BASE, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_ADDR, &addr);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_ADDR, &addr);
/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
* lastly because vhost depends on this msg to judge if
@@ -106,7 +109,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = kickfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_KICK, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_KICK, &file);
dev->kickfds[queue_sel] = kickfd;
return 0;
@@ -146,20 +149,19 @@ virtio_user_start_device(struct virtio_user_dev *dev)
if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
goto error;
- /* Step 1: set features
- * Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
- */
+ /* Step 1: set features */
features = dev->features;
+ /* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */
features &= ~(1ull << VIRTIO_NET_F_MAC);
+ /* Strip VIRTIO_NET_F_CTRL_VQ, as devices do not really need to know */
features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
PMD_DRV_LOG(INFO, "set features: %" PRIx64, features);
/* Step 2: share memory regions */
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_MEM_TABLE, NULL);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_MEM_TABLE, NULL);
if (ret < 0)
goto error;
@@ -170,7 +172,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 4: enable queues
* we enable the 1st queue pair by default.
*/
- vhost_user_enable_queue_pair(dev->vhostfd, 0, 1);
+ dev->ops->enable_qp(dev, 0, 1);
return 0;
error:
@@ -188,7 +190,7 @@ int virtio_user_stop_device(struct virtio_user_dev *dev)
}
for (i = 0; i < dev->max_queue_pairs; ++i)
- vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ dev->ops->enable_qp(dev, i, 0);
return 0;
}
@@ -214,36 +216,57 @@ parse_mac(struct virtio_user_dev *dev, const char *mac)
}
}
+static int
+is_vhost_user_by_type(const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == -1)
+ return 0;
+
+ return S_ISSOCK(sb.st_mode);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+ uint32_t i;
+
+ dev->vhostfd = -1;
+ for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
+ dev->kickfds[i] = -1;
+ dev->callfds[i] = -1;
+ }
+
+ if (is_vhost_user_by_type(dev->path)) {
+ dev->ops = &ops_user;
+ return dev->ops->setup(dev);
+ }
+
+ return -1;
+}
+
int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
int cq, int queue_size, const char *mac)
{
- uint32_t i;
-
snprintf(dev->path, PATH_MAX, "%s", path);
dev->max_queue_pairs = queues;
dev->queue_pairs = 1; /* mq disabled by default */
dev->queue_size = queue_size;
dev->mac_specified = 0;
parse_mac(dev, mac);
- dev->vhostfd = -1;
-
- for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
- dev->kickfds[i] = -1;
- dev->callfds[i] = -1;
- }
- dev->vhostfd = vhost_user_setup(dev->path);
- if (dev->vhostfd < 0) {
+ if (virtio_user_dev_setup(dev) < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL) < 0) {
+ if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
+ if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
&dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
@@ -288,9 +311,9 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
}
for (i = 0; i < q_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 1);
+ ret |= dev->ops->enable_qp(dev, i, 1);
for (i = q_pairs; i < dev->max_queue_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ ret |= dev->ops->enable_qp(dev, i, 0);
dev->queue_pairs = q_pairs;
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 28fc788..9f2f82e 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -37,9 +37,13 @@
#include <limits.h>
#include "../virtio_pci.h"
#include "../virtio_ring.h"
+#include "vhost.h"
struct virtio_user_dev {
+ /* for vhost_user backend */
int vhostfd;
+
+ /* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int mac_specified;
@@ -54,6 +58,7 @@ struct virtio_user_dev {
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
struct vring vrings[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
+ struct virtio_user_backend_ops *ops;
};
int virtio_user_start_device(struct virtio_user_dev *dev);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
` (3 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 6:13 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 6/7] net/virtio_user: enable offloading Jianfeng Tan
` (2 subsequent siblings)
7 siblings, 1 reply; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
This patch add support vhost kernel as the backend for virtio_user.
Three main hook functions are added:
- vhost_kernel_setup() to open char device, each vq pair needs one
vhostfd;
- vhost_kernel_ioctl() to communicate control messages with vhost
kernel module;
- vhost_kernel_enable_queue_pair() to open tap device and set it
as the backend of corresonding vhost fd (that is to say, vq pair).
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
doc/guides/rel_notes/release_17_02.rst | 20 ++
drivers/net/virtio/Makefile | 1 +
drivers/net/virtio/virtio_user/vhost.h | 2 +
drivers/net/virtio/virtio_user/vhost_kernel.c | 373 +++++++++++++++++++++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 21 +-
drivers/net/virtio/virtio_user/virtio_user_dev.h | 6 +
6 files changed, 420 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
diff --git a/doc/guides/rel_notes/release_17_02.rst b/doc/guides/rel_notes/release_17_02.rst
index 180af82..7354df5 100644
--- a/doc/guides/rel_notes/release_17_02.rst
+++ b/doc/guides/rel_notes/release_17_02.rst
@@ -52,6 +52,26 @@ New Features
See the :ref:`Generic flow API <Generic_flow_API>` documentation for more
information.
+* **virtio_user with vhost-kernel as another exceptional path.**
+
+ Previously, we upstreamed a virtual device, virtio_user with vhost-user
+ as the backend, as a way for IPC (Inter-Process Communication) and user
+ space container networking.
+
+ Virtio_user with vhost-kernel as the backend is a solution for exceptional
+ path, such as KNI, which exchanges packets with kernel networking stack.
+ This solution is very promising in:
+
+ * maintenance: vhost and vhost-net (kernel) is upstreamed and extensively
+ used kernel module.
+ * features: vhost-net is born to be a networking solution, which has
+ lots of networking related featuers, like multi queue, tso, multi-seg
+ mbuf, etc.
+ * performance: similar to KNI, this solution would use one or more
+ kthreads to send/receive packets from user space DPDK applications,
+ which has little impact on user space polling thread (except that
+ it might enter into kernel space to wake up those kthreads if
+ necessary).
Resolved Issues
---------------
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 97972a6..faeffb2 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -60,6 +60,7 @@ endif
ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
endif
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 515e4fc..5c983bd 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -118,4 +118,6 @@ struct virtio_user_backend_ops {
};
struct virtio_user_backend_ops ops_user;
+struct virtio_user_backend_ops ops_kernel;
+
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
new file mode 100644
index 0000000..1e7cdef
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -0,0 +1,373 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <string.h>
+#include <errno.h>
+
+#include <rte_memory.h>
+#include <rte_eal_memconfig.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+
+struct vhost_memory_kernel {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* vhost kernel ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#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)
+
+/* TUN ioctls */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
+#define TUNSETVNETLE _IOW('T', 220, int)
+#define TUNSETVNETBE _IOW('T', 222, int)
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+
+/* Constants */
+#define TUN_DEF_SNDBUF (1ull << 20)
+#define PATH_NET_TUN "/dev/net/tun"
+#define VHOST_KERNEL_MAX_REGIONS 64
+
+static uint64_t vhost_req_user_to_kernel[] = {
+ [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
+ [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,
+ [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
+ [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
+ [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
+ [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
+};
+
+/* By default, vhost kernel module allows 64 regions, but DPDK allows
+ * 256 segments. As a relief, below function merges those virtually
+ * adjacent memsegs into one region.
+ */
+static struct vhost_memory_kernel *
+prepare_vhost_memory_kernel(void)
+{
+ uint32_t i, j, k = 0;
+ struct rte_memseg *seg;
+ struct vhost_memory_region *mr;
+ struct vhost_memory_kernel *vm;
+
+ vm = malloc(sizeof(struct vhost_memory_kernel) +
+ VHOST_KERNEL_MAX_REGIONS *
+ sizeof(struct vhost_memory_region));
+
+ for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
+ seg = &rte_eal_get_configuration()->mem_config->memseg[i];
+ if (!seg->addr)
+ break;
+
+ int new_region = 1;
+
+ for (j = 0; j < k; ++j) {
+ mr = &vm->regions[j];
+
+ if (mr->userspace_addr + mr->memory_size ==
+ (uint64_t)(uintptr_t)seg->addr) {
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+
+ if ((uint64_t)(uintptr_t)seg->addr + seg->len ==
+ mr->userspace_addr) {
+ mr->guest_phys_addr =
+ (uint64_t)(uintptr_t)seg->addr;
+ mr->userspace_addr =
+ (uint64_t)(uintptr_t)seg->addr;
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+ }
+
+ if (new_region == 0)
+ continue;
+
+ mr = &vm->regions[k++];
+ /* use vaddr here! */
+ mr->guest_phys_addr = (uint64_t)(uintptr_t)seg->addr;
+ mr->userspace_addr = (uint64_t)(uintptr_t)seg->addr;
+ mr->memory_size = seg->len;
+ mr->mmap_offset = 0;
+
+ if (k >= VHOST_KERNEL_MAX_REGIONS) {
+ free(vm);
+ return NULL;
+ }
+ }
+
+ vm->nregions = k;
+ vm->padding = 0;
+ return vm;
+}
+
+static int
+vhost_kernel_ioctl(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
+{
+ int i, ret = -1;
+ uint64_t req_kernel;
+ struct vhost_memory_kernel *vm = NULL;
+
+ PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
+
+ req_kernel = vhost_req_user_to_kernel[req];
+
+ if (req_kernel == VHOST_SET_MEM_TABLE) {
+ vm = prepare_vhost_memory_kernel();
+ if (!vm)
+ return -1;
+ arg = (void *)vm;
+ }
+
+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
+ if (req_kernel == VHOST_SET_FEATURES)
+ *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
+
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
+
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+
+ if (vm)
+ free(vm);
+
+ if (ret < 0)
+ PMD_DRV_LOG(ERR, "%s failed: %s",
+ vhost_msg_strings[req], strerror(errno));
+
+ return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost kernel backend.
+ *
+ * @return
+ * - (-1) if fail to set up;
+ * - (>=0) if successful.
+ */
+static int
+vhost_kernel_setup(struct virtio_user_dev *dev)
+{
+ int vhostfd;
+ uint32_t i;
+
+ for (i = 0; i < dev->max_queue_pairs; ++i) {
+ vhostfd = open(dev->path, O_RDWR);
+ if (vhostfd < 0) {
+ PMD_DRV_LOG(ERR, "fail to open %s, %s",
+ dev->path, strerror(errno));
+ return -1;
+ }
+
+ dev->vhostfds[i] = vhostfd;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_set_backend(int vhostfd, int tapfd)
+{
+ struct vhost_vring_file f;
+
+ f.fd = tapfd;
+ f.index = 0;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ f.index = 1;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
+{
+ unsigned int tap_features;
+ int sndbuf = TUN_DEF_SNDBUF;
+ struct ifreq ifr;
+ int hdr_size;
+ int vhostfd;
+ int tapfd;
+
+ vhostfd = dev->vhostfds[pair_idx];
+
+ if (!enable) {
+ if (dev->tapfds[pair_idx]) {
+ close(dev->tapfds[pair_idx]);
+ dev->tapfds[pair_idx] = -1;
+ }
+ return vhost_kernel_set_backend(vhostfd, -1);
+ } else if (dev->tapfds[pair_idx] >= 0) {
+ return 0;
+ }
+
+ if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
+ (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
+ hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ else
+ hdr_size = sizeof(struct virtio_net_hdr);
+
+ /* TODO:
+ * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
+ * 2. get number of memory regions from vhost module parameter
+ * max_mem_regions, supported in newer version linux kernel
+ */
+ 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;
+ }
+
+ /* Construct ifr */
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ if (ioctl(tapfd, TUNGETFEATURES, &tap_features) == -1) {
+ PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
+ goto error;
+ }
+ if (tap_features & IFF_ONE_QUEUE)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+
+ /* Let tap instead of vhost-net handle vnet header, as the latter does
+ * not support offloading. And in this case, we should not set feature
+ * bit VHOST_NET_F_VIRTIO_NET_HDR.
+ */
+ if (tap_features & IFF_VNET_HDR) {
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
+ goto error;
+ }
+
+ if (dev->ifname)
+ strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
+ else
+ strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
+ if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
+ PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ fcntl(tapfd, F_SETFL, O_NONBLOCK);
+
+ if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (vhost_kernel_set_backend(vhostfd, tapfd) < 0)
+ goto error;
+
+ dev->tapfds[pair_idx] = tapfd;
+ if (!dev->ifname)
+ dev->ifname = strdup(ifr.ifr_name);
+
+ return 0;
+error:
+ return -1;
+}
+
+struct virtio_user_backend_ops ops_kernel = {
+ .setup = vhost_kernel_setup,
+ .send_request = vhost_kernel_ioctl,
+ .enable_qp = vhost_kernel_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 32039a1..c40b77e 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -192,6 +192,9 @@ int virtio_user_stop_device(struct virtio_user_dev *dev)
for (i = 0; i < dev->max_queue_pairs; ++i)
dev->ops->enable_qp(dev, i, 0);
+ free(dev->ifname);
+ dev->ifname = NULL;
+
return 0;
}
@@ -230,7 +233,7 @@ is_vhost_user_by_type(const char *path)
static int
virtio_user_dev_setup(struct virtio_user_dev *dev)
{
- uint32_t i;
+ uint32_t i, q;
dev->vhostfd = -1;
for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
@@ -238,12 +241,18 @@ virtio_user_dev_setup(struct virtio_user_dev *dev)
dev->callfds[i] = -1;
}
+ for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
+ dev->vhostfds[q] = -1;
+ dev->tapfds[q] = -1;
+ }
+
if (is_vhost_user_by_type(dev->path)) {
dev->ops = &ops_user;
- return dev->ops->setup(dev);
+ } else {
+ dev->ops = &ops_kernel;
}
- return -1;
+ return dev->ops->setup(dev);
}
int
@@ -295,7 +304,13 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
+ uint32_t i;
+
+ virtio_user_stop_device(dev);
+
close(dev->vhostfd);
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i)
+ close(dev->vhostfds[i]);
}
static uint8_t
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 9f2f82e..148b2e6 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -43,6 +43,12 @@ struct virtio_user_dev {
/* for vhost_user backend */
int vhostfd;
+ /* for vhost_kernel backend */
+ char *ifname;
+#define VHOST_KERNEL_MAX_QUEUES 8
+ int vhostfds[VHOST_KERNEL_MAX_QUEUES];
+ int tapfds[VHOST_KERNEL_MAX_QUEUES];
+
/* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 6/7] net/virtio_user: enable offloading
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
` (4 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
2017-01-09 14:06 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Bruce Richardson
7 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
When used with vhost kernel backend, we can offload at both directions.
- From vhost kernel to virtio_user, the offload is enabled so that
DPDK app can trust the flow is checksum-correct; and if DPDK app
sends it through another port, the checksum needs to be
recalculated or offloaded. It also applies to TSO.
- From virtio_user to vhost_kernel, the offload is enabled so that
kernel can trust the flow is L4-checksum-correct, no need to verify
it; if kernel will consume it, DPDK app should make sure the
l3-checksum is correctly set.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 61 ++++++++++++++++++++++++++-
1 file changed, 59 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index 1e7cdef..bdb4af2 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -91,6 +91,13 @@ struct vhost_memory_kernel {
#define IFF_ATTACH_QUEUE 0x0200
#define IFF_DETACH_QUEUE 0x0400
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
/* Constants */
#define TUN_DEF_SNDBUF (1ull << 20)
#define PATH_NET_TUN "/dev/net/tun"
@@ -176,6 +183,28 @@ 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 int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
@@ -196,10 +225,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
arg = (void *)vm;
}
- /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
- if (req_kernel == VHOST_SET_FEATURES)
+ if (req_kernel == VHOST_SET_FEATURES) {
+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
*(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;
+ }
+
for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
if (dev->vhostfds[i] < 0)
continue;
@@ -209,6 +243,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
break;
}
+ if (!ret && req_kernel == VHOST_GET_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.
+ */
+ *((uint64_t *)arg) |= VHOST_KERNEL_GUEST_OFFLOADS_MASK;
+ *((uint64_t *)arg) |= VHOST_KERNEL_HOST_OFFLOADS_MASK;
+ }
+
if (vm)
free(vm);
@@ -280,6 +323,12 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
int hdr_size;
int vhostfd;
int tapfd;
+ unsigned int offload =
+ TUN_F_CSUM |
+ TUN_F_TSO4 |
+ TUN_F_TSO6 |
+ TUN_F_TSO_ECN |
+ TUN_F_UFO;
vhostfd = dev->vhostfds[pair_idx];
@@ -354,6 +403,14 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
goto error;
}
+ /* TODO: before set the offload capabilities, we'd better (1) check
+ * negotiated features to see if necessary to offload; (2) query tap
+ * to see if it supports the offload capabilities.
+ */
+ if (ioctl(tapfd, TUNSETOFFLOAD, offload) != 0)
+ PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s",
+ strerror(errno));
+
if (vhost_kernel_set_backend(vhostfd, tapfd) < 0)
goto error;
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v3 7/7] net/virtio_user: enable multiqueue with vhost kernel
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
` (5 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 6/7] net/virtio_user: enable offloading Jianfeng Tan
@ 2017-01-04 3:59 ` Jianfeng Tan
2017-01-09 14:06 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Bruce Richardson
7 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-04 3:59 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
With vhost kernel, to enable multiqueue, we need backend device
in kernel support multiqueue feature. Specifically, with tap
as the backend, as linux/Documentation/networking/tuntap.txt shows,
we check if tap supports IFF_MULTI_QUEUE feature.
And for vhost kernel, each queue pair has a vhost fd, and with a tap
fd binding this vhost fd. All tap fds are set with the same tap
interface name.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 69 +++++++++++++++++++++---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 1 +
2 files changed, 64 insertions(+), 6 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index bdb4af2..023bdf8 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -206,6 +206,29 @@ prepare_vhost_memory_kernel(void)
(1ULL << VIRTIO_NET_F_CSUM))
static int
+tap_supporte_mq(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 & IFF_MULTI_QUEUE;
+}
+
+static int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
void *arg)
@@ -213,6 +236,8 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
int i, ret = -1;
uint64_t req_kernel;
struct vhost_memory_kernel *vm = NULL;
+ int vhostfd;
+ unsigned int queue_sel;
PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
@@ -232,15 +257,37 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
/* 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);
}
- for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
- if (dev->vhostfds[i] < 0)
- continue;
+ switch (req_kernel) {
+ case VHOST_SET_VRING_NUM:
+ case VHOST_SET_VRING_ADDR:
+ case VHOST_SET_VRING_BASE:
+ case VHOST_GET_VRING_BASE:
+ case VHOST_SET_VRING_KICK:
+ case VHOST_SET_VRING_CALL:
+ queue_sel = *(unsigned int *)arg;
+ vhostfd = dev->vhostfds[queue_sel / 2];
+ *(unsigned int *)arg = queue_sel % 2;
+ PMD_DRV_LOG(DEBUG, "vhostfd=%d, index=%u",
+ vhostfd, *(unsigned int *)arg);
+ break;
+ default:
+ vhostfd = -1;
+ }
+ if (vhostfd == -1) {
+ for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
- ret = ioctl(dev->vhostfds[i], req_kernel, arg);
- if (ret < 0)
- break;
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ ret = ioctl(vhostfd, req_kernel, arg);
}
if (!ret && req_kernel == VHOST_GET_FEATURES) {
@@ -250,6 +297,12 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
*/
*((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 (tap_supporte_mq())
+ *(uint64_t *)arg |= (1ull << VIRTIO_NET_F_MQ);
}
if (vm)
@@ -329,6 +382,7 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
TUN_F_TSO6 |
TUN_F_TSO_ECN |
TUN_F_UFO;
+ int req_mq = (dev->max_queue_pairs > 1);
vhostfd = dev->vhostfds[pair_idx];
@@ -382,6 +436,9 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
goto error;
}
+ if (req_mq)
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+
if (dev->ifname)
strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
else
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index c40b77e..2d9d989 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -93,6 +93,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.num = vring->num;
dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
+ state.index = queue_sel;
state.num = 0; /* no reservation */
dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device Jianfeng Tan
@ 2017-01-04 5:46 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 5:46 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang, stable
On Wed, Jan 04, 2017 at 03:59:21AM +0000, Jianfeng Tan wrote:
> virtio_user is not properly reset when users call vtpci_reset(),
> as it ignores VIRTIO_CONFIG_STATUS_RESET status in
> virtio_user_set_status().
>
> This might lead to initialization failure as it starts to re-init
> the device before sending RESET messege to backend. Besides, previous
> callfds and kickfds are not closed.
>
> To fix it, we add support to disable virtqueues when it's set to
> DRIVER OK status, and re-init fields in struct virtio_user_dev.
>
> Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
> Fixes: 37a7eb2ae816 ("net/virtio-user: add device emulation layer")
>
> CC: stable@dpdk.org
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Note that, typically, there should be no empty line between 'Cc' and SoB.
> ---
> drivers/net/virtio/virtio_user/virtio_user_dev.c | 26 ++++++++++++++++--------
> drivers/net/virtio/virtio_user_ethdev.c | 15 ++++++++------
> 2 files changed, 27 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> index 0d7e17b..a38398b 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> @@ -182,7 +182,17 @@ virtio_user_start_device(struct virtio_user_dev *dev)
>
> int virtio_user_stop_device(struct virtio_user_dev *dev)
The name doesn't seem to be well named: "dev_stop" comes to my firstly
when I saw that :/
Rename it to "xxx_reset_device"?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
@ 2017-01-04 6:02 ` Yuanhan Liu
2017-01-04 6:46 ` Tan, Jianfeng
0 siblings, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 6:02 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Wed, Jan 04, 2017 at 03:59:22AM +0000, Jianfeng Tan wrote:
> To support vhost kernel as the backend of net_virtio_user in coming
> patches, we move vhost_user specific structs and macros into
> vhost_user.c, and only keep common definitions in vhost.h.
>
> Besides, remove VHOST_USER_MQ feature check.
Again, I have to ask, why? You don't only remove the check, also, you
removed this feature setting, which seems to break the MQ support?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
@ 2017-01-04 6:11 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 6:11 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Wed, Jan 04, 2017 at 03:59:23AM +0000, Jianfeng Tan wrote:
> +struct virtio_user_backend_ops ops_user;
Better to qualify it with "extern const" ...
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2017-01-04 6:13 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 6:13 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Wed, Jan 04, 2017 at 03:59:24AM +0000, Jianfeng Tan wrote:
> +static int
> +vhost_kernel_ioctl(struct virtio_user_dev *dev,
> + enum vhost_user_request req,
> + void *arg)
> +{
> + int i, ret = -1;
> + uint64_t req_kernel;
> + struct vhost_memory_kernel *vm = NULL;
> +
> + PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
> +
> + req_kernel = vhost_req_user_to_kernel[req];
> +
> + if (req_kernel == VHOST_SET_MEM_TABLE) {
> + vm = prepare_vhost_memory_kernel();
> + if (!vm)
> + return -1;
> + arg = (void *)vm;
> + }
> +
> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
> + if (req_kernel == VHOST_SET_FEATURES)
> + *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
You missed my comments in last version?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code
2017-01-04 6:02 ` Yuanhan Liu
@ 2017-01-04 6:46 ` Tan, Jianfeng
2017-01-04 7:08 ` Yuanhan Liu
0 siblings, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-04 6:46 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, Yigit, Ferruh, Liang, Cunming
Hi Yuanhan,
> -----Original Message-----
> From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> Sent: Wednesday, January 4, 2017 2:03 PM
> To: Tan, Jianfeng
> Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> Subject: Re: [PATCH v3 3/7] net/virtio_user: move vhost user specific code
>
> On Wed, Jan 04, 2017 at 03:59:22AM +0000, Jianfeng Tan wrote:
> > To support vhost kernel as the backend of net_virtio_user in coming
> > patches, we move vhost_user specific structs and macros into
> > vhost_user.c, and only keep common definitions in vhost.h.
> >
> > Besides, remove VHOST_USER_MQ feature check.
>
> Again, I have to ask, why? You don't only remove the check, also, you
> removed this feature setting, which seems to break the MQ support?
I have answered it here:
http://dpdk.org/ml/archives/dev/2016-December/053520.html
To be more clear, VHOST_USER_MQ is a not-well-defined macro: #define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
which is a feature bit in vhost user protocol.
According to QEMU/ docs/specs/vhost-user.txt, "If VHOST_USER_F_PROTOCOL_FEATURES has not been negotiated, the ring is initialized in an enabled state. "
But our DPDK vhost library does not take care of this feature bit. Just make this as default: the ring is initialized in an disabled state. And our virtio_user with vhost-user does send VHOST_USER_SET_VRING_ENABLE to enable each queue pair.
So I think it's not necessary to add it back.
How do you think?
Thanks,
Jianfeng
>
> --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code
2017-01-04 6:46 ` Tan, Jianfeng
@ 2017-01-04 7:08 ` Yuanhan Liu
0 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 7:08 UTC (permalink / raw)
To: Tan, Jianfeng; +Cc: dev, Yigit, Ferruh, Liang, Cunming
On Wed, Jan 04, 2017 at 06:46:34AM +0000, Tan, Jianfeng wrote:
> Hi Yuanhan,
>
> > -----Original Message-----
> > From: Yuanhan Liu [mailto:yuanhan.liu@linux.intel.com]
> > Sent: Wednesday, January 4, 2017 2:03 PM
> > To: Tan, Jianfeng
> > Cc: dev@dpdk.org; Yigit, Ferruh; Liang, Cunming
> > Subject: Re: [PATCH v3 3/7] net/virtio_user: move vhost user specific code
> >
> > On Wed, Jan 04, 2017 at 03:59:22AM +0000, Jianfeng Tan wrote:
> > > To support vhost kernel as the backend of net_virtio_user in coming
> > > patches, we move vhost_user specific structs and macros into
> > > vhost_user.c, and only keep common definitions in vhost.h.
> > >
> > > Besides, remove VHOST_USER_MQ feature check.
> >
> > Again, I have to ask, why? You don't only remove the check, also, you
> > removed this feature setting, which seems to break the MQ support?
>
> I have answered it here:
> http://dpdk.org/ml/archives/dev/2016-December/053520.html
I thought we have made some agreements :/
>
> To be more clear, VHOST_USER_MQ is a not-well-defined macro: #define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES),
> which is a feature bit in vhost user protocol.
Yes, it's again named wrongly.
> According to QEMU/ docs/specs/vhost-user.txt, "If VHOST_USER_F_PROTOCOL_FEATURES has not been negotiated, the ring is initialized in an enabled state. "
>
> But our DPDK vhost library does not take care of this feature bit.
> Just make this as default: the ring is initialized in an disabled state. And our virtio_user with vhost-user does send VHOST_USER_SET_VRING_ENABLE to enable each queue pair.
VHOST_USER_F_PROTOCOL_FEATURES is a fundamental feature for quite many
vhost-user extended features, including the MQ. If it's not set, the MQ
should not work.
It may still work in your case, becase you made an assumtion that the
vhost backend supports the MQ feature (which is true in nowadays, as
the feature has been there for a quite while). However, that's not an
assumtion you can take while adding the vhost-user MQ support at that
time. And such feature bit (including the PROTOCOL_F_MQ) makes sure
that we will not try to enable MQ with and older vhost backend that
doesn't have the support.
Put simply, this feature is needed, and as the feature name states,
it's needed only for vhost-user.
--yliu
>
> So I think it's not necessary to add it back.
>
> How do you think?
>
> Thanks,
> Jianfeng
>
> >
> > --yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2016-12-26 7:44 ` Yuanhan Liu
@ 2017-01-04 7:22 ` Tan, Jianfeng
2017-01-04 7:46 ` Yuanhan Liu
2017-01-09 4:51 ` Jason Wang
0 siblings, 2 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-04 7:22 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: dev, ferruh.yigit, cunming.liang
Sorry, I forget to reply this comment.
On 12/26/2016 3:44 PM, Yuanhan Liu wrote:
> [...]
>> +
>> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
> Because this feature need the vhost IOTLB support from the device
> emulation. Patches for QEMU hasn't been merged yet, but it has been
> there for a while.
Yes. And it's in need of help from QEMU.
>
> Since we don't have the support yet, for sure it won't work. But
> I'm wondering why you have to disable it explicitly?
Here we do not have QEMU. Frontend driver talks with vhost-net through
virtio_user_dev. And both ends claim to support VIRTIO_F_IOMMU_PLATFORM.
So this feature bit will be negotiated if we don't explicitly disable
it. In my previous test, it fails in vhost_init_device_iotlb() of vhost
kernel module. My guess is that, for this feature, without the help of
QEMU, vhost kernel module cannot work independently.
Thanks,
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-04 7:22 ` Tan, Jianfeng
@ 2017-01-04 7:46 ` Yuanhan Liu
2017-01-09 4:51 ` Jason Wang
1 sibling, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-04 7:46 UTC (permalink / raw)
To: Tan, Jianfeng; +Cc: dev, ferruh.yigit, cunming.liang
On Wed, Jan 04, 2017 at 03:22:08PM +0800, Tan, Jianfeng wrote:
>
> Sorry, I forget to reply this comment.
>
> On 12/26/2016 3:44 PM, Yuanhan Liu wrote:
> >[...]
> >>+
> >>+ /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
> >Because this feature need the vhost IOTLB support from the device
> >emulation. Patches for QEMU hasn't been merged yet, but it has been
> >there for a while.
>
> Yes. And it's in need of help from QEMU.
>
> >
> >Since we don't have the support yet, for sure it won't work. But
> >I'm wondering why you have to disable it explicitly?
>
> Here we do not have QEMU. Frontend driver talks with vhost-net through
> virtio_user_dev. And both ends claim to support VIRTIO_F_IOMMU_PLATFORM. So
> this feature bit will be negotiated if we don't explicitly disable it. In my
> previous test, it fails in vhost_init_device_iotlb() of vhost kernel module.
> My guess is that, for this feature, without the help of QEMU, vhost kernel
> module cannot work independently.
The negotiation is a work between the driver, the device and the vhost
backend. If the device doesn't claim to support it, how could it be
nenogitated.
Think that way, does old QEMU (that doesn't support this feature) disable this
feature explicitly? No. Then why do we have to do that?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
2016-12-26 7:44 ` Yuanhan Liu
@ 2017-01-09 4:39 ` Jason Wang
2017-01-10 6:11 ` Tan, Jianfeng
2017-01-11 2:30 ` Tan, Jianfeng
1 sibling, 2 replies; 72+ messages in thread
From: Jason Wang @ 2017-01-09 4:39 UTC (permalink / raw)
To: Jianfeng Tan, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 2016年12月23日 15:14, Jianfeng Tan wrote:
> This patch add support vhost kernel as the backend for virtio_user.
> Three main hook functions are added:
> - vhost_kernel_setup() to open char device, each vq pair needs one
> vhostfd;
> - vhost_kernel_ioctl() to communicate control messages with vhost
> kernel module;
> - vhost_kernel_enable_queue_pair() to open tap device and set it
> as the backend of corresonding vhost fd (that is to say, vq pair).
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> ---
> drivers/net/virtio/Makefile | 1 +
> drivers/net/virtio/virtio_user/vhost.h | 2 +
> drivers/net/virtio/virtio_user/vhost_kernel.c | 364 +++++++++++++++++++++++
> drivers/net/virtio/virtio_user/virtio_user_dev.c | 21 +-
> drivers/net/virtio/virtio_user/virtio_user_dev.h | 4 +
> 5 files changed, 388 insertions(+), 4 deletions(-)
> create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
>
> diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
> index 97972a6..faeffb2 100644
> --- a/drivers/net/virtio/Makefile
> +++ b/drivers/net/virtio/Makefile
> @@ -60,6 +60,7 @@ endif
>
> ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
> SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
> +SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel.c
> SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
> SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
> endif
> diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
> index bd67133..ffab13a 100644
> --- a/drivers/net/virtio/virtio_user/vhost.h
> +++ b/drivers/net/virtio/virtio_user/vhost.h
> @@ -120,4 +120,6 @@ struct virtio_user_backend_ops {
> };
>
> struct virtio_user_backend_ops ops_user;
> +struct virtio_user_backend_ops ops_kernel;
> +
> #endif
> diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
> new file mode 100644
> index 0000000..8984c5c
> --- /dev/null
> +++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
> @@ -0,0 +1,364 @@
> +/*-
> + * BSD LICENSE
> + *
> + * Copyright(c) 2010-2016 Intel Corporation. All rights reserved.
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * * Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in
> + * the documentation and/or other materials provided with the
> + * distribution.
> + * * Neither the name of Intel Corporation nor the names of its
> + * contributors may be used to endorse or promote products derived
> + * from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <unistd.h>
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <fcntl.h>
> +#include <sys/ioctl.h>
> +#include <net/if.h>
> +#include <string.h>
> +#include <errno.h>
> +
> +#include <rte_memory.h>
> +#include <rte_eal_memconfig.h>
> +
> +#include "vhost.h"
> +#include "virtio_user_dev.h"
> +
> +struct vhost_memory_kernel {
> + uint32_t nregions;
> + uint32_t padding;
> + struct vhost_memory_region regions[0];
> +};
> +
> +/* vhost kernel ioctls */
> +#define VHOST_VIRTIO 0xAF
> +#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
> +#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
> +#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
> +#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
> +#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
> +#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
> +#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
> +#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
> +#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
> +#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
> +#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
> +#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
> +#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
> +#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)
> +
> +/* TUN ioctls */
> +#define TUNSETIFF _IOW('T', 202, int)
> +#define TUNGETFEATURES _IOR('T', 207, unsigned int)
> +#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
> +#define TUNGETIFF _IOR('T', 210, unsigned int)
> +#define TUNSETSNDBUF _IOW('T', 212, int)
> +#define TUNGETVNETHDRSZ _IOR('T', 215, int)
> +#define TUNSETVNETHDRSZ _IOW('T', 216, int)
> +#define TUNSETQUEUE _IOW('T', 217, int)
> +#define TUNSETVNETLE _IOW('T', 220, int)
> +#define TUNSETVNETBE _IOW('T', 222, int)
> +
> +/* TUNSETIFF ifr flags */
> +#define IFF_TAP 0x0002
> +#define IFF_NO_PI 0x1000
> +#define IFF_ONE_QUEUE 0x2000
> +#define IFF_VNET_HDR 0x4000
> +#define IFF_MULTI_QUEUE 0x0100
> +#define IFF_ATTACH_QUEUE 0x0200
> +#define IFF_DETACH_QUEUE 0x0400
Do we really want to duplicate those things which has been exposed by
uapi here?
> +
> +/* Constants */
> +#define TUN_DEF_SNDBUF (1ull << 20)
> +#define PATH_NET_TUN "/dev/net/tun"
> +#define VHOST_KERNEL_MAX_REGIONS 64
Unfortunate not a constant any more since c9ce42f72fd0 vhost: add
max_mem_regions module parameter.
> +
> +static uint64_t vhost_req_user_to_kernel[] = {
> + [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
> + [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,
> + [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
> + [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
> + [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
> + [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
> +};
> +
> +/* By default, vhost kernel module allows 64 regions, but DPDK allows
> + * 256 segments. As a relief, below function merges those virtually
> + * adjacent memsegs into one region.
> + */
> +static struct vhost_memory_kernel *
> +prepare_vhost_memory_kernel(void)
> +{
> + uint32_t i, j, k = 0;
> + struct rte_memseg *seg;
> + struct vhost_memory_region *mr;
> + struct vhost_memory_kernel *vm;
> +
> + vm = malloc(sizeof(struct vhost_memory_kernel) +
> + VHOST_KERNEL_MAX_REGIONS *
> + sizeof(struct vhost_memory_region));
> +
> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
> + if (!seg->addr)
> + break;
If we're sure the number of regions is less than 64(or the module
parameter read from /sys), can we avoid the iteration here?
> +
> + int new_region = 1;
> +
> + for (j = 0; j < k; ++j) {
> + mr = &vm->regions[j];
> +
> + if (mr->userspace_addr + mr->memory_size ==
> + (uint64_t)seg->addr) {
> + mr->memory_size += seg->len;
> + new_region = 0;
> + break;
> + }
> +
> + if ((uint64_t)seg->addr + seg->len ==
> + mr->userspace_addr) {
> + mr->guest_phys_addr = (uint64_t)seg->addr;
> + mr->userspace_addr = (uint64_t)seg->addr;
> + mr->memory_size += seg->len;
> + new_region = 0;
> + break;
> + }
> + }
> +
> + if (new_region == 0)
> + continue;
> +
> + mr = &vm->regions[k++];
> + mr->guest_phys_addr = (uint64_t)seg->addr; /* use vaddr here! */
> + mr->userspace_addr = (uint64_t)seg->addr;
> + mr->memory_size = seg->len;
> + mr->mmap_offset = 0;
> +
> + if (k >= VHOST_KERNEL_MAX_REGIONS) {
> + free(vm);
> + return NULL;
> + }
> + }
> +
> + vm->nregions = k;
> + vm->padding = 0;
> + return vm;
> +}
> +
> +static int
> +vhost_kernel_ioctl(struct virtio_user_dev *dev,
> + enum vhost_user_request req,
> + void *arg)
> +{
> + int i, ret = -1;
> + uint64_t req_kernel;
> + struct vhost_memory_kernel *vm = NULL;
> +
> + req_kernel = vhost_req_user_to_kernel[req];
> +
> + if (req_kernel == VHOST_SET_MEM_TABLE) {
> + vm = prepare_vhost_memory_kernel();
> + if (!vm)
> + return -1;
> + arg = (void *)vm;
> + }
> +
> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
I think the reason is when VIRTIO_F_IOMMU_PLATFORM is negotiated, all
address should be iova instead of gpa.
> + if (req_kernel == VHOST_SET_FEATURES)
> + *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
> +
> + for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
> + if (dev->vhostfds[i] < 0)
> + continue;
> +
> + ret = ioctl(dev->vhostfds[i], req_kernel, arg);
> + if (ret < 0)
> + break;
> + }
> +
> + if (vm)
> + free(vm);
> +
> + return ret;
> +}
> +
> +/**
> + * Set up environment to talk with a vhost kernel backend.
> + *
> + * @return
> + * - (-1) if fail to set up;
> + * - (>=0) if successful.
> + */
> +static int
> +vhost_kernel_setup(struct virtio_user_dev *dev)
> +{
> + int vhostfd;
> + uint32_t q;
> +
> + for (q = 0; q < dev->max_queue_pairs; ++q) {
> + vhostfd = open(dev->path, O_RDWR);
> + if (vhostfd < 0) {
> + PMD_DRV_LOG(ERR, "fail to open %s, %s",
> + dev->path, strerror(errno));
> + return -1;
> + }
> +
> + dev->vhostfds[q] = vhostfd;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +vhost_kernel_set_backend(int vhostfd, int tapfd)
> +{
> + struct vhost_vring_file f;
> +
> + f.fd = tapfd;
> + f.index = 0;
> + if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
> + PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
> + strerror(errno));
> + return -1;
> + }
> +
> + f.index = 1;
> + if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
> + PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
> + strerror(errno));
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
> + uint16_t pair_idx,
> + int enable)
> +{
> + unsigned int features;
> + int sndbuf = TUN_DEF_SNDBUF;
> + struct ifreq ifr;
> + int hdr_size;
> + int vhostfd;
> + int tapfd;
> +
> + vhostfd = dev->vhostfds[pair_idx];
> +
> + if (!enable) {
> + if (dev->tapfds[pair_idx]) {
> + close(dev->tapfds[pair_idx]);
> + dev->tapfds[pair_idx] = -1;
> + }
> + return vhost_kernel_set_backend(vhostfd, -1);
If this is used to for thing like ethtool -L in guest, we should use
TUNSETQUEUE here.
> + } else if (dev->tapfds[pair_idx] >= 0) {
> + return 0;
> + }
> +
> + if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
> + (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
> + hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
> + else
> + hdr_size = sizeof(struct virtio_net_hdr);
> +
> + /* TODO:
> + * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
> + * 2. get number of memory regions from vhost module parameter
> + * max_mem_regions, supported in newer version linux kernel
> + */
> + 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;
> + }
> +
> + /* Construct ifr */
> + memset(&ifr, 0, sizeof(ifr));
> + ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
> +
> + if (ioctl(tapfd, TUNGETFEATURES, &features) == -1) {
> + PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
> + goto error;
> + }
> + if (features & IFF_ONE_QUEUE)
> + ifr.ifr_flags |= IFF_ONE_QUEUE;
> +
> + /* Let tap instead of vhost-net handle vnet header, as the latter does
> + * not support offloading. And in this case, we should not set feature
> + * bit VHOST_NET_F_VIRTIO_NET_HDR.
> + */
> + if (features & IFF_VNET_HDR) {
> + ifr.ifr_flags |= IFF_VNET_HDR;
> + } else {
> + PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
> + goto error;
> + }
> +
> + if (dev->ifname)
> + strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
> + else
> + strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
> + if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
> + PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
> + goto error;
> + }
This requires CAP_NET_ADMIN, so we should really consider to accept a
pre-created fd here.
> +
> + fcntl(tapfd, F_SETFL, O_NONBLOCK);
> +
> + if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
> + PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno));
> + goto error;
> + }
> +
> + if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
> + PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
> + goto error;
> + }
Let's use INT_MAX as default here to survive from evil consumer here.
> +
> + if (vhost_kernel_set_backend(vhostfd, tapfd) < 0)
> + goto error;
> +
> + dev->tapfds[pair_idx] = tapfd;
> + if (!dev->ifname)
> + dev->ifname = strdup(ifr.ifr_name);
> +
> + return 0;
> +error:
> + return -1;
> +}
> +
> +struct virtio_user_backend_ops ops_kernel = {
> + .setup = vhost_kernel_setup,
> + .send_request = vhost_kernel_ioctl,
> + .enable_qp = vhost_kernel_enable_queue_pair
> +};
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> index a818c29..c718b85 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> @@ -219,7 +219,7 @@ is_vhost_user_by_type(const char *path)
> static int
> virtio_user_dev_setup(struct virtio_user_dev *dev)
> {
> - uint32_t i;
> + uint32_t i, q;
>
> dev->vhostfd = -1;
> for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
> @@ -227,12 +227,18 @@ virtio_user_dev_setup(struct virtio_user_dev *dev)
> dev->callfds[i] = -1;
> }
>
> + for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
> + dev->vhostfds[q] = -1;
> + dev->tapfds[q] = -1;
> + }
> +
> if (is_vhost_user_by_type(dev->path)) {
> dev->ops = &ops_user;
> - return dev->ops->setup(dev);
> + } else {
> + dev->ops = &ops_kernel;
> }
>
> - return -1;
> + return dev->ops->setup(dev);
> }
>
> int
> @@ -284,7 +290,9 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
> void
> virtio_user_dev_uninit(struct virtio_user_dev *dev)
> {
> - uint32_t i;
> + uint32_t i, q;
> +
> + dev->ops->send_request(dev, VHOST_USER_RESET_OWNER, NULL);
>
> for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
> close(dev->callfds[i]);
> @@ -292,6 +300,11 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
> }
>
> close(dev->vhostfd);
> +
> + for (q = 0; q < VHOST_KERNEL_MAX_QUEUES; ++q) {
> + close(dev->vhostfds[q]);
> + close(dev->tapfds[q]);
> + }
> }
>
> static uint8_t
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> index 503a496..148b2e6 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
> @@ -44,6 +44,10 @@ struct virtio_user_dev {
> int vhostfd;
>
> /* for vhost_kernel backend */
> + char *ifname;
> +#define VHOST_KERNEL_MAX_QUEUES 8
> + int vhostfds[VHOST_KERNEL_MAX_QUEUES];
> + int tapfds[VHOST_KERNEL_MAX_QUEUES];
>
> /* for both vhost_user and vhost_kernel */
> int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-04 7:22 ` Tan, Jianfeng
2017-01-04 7:46 ` Yuanhan Liu
@ 2017-01-09 4:51 ` Jason Wang
1 sibling, 0 replies; 72+ messages in thread
From: Jason Wang @ 2017-01-09 4:51 UTC (permalink / raw)
To: Tan, Jianfeng, Yuanhan Liu; +Cc: dev, ferruh.yigit, cunming.liang
On 2017年01月04日 15:22, Tan, Jianfeng wrote:
>
> Sorry, I forget to reply this comment.
>
> On 12/26/2016 3:44 PM, Yuanhan Liu wrote:
>> [...]
>>> +
>>> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
>> Because this feature need the vhost IOTLB support from the device
>> emulation. Patches for QEMU hasn't been merged yet, but it has been
>> there for a while.
>
> Yes. And it's in need of help from QEMU.
>
>>
>> Since we don't have the support yet, for sure it won't work. But
>> I'm wondering why you have to disable it explicitly?
>
> Here we do not have QEMU. Frontend driver talks with vhost-net through
> virtio_user_dev. And both ends claim to support
> VIRTIO_F_IOMMU_PLATFORM. So this feature bit will be negotiated if we
> don't explicitly disable it. In my previous test, it fails in
> vhost_init_device_iotlb() of vhost kernel module.
Interesting, vhost_init_device_iotlb() only fail when OOM. Do you meet it?
> My guess is that, for this feature, without the help of QEMU, vhost
> kernel module cannot work independently.
Technically it can if your userspace supports device IOTLB APIs too.
Or if you're using static mappings, and preset all mappings through
VHOST_IOTLB_UPADATE to make sure no IOTLB misses.
Thanks
>
> Thanks,
> Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
` (6 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
@ 2017-01-09 14:06 ` Bruce Richardson
2017-01-10 8:46 ` Tan, Jianfeng
7 siblings, 1 reply; 72+ messages in thread
From: Bruce Richardson @ 2017-01-09 14:06 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, yuanhan.liu, ferruh.yigit, cunming.liang
On Wed, Jan 04, 2017 at 03:59:19AM +0000, Jianfeng Tan wrote:
> v3:
> - Drop the patch to postpone driver ok sending patch, superseded it
> with a bug fix to disable all virtqueues and re-init the device.
> (you might wonder why not just send reset owner msg. Under my test,
> it causes spinlock deadlock problem when killing the program).
> - Avoid compiling error on 32-bit system for pointer convert.
> - Fix a bug in patch "abstract virtio user backend ops", vhostfd is
> not properly assigned.
> - Fix a "MQ cannot be used" bug in v2, which is related to strip
> some feature bits that vhost kernel does not recognize.
> - Update release note.
>
> v2: (Lots of them are from yuanhan's comment)
> - Add offloding feature.
> - Add multiqueue support.
> - Add a new patch to postpone the sending of driver ok notification.
> - Put fix patch ahead of the whole patch series.
> - Split original 0001 patch into 0003 and 0004 patches.
> - Remove the original vhost_internal design, just add those into
> struct virtio_user_dev for simplicity.
> - Reword "control" to "send_request".
> - Reword "host_features" to "device_features".
>
> In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
> as the backend). The path to go with a vhost-kernel backend has been
> dropped for bad performance comparing to vhost-user and code simplicity.
>
> But after a second thought, virtio_user + vhost-kernel is a good
> candidate as an exceptional path, such as KNI, which exchanges packets
> with kernel networking stack.
> - maintenance: vhost-net (kernel) is upstreamed and extensively used
> kernel module. We don't need any out-of-tree module like KNI.
> - performance: as with KNI, this solution would use one or more
> kthreads to send/receive packets from user space DPDK applications,
> which has little impact on user space polling thread (except that
> it might enter into kernel space to wake up those kthreads if
> necessary).
> - features: vhost-net is born to be a networking solution, which has
> lots of networking related featuers, like multi queue, tso, multi-seg
> mbuf, etc.
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>
Sounds great. However, I think we'll need a how-to doc for this to help
people get it up and running as a KNI replacement for packets to/from
the kernel. Any plans to draw up such a doc? It would be good to include
it in this patchset.
/Bruce
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-09 4:39 ` Jason Wang
@ 2017-01-10 6:11 ` Tan, Jianfeng
2017-01-10 8:46 ` Thomas Monjalon
2017-01-11 2:42 ` Jason Wang
2017-01-11 2:30 ` Tan, Jianfeng
1 sibling, 2 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-10 6:11 UTC (permalink / raw)
To: Jason Wang, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
Hi Jason,
On 1/9/2017 12:39 PM, Jason Wang wrote:
>
>
> On 2016年12月23日 15:14, Jianfeng Tan wrote:
>> This patch add support vhost kernel as the backend for virtio_user.
>> Three main hook functions are added:
>> - vhost_kernel_setup() to open char device, each vq pair needs one
>> vhostfd;
>> - vhost_kernel_ioctl() to communicate control messages with vhost
>> kernel module;
>> - vhost_kernel_enable_queue_pair() to open tap device and set it
>> as the backend of corresonding vhost fd (that is to say, vq pair).
>>
>> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>> ---
>>
[...]
>> +/* TUNSETIFF ifr flags */
>> +#define IFF_TAP 0x0002
>> +#define IFF_NO_PI 0x1000
>> +#define IFF_ONE_QUEUE 0x2000
>> +#define IFF_VNET_HDR 0x4000
>> +#define IFF_MULTI_QUEUE 0x0100
>> +#define IFF_ATTACH_QUEUE 0x0200
>> +#define IFF_DETACH_QUEUE 0x0400
>
> Do we really want to duplicate those things which has been exposed by
> uapi here?
You mean those defined in <linux/if_tun.h>? Redefine those common
macros, or include standard header file, with respective pros and cons.
DPDK prefers the redefinition way as far as I understand, doesn't it?
>
>> +
>> +/* Constants */
>> +#define TUN_DEF_SNDBUF (1ull << 20)
>> +#define PATH_NET_TUN "/dev/net/tun"
>> +#define VHOST_KERNEL_MAX_REGIONS 64
>
> Unfortunate not a constant any more since c9ce42f72fd0 vhost: add
> max_mem_regions module parameter.
Yes, I was considering to ignore this in the initial release. But it's
not a big effort, I'll try to fix it in latest version.
>
>> +
>> +static uint64_t vhost_req_user_to_kernel[] = {
>> + [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
>> + [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,
>> + [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
>> + [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
>> + [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
>> + [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
>> +};
>> +
>> +/* By default, vhost kernel module allows 64 regions, but DPDK allows
>> + * 256 segments. As a relief, below function merges those virtually
>> + * adjacent memsegs into one region.
>> + */
>> +static struct vhost_memory_kernel *
>> +prepare_vhost_memory_kernel(void)
>> +{
>> + uint32_t i, j, k = 0;
>> + struct rte_memseg *seg;
>> + struct vhost_memory_region *mr;
>> + struct vhost_memory_kernel *vm;
>> +
>> + vm = malloc(sizeof(struct vhost_memory_kernel) +
>> + VHOST_KERNEL_MAX_REGIONS *
>> + sizeof(struct vhost_memory_region));
>> +
>> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
>> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
>> + if (!seg->addr)
>> + break;
>
> If we're sure the number of regions is less than 64(or the module
> parameter read from /sys), can we avoid the iteration here?
The "if" statement under the "for" statement can save us from all
RTE_MAX_MEMSEG iteration.
>
>> +
>> + int new_region = 1;
>> +
>> + for (j = 0; j < k; ++j) {
>> + mr = &vm->regions[j];
>> +
>> + if (mr->userspace_addr + mr->memory_size ==
>> + (uint64_t)seg->addr) {
>> + mr->memory_size += seg->len;
>> + new_region = 0;
>> + break;
>> + }
>> +
>> + if ((uint64_t)seg->addr + seg->len ==
>> + mr->userspace_addr) {
>> + mr->guest_phys_addr = (uint64_t)seg->addr;
>> + mr->userspace_addr = (uint64_t)seg->addr;
>> + mr->memory_size += seg->len;
>> + new_region = 0;
>> + break;
>> + }
>> + }
>> +
>> + if (new_region == 0)
>> + continue;
>> +
>> + mr = &vm->regions[k++];
>> + mr->guest_phys_addr = (uint64_t)seg->addr; /* use vaddr
>> here! */
>> + mr->userspace_addr = (uint64_t)seg->addr;
>> + mr->memory_size = seg->len;
>> + mr->mmap_offset = 0;
>> +
>> + if (k >= VHOST_KERNEL_MAX_REGIONS) {
>> + free(vm);
>> + return NULL;
>> + }
>> + }
>> +
>> + vm->nregions = k;
>> + vm->padding = 0;
>> + return vm;
>> +}
>> +
>> +static int
>> +vhost_kernel_ioctl(struct virtio_user_dev *dev,
>> + enum vhost_user_request req,
>> + void *arg)
>> +{
>> + int i, ret = -1;
>> + uint64_t req_kernel;
>> + struct vhost_memory_kernel *vm = NULL;
>> +
>> + req_kernel = vhost_req_user_to_kernel[req];
>> +
>> + if (req_kernel == VHOST_SET_MEM_TABLE) {
>> + vm = prepare_vhost_memory_kernel();
>> + if (!vm)
>> + return -1;
>> + arg = (void *)vm;
>> + }
>> +
>> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
>
> I think the reason is when VIRTIO_F_IOMMU_PLATFORM is negotiated, all
> address should be iova instead of gpa.
>
Yes, I agree. As we don't have to do memory protection in a single
process, so it's completely useless here, right?
>> + if (req_kernel == VHOST_SET_FEATURES)
>> + *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
>> +
>> + for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
>> + if (dev->vhostfds[i] < 0)
>> + continue;
>> +
[...]
>> + if (!enable) {
>> + if (dev->tapfds[pair_idx]) {
>> + close(dev->tapfds[pair_idx]);
>> + dev->tapfds[pair_idx] = -1;
>> + }
>> + return vhost_kernel_set_backend(vhostfd, -1);
>
> If this is used to for thing like ethtool -L in guest, we should use
> TUNSETQUEUE here.
Oops, I was missing this ioctl operation. Let me fix it in next version.
>
>> + } else if (dev->tapfds[pair_idx] >= 0) {
>> + return 0;
>> + }
>> +
>> + if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
>> + (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
>> + hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
>> + else
>> + hdr_size = sizeof(struct virtio_net_hdr);
>> +
>> + /* TODO:
>> + * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
>> + * 2. get number of memory regions from vhost module parameter
>> + * max_mem_regions, supported in newer version linux kernel
>> + */
>> + 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;
>> + }
>> +
>> + /* Construct ifr */
>> + memset(&ifr, 0, sizeof(ifr));
>> + ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
>> +
>> + if (ioctl(tapfd, TUNGETFEATURES, &features) == -1) {
>> + PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
>> + goto error;
>> + }
>> + if (features & IFF_ONE_QUEUE)
>> + ifr.ifr_flags |= IFF_ONE_QUEUE;
>> +
>> + /* Let tap instead of vhost-net handle vnet header, as the
>> latter does
>> + * not support offloading. And in this case, we should not set
>> feature
>> + * bit VHOST_NET_F_VIRTIO_NET_HDR.
>> + */
>> + if (features & IFF_VNET_HDR) {
>> + ifr.ifr_flags |= IFF_VNET_HDR;
>> + } else {
>> + PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
>> + goto error;
>> + }
>> +
>> + if (dev->ifname)
>> + strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
>> + else
>> + strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
>> + if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
>> + PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
>> + goto error;
>> + }
>
> This requires CAP_NET_ADMIN, so we should really consider to accept a
> pre-created fd here.
It sounds like a future work for me. So far, all DPDK apps are running
in privileged mode (including CAP_NET_ADMIN?).
>
>> +
>> + fcntl(tapfd, F_SETFL, O_NONBLOCK);
>> +
>> + if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
>> + PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s",
>> strerror(errno));
>> + goto error;
>> + }
>> +
>> + if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
>> + PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
>> + goto error;
>> + }
>
> Let's use INT_MAX as default here to survive from evil consumer here.
Oh yes, I will fix it.
Thanks,
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path
2017-01-09 14:06 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Bruce Richardson
@ 2017-01-10 8:46 ` Tan, Jianfeng
0 siblings, 0 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-10 8:46 UTC (permalink / raw)
To: Bruce Richardson; +Cc: dev, yuanhan.liu, ferruh.yigit, cunming.liang
On 1/9/2017 10:06 PM, Bruce Richardson wrote:
> On Wed, Jan 04, 2017 at 03:59:19AM +0000, Jianfeng Tan wrote:
>> v3:
>> - Drop the patch to postpone driver ok sending patch, superseded it
>> with a bug fix to disable all virtqueues and re-init the device.
>> (you might wonder why not just send reset owner msg. Under my test,
>> it causes spinlock deadlock problem when killing the program).
>> - Avoid compiling error on 32-bit system for pointer convert.
>> - Fix a bug in patch "abstract virtio user backend ops", vhostfd is
>> not properly assigned.
>> - Fix a "MQ cannot be used" bug in v2, which is related to strip
>> some feature bits that vhost kernel does not recognize.
>> - Update release note.
>>
>> v2: (Lots of them are from yuanhan's comment)
>> - Add offloding feature.
>> - Add multiqueue support.
>> - Add a new patch to postpone the sending of driver ok notification.
>> - Put fix patch ahead of the whole patch series.
>> - Split original 0001 patch into 0003 and 0004 patches.
>> - Remove the original vhost_internal design, just add those into
>> struct virtio_user_dev for simplicity.
>> - Reword "control" to "send_request".
>> - Reword "host_features" to "device_features".
>>
>> In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
>> as the backend). The path to go with a vhost-kernel backend has been
>> dropped for bad performance comparing to vhost-user and code simplicity.
>>
>> But after a second thought, virtio_user + vhost-kernel is a good
>> candidate as an exceptional path, such as KNI, which exchanges packets
>> with kernel networking stack.
>> - maintenance: vhost-net (kernel) is upstreamed and extensively used
>> kernel module. We don't need any out-of-tree module like KNI.
>> - performance: as with KNI, this solution would use one or more
>> kthreads to send/receive packets from user space DPDK applications,
>> which has little impact on user space polling thread (except that
>> it might enter into kernel space to wake up those kthreads if
>> necessary).
>> - features: vhost-net is born to be a networking solution, which has
>> lots of networking related featuers, like multi queue, tso, multi-seg
>> mbuf, etc.
>>
>> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>>
> Sounds great. However, I think we'll need a how-to doc for this to help
> people get it up and running as a KNI replacement for packets to/from
> the kernel. Any plans to draw up such a doc? It would be good to include
> it in this patchset.
I was planning to do that in separate patchset. But I agree with you
that it's better to do that in this patchset.
Thanks,
Jianfeng
>
> /Bruce
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-10 6:11 ` Tan, Jianfeng
@ 2017-01-10 8:46 ` Thomas Monjalon
2017-01-10 9:11 ` Tan, Jianfeng
2017-01-11 2:42 ` Jason Wang
1 sibling, 1 reply; 72+ messages in thread
From: Thomas Monjalon @ 2017-01-10 8:46 UTC (permalink / raw)
To: Tan, Jianfeng; +Cc: dev, Jason Wang, yuanhan.liu, ferruh.yigit, cunming.liang
2017-01-10 14:11, Tan, Jianfeng:
> On 1/9/2017 12:39 PM, Jason Wang wrote:
> > On 2016年12月23日 15:14, Jianfeng Tan wrote:
> [...]
> >> +/* TUNSETIFF ifr flags */
> >> +#define IFF_TAP 0x0002
> >> +#define IFF_NO_PI 0x1000
> >> +#define IFF_ONE_QUEUE 0x2000
> >> +#define IFF_VNET_HDR 0x4000
> >> +#define IFF_MULTI_QUEUE 0x0100
> >> +#define IFF_ATTACH_QUEUE 0x0200
> >> +#define IFF_DETACH_QUEUE 0x0400
> >
> > Do we really want to duplicate those things which has been exposed by
> > uapi here?
>
> You mean those defined in <linux/if_tun.h>? Redefine those common
> macros, or include standard header file, with respective pros and cons.
> DPDK prefers the redefinition way as far as I understand, doesn't it?
What is the benefit of copying defines instead of including?
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-10 8:46 ` Thomas Monjalon
@ 2017-01-10 9:11 ` Tan, Jianfeng
0 siblings, 0 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-10 9:11 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: dev, Jason Wang, yuanhan.liu, ferruh.yigit, cunming.liang
On 1/10/2017 4:46 PM, Thomas Monjalon wrote:
> 2017-01-10 14:11, Tan, Jianfeng:
>> On 1/9/2017 12:39 PM, Jason Wang wrote:
>>> On 2016年12月23日 15:14, Jianfeng Tan wrote:
>> [...]
>>>> +/* TUNSETIFF ifr flags */
>>>> +#define IFF_TAP 0x0002
>>>> +#define IFF_NO_PI 0x1000
>>>> +#define IFF_ONE_QUEUE 0x2000
>>>> +#define IFF_VNET_HDR 0x4000
>>>> +#define IFF_MULTI_QUEUE 0x0100
>>>> +#define IFF_ATTACH_QUEUE 0x0200
>>>> +#define IFF_DETACH_QUEUE 0x0400
>>> Do we really want to duplicate those things which has been exposed by
>>> uapi here?
>> You mean those defined in <linux/if_tun.h>? Redefine those common
>> macros, or include standard header file, with respective pros and cons.
>> DPDK prefers the redefinition way as far as I understand, doesn't it?
> What is the benefit of copying defines instead of including?
(1) Avoid compiling errors that old version missed some of macros.
(2) Better for portability (does not apply here).
I might take it for granted a little bit as I see some very popular open
source projects behave like this way, like QEMU, HAProxy, etc.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-09 4:39 ` Jason Wang
2017-01-10 6:11 ` Tan, Jianfeng
@ 2017-01-11 2:30 ` Tan, Jianfeng
2017-01-11 2:45 ` Jason Wang
1 sibling, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-11 2:30 UTC (permalink / raw)
To: Jason Wang, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
Hi Jason,
On 1/9/2017 12:39 PM, Jason Wang wrote:
>> + if (!enable) {
>> + if (dev->tapfds[pair_idx]) {
>> + close(dev->tapfds[pair_idx]);
>> + dev->tapfds[pair_idx] = -1;
>> + }
>> + return vhost_kernel_set_backend(vhostfd, -1);
>
> If this is used to for thing like ethtool -L in guest, we should use
> TUNSETQUEUE here.
To make it clear, why we need to ioctl(..., TUNSETQUEUE, ...) here.
According to Linux/Documentation/networking/tuntap.txt,
"A new ioctl(TUNSETQUEUE) were introduced to enable or disable a
queue. When
calling it with IFF_DETACH_QUEUE flag, the queue were disabled. And
when
calling it with IFF_ATTACH_QUEUE flag, the queue were enabled. The
queue were
enabled by default after it was created through TUNSETIFF."
As it's enabled by default, do you still see the necessity to call it
explicitly?
Thanks,
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-10 6:11 ` Tan, Jianfeng
2017-01-10 8:46 ` Thomas Monjalon
@ 2017-01-11 2:42 ` Jason Wang
2017-01-11 3:13 ` Tan, Jianfeng
1 sibling, 1 reply; 72+ messages in thread
From: Jason Wang @ 2017-01-11 2:42 UTC (permalink / raw)
To: Tan, Jianfeng, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 2017年01月10日 14:11, Tan, Jianfeng wrote:
>
> Hi Jason,
>
>
> On 1/9/2017 12:39 PM, Jason Wang wrote:
>>
>>
>> On 2016年12月23日 15:14, Jianfeng Tan wrote:
>>> This patch add support vhost kernel as the backend for virtio_user.
>>> Three main hook functions are added:
>>> - vhost_kernel_setup() to open char device, each vq pair needs one
>>> vhostfd;
>>> - vhost_kernel_ioctl() to communicate control messages with vhost
>>> kernel module;
>>> - vhost_kernel_enable_queue_pair() to open tap device and set it
>>> as the backend of corresonding vhost fd (that is to say, vq pair).
>>>
>>> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>>> ---
>>>
> [...]
>>> +/* TUNSETIFF ifr flags */
>>> +#define IFF_TAP 0x0002
>>> +#define IFF_NO_PI 0x1000
>>> +#define IFF_ONE_QUEUE 0x2000
>>> +#define IFF_VNET_HDR 0x4000
>>> +#define IFF_MULTI_QUEUE 0x0100
>>> +#define IFF_ATTACH_QUEUE 0x0200
>>> +#define IFF_DETACH_QUEUE 0x0400
>>
>> Do we really want to duplicate those things which has been exposed by
>> uapi here?
>
> You mean those defined in <linux/if_tun.h>? Redefine those common
> macros, or include standard header file, with respective pros and
> cons. DPDK prefers the redefinition way as far as I understand,
> doesn't it?
>
Well, if you really want to do this, you may want to use an independent
file. Then you can sync it with linux headers with a bash script.
>>
>>> +
>>> +/* Constants */
>>> +#define TUN_DEF_SNDBUF (1ull << 20)
>>> +#define PATH_NET_TUN "/dev/net/tun"
>>> +#define VHOST_KERNEL_MAX_REGIONS 64
>>
>> Unfortunate not a constant any more since c9ce42f72fd0 vhost: add
>> max_mem_regions module parameter.
>
> Yes, I was considering to ignore this in the initial release. But it's
> not a big effort, I'll try to fix it in latest version.
>
>>
>>> +
>>> +static uint64_t vhost_req_user_to_kernel[] = {
>>> + [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
>>> + [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,
>>> + [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
>>> + [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
>>> + [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
>>> + [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
>>> +};
>>> +
>>> +/* By default, vhost kernel module allows 64 regions, but DPDK allows
>>> + * 256 segments. As a relief, below function merges those virtually
>>> + * adjacent memsegs into one region.
>>> + */
>>> +static struct vhost_memory_kernel *
>>> +prepare_vhost_memory_kernel(void)
>>> +{
>>> + uint32_t i, j, k = 0;
>>> + struct rte_memseg *seg;
>>> + struct vhost_memory_region *mr;
>>> + struct vhost_memory_kernel *vm;
>>> +
>>> + vm = malloc(sizeof(struct vhost_memory_kernel) +
>>> + VHOST_KERNEL_MAX_REGIONS *
>>> + sizeof(struct vhost_memory_region));
>>> +
>>> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
>>> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
>>> + if (!seg->addr)
>>> + break;
>>
>> If we're sure the number of regions is less than 64(or the module
>> parameter read from /sys), can we avoid the iteration here?
>
> The "if" statement under the "for" statement can save us from all
> RTE_MAX_MEMSEG iteration.
But if we know the number of regions is short than the limit, there's
even no need for this?
>
>>
>>> +
>>> + int new_region = 1;
>>> +
>>> + for (j = 0; j < k; ++j) {
>>> + mr = &vm->regions[j];
>>> +
>>> + if (mr->userspace_addr + mr->memory_size ==
>>> + (uint64_t)seg->addr) {
>>> + mr->memory_size += seg->len;
>>> + new_region = 0;
>>> + break;
>>> + }
>>> +
>>> + if ((uint64_t)seg->addr + seg->len ==
>>> + mr->userspace_addr) {
>>> + mr->guest_phys_addr = (uint64_t)seg->addr;
>>> + mr->userspace_addr = (uint64_t)seg->addr;
>>> + mr->memory_size += seg->len;
>>> + new_region = 0;
>>> + break;
>>> + }
>>> + }
>>> +
>>> + if (new_region == 0)
>>> + continue;
>>> +
>>> + mr = &vm->regions[k++];
>>> + mr->guest_phys_addr = (uint64_t)seg->addr; /* use vaddr
>>> here! */
>>> + mr->userspace_addr = (uint64_t)seg->addr;
>>> + mr->memory_size = seg->len;
>>> + mr->mmap_offset = 0;
>>> +
>>> + if (k >= VHOST_KERNEL_MAX_REGIONS) {
>>> + free(vm);
>>> + return NULL;
>>> + }
>>> + }
>>> +
>>> + vm->nregions = k;
>>> + vm->padding = 0;
>>> + return vm;
>>> +}
>>> +
>>> +static int
>>> +vhost_kernel_ioctl(struct virtio_user_dev *dev,
>>> + enum vhost_user_request req,
>>> + void *arg)
>>> +{
>>> + int i, ret = -1;
>>> + uint64_t req_kernel;
>>> + struct vhost_memory_kernel *vm = NULL;
>>> +
>>> + req_kernel = vhost_req_user_to_kernel[req];
>>> +
>>> + if (req_kernel == VHOST_SET_MEM_TABLE) {
>>> + vm = prepare_vhost_memory_kernel();
>>> + if (!vm)
>>> + return -1;
>>> + arg = (void *)vm;
>>> + }
>>> +
>>> + /* Does not work when VIRTIO_F_IOMMU_PLATFORM now, why? */
>>
>> I think the reason is when VIRTIO_F_IOMMU_PLATFORM is negotiated, all
>> address should be iova instead of gpa.
>>
>
> Yes, I agree. As we don't have to do memory protection in a single
> process, so it's completely useless here, right?
Yes if there's no IOMMU concept in this case.
>
>>> + if (req_kernel == VHOST_SET_FEATURES)
>>> + *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
>>> +
>>> + for (i = 0; i < VHOST_KERNEL_MAX_QUEUES; ++i) {
>>> + if (dev->vhostfds[i] < 0)
>>> + continue;
>>> +
> [...]
>>> + if (!enable) {
>>> + if (dev->tapfds[pair_idx]) {
>>> + close(dev->tapfds[pair_idx]);
>>> + dev->tapfds[pair_idx] = -1;
>>> + }
>>> + return vhost_kernel_set_backend(vhostfd, -1);
>>
>> If this is used to for thing like ethtool -L in guest, we should use
>> TUNSETQUEUE here.
>
> Oops, I was missing this ioctl operation. Let me fix it in next version.
>
>>
>>> + } else if (dev->tapfds[pair_idx] >= 0) {
>>> + return 0;
>>> + }
>>> +
>>> + if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
>>> + (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
>>> + hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
>>> + else
>>> + hdr_size = sizeof(struct virtio_net_hdr);
>>> +
>>> + /* TODO:
>>> + * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
>>> + * 2. get number of memory regions from vhost module parameter
>>> + * max_mem_regions, supported in newer version linux kernel
>>> + */
>>> + 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;
>>> + }
>>> +
>>> + /* Construct ifr */
>>> + memset(&ifr, 0, sizeof(ifr));
>>> + ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
>>> +
>>> + if (ioctl(tapfd, TUNGETFEATURES, &features) == -1) {
>>> + PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s",
>>> strerror(errno));
>>> + goto error;
>>> + }
>>> + if (features & IFF_ONE_QUEUE)
>>> + ifr.ifr_flags |= IFF_ONE_QUEUE;
>>> +
>>> + /* Let tap instead of vhost-net handle vnet header, as the
>>> latter does
>>> + * not support offloading. And in this case, we should not set
>>> feature
>>> + * bit VHOST_NET_F_VIRTIO_NET_HDR.
>>> + */
>>> + if (features & IFF_VNET_HDR) {
>>> + ifr.ifr_flags |= IFF_VNET_HDR;
>>> + } else {
>>> + PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
>>> + goto error;
>>> + }
>>> +
>>> + if (dev->ifname)
>>> + strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
>>> + else
>>> + strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
>>> + if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
>>> + PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
>>> + goto error;
>>> + }
>>
>> This requires CAP_NET_ADMIN, so we should really consider to accept a
>> pre-created fd here.
>
> It sounds like a future work for me. So far, all DPDK apps are running
> in privileged mode (including CAP_NET_ADMIN?).
>
That's not safe. Accepting a per-created fd can solve this, and can even
support macvtap.
>>
>>> +
>>> + fcntl(tapfd, F_SETFL, O_NONBLOCK);
>>> +
>>> + if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
>>> + PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s",
>>> strerror(errno));
>>> + goto error;
>>> + }
>>> +
>>> + if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
>>> + PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
>>> + goto error;
>>> + }
>>
>> Let's use INT_MAX as default here to survive from evil consumer here.
>
> Oh yes, I will fix it.
>
> Thanks,
> Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-11 2:30 ` Tan, Jianfeng
@ 2017-01-11 2:45 ` Jason Wang
0 siblings, 0 replies; 72+ messages in thread
From: Jason Wang @ 2017-01-11 2:45 UTC (permalink / raw)
To: Tan, Jianfeng, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 2017年01月11日 10:30, Tan, Jianfeng wrote:
>
> Hi Jason,
>
>
> On 1/9/2017 12:39 PM, Jason Wang wrote:
>>> + if (!enable) {
>>> + if (dev->tapfds[pair_idx]) {
>>> + close(dev->tapfds[pair_idx]);
>>> + dev->tapfds[pair_idx] = -1;
>>> + }
>>> + return vhost_kernel_set_backend(vhostfd, -1);
>>
>> If this is used to for thing like ethtool -L in guest, we should use
>> TUNSETQUEUE here.
>
> To make it clear, why we need to ioctl(..., TUNSETQUEUE, ...) here.
> According to Linux/Documentation/networking/tuntap.txt,
> "A new ioctl(TUNSETQUEUE) were introduced to enable or disable a
> queue. When
> calling it with IFF_DETACH_QUEUE flag, the queue were disabled.
> And when
> calling it with IFF_ATTACH_QUEUE flag, the queue were enabled. The
> queue were
> enabled by default after it was created through TUNSETIFF."
>
> As it's enabled by default, do you still see the necessity to call it
> explicitly?
If you want to keep it enabled, no need. But if you want to disable one
specific queue (which I believe is the case of !enable?), you need to
call it.
Thanks
>
> Thanks,
> Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-11 2:42 ` Jason Wang
@ 2017-01-11 3:13 ` Tan, Jianfeng
2017-01-11 3:23 ` Jason Wang
0 siblings, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-11 3:13 UTC (permalink / raw)
To: Jason Wang, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 1/11/2017 10:42 AM, Jason Wang wrote:
>
>
> On 2017年01月10日 14:11, Tan, Jianfeng wrote:
>>
>> Hi Jason,
>>
>>
>> On 1/9/2017 12:39 PM, Jason Wang wrote:
>>>
>>>
>>> On 2016年12月23日 15:14, Jianfeng Tan wrote:
>>>> This patch add support vhost kernel as the backend for virtio_user.
>>>> Three main hook functions are added:
>>>> - vhost_kernel_setup() to open char device, each vq pair needs one
>>>> vhostfd;
>>>> - vhost_kernel_ioctl() to communicate control messages with vhost
>>>> kernel module;
>>>> - vhost_kernel_enable_queue_pair() to open tap device and set it
>>>> as the backend of corresonding vhost fd (that is to say, vq
>>>> pair).
>>>>
>>>> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>>>> ---
>>>>
>> [...]
>>>> +/* TUNSETIFF ifr flags */
>>>> +#define IFF_TAP 0x0002
>>>> +#define IFF_NO_PI 0x1000
>>>> +#define IFF_ONE_QUEUE 0x2000
>>>> +#define IFF_VNET_HDR 0x4000
>>>> +#define IFF_MULTI_QUEUE 0x0100
>>>> +#define IFF_ATTACH_QUEUE 0x0200
>>>> +#define IFF_DETACH_QUEUE 0x0400
>>>
>>> Do we really want to duplicate those things which has been exposed
>>> by uapi here?
>>
>> You mean those defined in <linux/if_tun.h>? Redefine those common
>> macros, or include standard header file, with respective pros and
>> cons. DPDK prefers the redefinition way as far as I understand,
>> doesn't it?
>>
>
> Well, if you really want to do this, you may want to use an
> independent file. Then you can sync it with linux headers with a bash
> script.
Agreed!
[...]
>>>> +static struct vhost_memory_kernel *
>>>> +prepare_vhost_memory_kernel(void)
>>>> +{
>>>> + uint32_t i, j, k = 0;
>>>> + struct rte_memseg *seg;
>>>> + struct vhost_memory_region *mr;
>>>> + struct vhost_memory_kernel *vm;
>>>> +
>>>> + vm = malloc(sizeof(struct vhost_memory_kernel) +
>>>> + VHOST_KERNEL_MAX_REGIONS *
>>>> + sizeof(struct vhost_memory_region));
>>>> +
>>>> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
>>>> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
>>>> + if (!seg->addr)
>>>> + break;
>>>
>>> If we're sure the number of regions is less than 64(or the module
>>> parameter read from /sys), can we avoid the iteration here?
>>
>> The "if" statement under the "for" statement can save us from all
>> RTE_MAX_MEMSEG iteration.
>
> But if we know the number of regions is short than the limit, there's
> even no need for this?
The problem is: we don't have a variable to know how many segments are
there in DPDK. Anyway, we need to report error in the situation that #
of segments > # of regions. Or can you give a more specific example?
[...]
>>>> + if (dev->ifname)
>>>> + strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
>>>> + else
>>>> + strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
>>>> + if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
>>>> + PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
>>>> + goto error;
>>>> + }
>>>
>>> This requires CAP_NET_ADMIN, so we should really consider to accept
>>> a pre-created fd here.
>>
>> It sounds like a future work for me. So far, all DPDK apps are
>> running in privileged mode (including CAP_NET_ADMIN?).
>>
>
> That's not safe.
Yes.
> Accepting a per-created fd can solve this, and can even support macvtap
So you are proposing a way to specify the virtio_user parameter like,
--vdev=virtio_user,tapfd=fd1,xxx, right? As replied in your another
comment, it's a great way to go. But I'm afraid we have no time to
implement that in this cycle.
Thanks,
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-11 3:13 ` Tan, Jianfeng
@ 2017-01-11 3:23 ` Jason Wang
2017-01-12 9:40 ` Tan, Jianfeng
0 siblings, 1 reply; 72+ messages in thread
From: Jason Wang @ 2017-01-11 3:23 UTC (permalink / raw)
To: Tan, Jianfeng, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 2017年01月11日 11:13, Tan, Jianfeng wrote:
>
>
> On 1/11/2017 10:42 AM, Jason Wang wrote:
>>
>>
>> On 2017年01月10日 14:11, Tan, Jianfeng wrote:
>>>
>>> Hi Jason,
>>>
>>>
>>> On 1/9/2017 12:39 PM, Jason Wang wrote:
>>>>
>>>>
>>>> On 2016年12月23日 15:14, Jianfeng Tan wrote:
>>>>> This patch add support vhost kernel as the backend for virtio_user.
>>>>> Three main hook functions are added:
>>>>> - vhost_kernel_setup() to open char device, each vq pair needs one
>>>>> vhostfd;
>>>>> - vhost_kernel_ioctl() to communicate control messages with vhost
>>>>> kernel module;
>>>>> - vhost_kernel_enable_queue_pair() to open tap device and set it
>>>>> as the backend of corresonding vhost fd (that is to say, vq
>>>>> pair).
>>>>>
>>>>> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
>>>>> ---
>>>>>
>>> [...]
>>>>> +/* TUNSETIFF ifr flags */
>>>>> +#define IFF_TAP 0x0002
>>>>> +#define IFF_NO_PI 0x1000
>>>>> +#define IFF_ONE_QUEUE 0x2000
>>>>> +#define IFF_VNET_HDR 0x4000
>>>>> +#define IFF_MULTI_QUEUE 0x0100
>>>>> +#define IFF_ATTACH_QUEUE 0x0200
>>>>> +#define IFF_DETACH_QUEUE 0x0400
>>>>
>>>> Do we really want to duplicate those things which has been exposed
>>>> by uapi here?
>>>
>>> You mean those defined in <linux/if_tun.h>? Redefine those common
>>> macros, or include standard header file, with respective pros and
>>> cons. DPDK prefers the redefinition way as far as I understand,
>>> doesn't it?
>>>
>>
>> Well, if you really want to do this, you may want to use an
>> independent file. Then you can sync it with linux headers with a bash
>> script.
>
> Agreed!
>
> [...]
>>>>> +static struct vhost_memory_kernel *
>>>>> +prepare_vhost_memory_kernel(void)
>>>>> +{
>>>>> + uint32_t i, j, k = 0;
>>>>> + struct rte_memseg *seg;
>>>>> + struct vhost_memory_region *mr;
>>>>> + struct vhost_memory_kernel *vm;
>>>>> +
>>>>> + vm = malloc(sizeof(struct vhost_memory_kernel) +
>>>>> + VHOST_KERNEL_MAX_REGIONS *
>>>>> + sizeof(struct vhost_memory_region));
>>>>> +
>>>>> + for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
>>>>> + seg = &rte_eal_get_configuration()->mem_config->memseg[i];
>>>>> + if (!seg->addr)
>>>>> + break;
>>>>
>>>> If we're sure the number of regions is less than 64(or the module
>>>> parameter read from /sys), can we avoid the iteration here?
>>>
>>> The "if" statement under the "for" statement can save us from all
>>> RTE_MAX_MEMSEG iteration.
>>
>> But if we know the number of regions is short than the limit, there's
>> even no need for this?
>
> The problem is: we don't have a variable to know how many segments are
> there in DPDK. Anyway, we need to report error in the situation that #
> of segments > # of regions. Or can you give a more specific example?
>
> [...]
If we don't know #segments, I'm fine with current code.
>>>>> + if (dev->ifname)
>>>>> + strncpy(ifr.ifr_name, dev->ifname, IFNAMSIZ);
>>>>> + else
>>>>> + strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
>>>>> + if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
>>>>> + PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
>>>>> + goto error;
>>>>> + }
>>>>
>>>> This requires CAP_NET_ADMIN, so we should really consider to accept
>>>> a pre-created fd here.
>>>
>>> It sounds like a future work for me. So far, all DPDK apps are
>>> running in privileged mode (including CAP_NET_ADMIN?).
>>>
>>
>> That's not safe.
>
> Yes.
>
>> Accepting a per-created fd can solve this, and can even support macvtap
>
> So you are proposing a way to specify the virtio_user parameter like,
> --vdev=virtio_user,tapfd=fd1,xxx, right? As replied in your another
> comment, it's a great way to go. But I'm afraid we have no time to
> implement that in this cycle.
>
> Thanks,
> Jianfeng
Ok, just want to show its advantages. It can be added on top.
And two more suggestions:
- better to split tap support out of vhost file
- kernel support more than 8 queues on recent kernel, so there's no need
to limit it to 8. When running on old kernel, TUNSETIFF will fail which
can give user a hint to reduce #queues.
Thanks
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-11 3:23 ` Jason Wang
@ 2017-01-12 9:40 ` Tan, Jianfeng
2017-01-13 2:27 ` Jason Wang
0 siblings, 1 reply; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-12 9:40 UTC (permalink / raw)
To: Jason Wang, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
Hi Jason,
> Ok, just want to show its advantages. It can be added on top.
>
> And two more suggestions:
> - better to split tap support out of vhost file
Good suggestion! Will do that in next version.
> - kernel support more than 8 queues on recent kernel, so there's no
> need to limit it to 8. When running on old kernel, TUNSETIFF will fail
> which can give user a hint to reduce #queues.
>
After a try, even we remove such restriction in vhost_kernel backend, we
are still restricted by VIRTIO_MAX_VIRTQUEUES (8) defined in virtio PMD.
Thanks for the suggestions!
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support
2017-01-12 9:40 ` Tan, Jianfeng
@ 2017-01-13 2:27 ` Jason Wang
0 siblings, 0 replies; 72+ messages in thread
From: Jason Wang @ 2017-01-13 2:27 UTC (permalink / raw)
To: Tan, Jianfeng, dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang
On 2017年01月12日 17:40, Tan, Jianfeng wrote:
> Hi Jason,
>
>> Ok, just want to show its advantages. It can be added on top.
>>
>> And two more suggestions:
>> - better to split tap support out of vhost file
>
> Good suggestion! Will do that in next version.
>
>> - kernel support more than 8 queues on recent kernel, so there's no
>> need to limit it to 8. When running on old kernel, TUNSETIFF will
>> fail which can give user a hint to reduce #queues.
>>
>
> After a try, even we remove such restriction in vhost_kernel backend,
> we are still restricted by VIRTIO_MAX_VIRTQUEUES (8) defined in virtio
> PMD.
I think we'd better remove this limitation in the future.
Thanks
>
> Thanks for the suggestions!
>
> Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 0/8] virtio_user as an alternative exception path
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
` (5 preceding siblings ...)
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 1/8] net/virtio_user: fix wrongly get/set features Jianfeng Tan
` (8 more replies)
6 siblings, 9 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
v4:
- Fix a clang compiling error by removing "NULL" line in the definition
of vhost_msg_strings. This error does not show up when it's defined
as a static variable, so not necessary to fix it in stable branch.
- Query kernel to get how many regions are supported, default 64 regions.
- Set TUNSETSNDBUF to INT_MAX.
- When get_features, unmask those backend-specific feature bits.
- Remove VHOST_KERNEL_MAX_QUEUES (8) restriction, but due to another
restriction by VIRTIO_MAX_VIRTQUEUES (8), we still cannot configure
more than 8 queues.
- Add a howto document.
v3:
- Drop the patch to postpone driver ok sending patch, superseded it
with a bug fix to disable all virtqueues and re-init the device.
(you might wonder why not just send reset owner msg. Under my test,
it causes spinlock deadlock problem when killing the program).
- Avoid compiling error on 32-bit system for pointer convert.
- Fix a bug in patch "abstract virtio user backend ops", vhostfd is
not properly assigned.
- Fix a "MQ cannot be used" bug in v2, which is related to strip
some feature bits that vhost kernel does not recognize.
- Update release note.
v2: (Lots of them are from yuanhan's comment)
- Add offloding feature.
- Add multiqueue support.
- Add a new patch to postpone the sending of driver ok notification.
- Put fix patch ahead of the whole patch series.
- Split original 0001 patch into 0003 and 0004 patches.
- Remove the original vhost_internal design, just add those into
struct virtio_user_dev for simplicity.
- Reword "control" to "send_request".
- Reword "host_features" to "device_features".
In v16.07, we upstreamed a virtual device, virtio_user (with vhost-user
as the backend). The path to go with a vhost-kernel backend has been
dropped for bad performance comparing to vhost-user and code simplicity.
But after a second thought, virtio_user + vhost-kernel is a good
candidate as an exceptional path, such as KNI, which exchanges packets
with kernel networking stack.
- maintenance: vhost-net (kernel) is upstreamed and extensively used
kernel module. We don't need any out-of-tree module like KNI.
- performance: as with KNI, this solution would use one or more
kthreads to send/receive packets from user space DPDK applications,
which has little impact on user space polling thread (except that
it might enter into kernel space to wake up those kthreads if
necessary).
- features: vhost-net is born to be a networking solution, which has
lots of networking related featuers, like multi queue, tso, multi-seg
mbuf, etc.
How to test:
Here is a simple test case for:
_______testpmd_______
iperf-c/iperf-s -- tap0 -- [virtio_user0 -- ixgbe]
|
|
|
iperf-s/iperf-c -- ixgbe (kernel driver, eth3)
Step 1: Remove MAC addres setting in testpmd csum fwd engine.
diff --git a/app/test-pmd/csumonly.c b/app/test-pmd/csumonly.c
index 57e6ae2..225c7a8 100644
--- a/app/test-pmd/csumonly.c
+++ b/app/test-pmd/csumonly.c
@@ -706,10 +706,12 @@ pkt_burst_checksum_forward(struct fwd_stream *fs)
* and inner headers */
eth_hdr = rte_pktmbuf_mtod(m, struct ether_hdr *);
+#if 0
ether_addr_copy(&peer_eth_addrs[fs->peer_addr],
ð_hdr->d_addr);
ether_addr_copy(&ports[fs->tx_port].eth_addr,
ð_hdr->s_addr);
+#endif
parse_ethernet(eth_hdr, &info);
l3_hdr = (char *)eth_hdr + info.l2_len;
Step 2: bind one ixgbe with igb_uio, and start testpmd
$(testpmd) -c 0xc -n 4 \
--vdev=virtio_user0,path=/dev/vhost-net,queue_size=1024 \
-- -i --txqflags=0x0 --disable-hw-vlan --enable-lro \
--enable-rx-cksum --rxd=1024 --txd=1024
To test multiqueue, start testpmd like this:
$(testpmd) -c 0xc -n 4 \
--vdev=virtio_user0,path=/dev/vhost-net,queues=2,queue_size=1024 \
-- -i --txqflags=0x0 --disable-hw-vlan --enable-lro --enable-rx-cksum \
--txq=2 --rxq=2 --rxd=1024 --txd=1024
Step 3: start testpmd: (port 0 is ixgbe, port 1 is virtio_user)
# set fwd csum
# csum set ip hw 0
# csum set tcp hw 0
# csum set ip sw 1
# csum set tcp hw 1
# tso set 1448 0
# tso set 1448 1
# start
Step 4: start the other end with below script
$ ip netns add ns1
$ ip link set eth3 netns ns1
$ ip netns exec ns1 ifconfig eth3 1.1.1.2/24 up
$ ip netns exec ns1 taskset 0xf0 iperf3 -s -i 1
Use below command if you change the position of iperf-c and iperf-s.
$ ip netns exec ns1 numactl -N 0 iperf3 -c 1.1.1.3 -i 1 -t 30
Step 5: up the tap0 and configure ip
$ ifconfig tap0 1.1.1.3/24 up
Step 6: start test
$ iperf3 -c 1.1.1.2 -i 1 -t 30
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
Jianfeng Tan (8):
net/virtio_user: fix wrongly get/set features
net/virtio_user: fix not properly reset device
net/virtio_user: move vhost user specific code
net/virtio_user: abstract virtio user backend ops
net/virtio_user: add vhost kernel support
net/virtio_user: enable offloading
net/virtio_user: enable multiqueue with vhost kernel
doc: add guide to use virtio_user as exceptional path
.../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
doc/guides/prog_guide/index.rst | 1 +
.../prog_guide/virtio_user_as_exceptional_path.rst | 104 ++++++
doc/guides/rel_notes/release_17_02.rst | 20 +
drivers/net/virtio/Makefile | 2 +
drivers/net/virtio/virtio_ethdev.h | 5 +
drivers/net/virtio/virtio_user/vhost.h | 51 +--
drivers/net/virtio/virtio_user/vhost_kernel.c | 401 +++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost_kernel_tap.c | 133 +++++++
drivers/net/virtio/virtio_user/vhost_kernel_tap.h | 67 ++++
drivers/net/virtio/virtio_user/vhost_user.c | 98 +++--
drivers/net/virtio/virtio_user/virtio_user_dev.c | 153 +++++---
drivers/net/virtio/virtio_user/virtio_user_dev.h | 15 +-
drivers/net/virtio/virtio_user_ethdev.c | 20 +-
14 files changed, 945 insertions(+), 125 deletions(-)
create mode 100644 doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
create mode 100644 doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel_tap.c
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel_tap.h
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 1/8] net/virtio_user: fix wrongly get/set features
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 2/8] net/virtio_user: fix not properly reset device Jianfeng Tan
` (7 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan, stable
Before the commit 86d59b21468a ("net/virtio: support LRO"), features
in virtio PMD, is decided and properly set at device initialization
and will not be changed. But afterward, features could be changed in
virtio_dev_configure(), and will be re-negotiated if it's changed.
In virtio_user, device features is obtained at driver probe phase
only once, but we did not store it. So the added feature bits in
re-negotiation will fail.
To fix it, we store it down, and will be used to feature negotiation
either at device initialization phase or device configure phase.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
CC: stable@dpdk.org
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_ethdev.h | 5 ++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 34 +++++++++++-------------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 +++-
drivers/net/virtio/virtio_user_ethdev.c | 5 ++--
4 files changed, 28 insertions(+), 21 deletions(-)
diff --git a/drivers/net/virtio/virtio_ethdev.h b/drivers/net/virtio/virtio_ethdev.h
index 27d9a19..4feccf9 100644
--- a/drivers/net/virtio/virtio_ethdev.h
+++ b/drivers/net/virtio/virtio_ethdev.h
@@ -70,6 +70,11 @@
1ULL << VIRTIO_F_VERSION_1 | \
1ULL << VIRTIO_F_IOMMU_PLATFORM)
+#define VIRTIO_PMD_SUPPORTED_GUEST_FEATURES \
+ (VIRTIO_PMD_DEFAULT_GUEST_FEATURES | \
+ 1u << VIRTIO_NET_F_GUEST_CSUM | \
+ 1u << VIRTIO_NET_F_GUEST_TSO4 | \
+ 1u << VIRTIO_NET_F_GUEST_TSO6)
/*
* CQ function prototype
*/
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index e239e0e..0d7e17b 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -148,12 +148,13 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 1: set features
* Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * and VIRTIO_NET_F_MAC is stripped.
+ * VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
*/
features = dev->features;
if (dev->max_queue_pairs > 1)
features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
+ features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
@@ -228,29 +229,26 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
}
if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
- &dev->features) < 0) {
+ &dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
}
if (dev->mac_specified)
- dev->features |= (1ull << VIRTIO_NET_F_MAC);
+ dev->device_features |= (1ull << VIRTIO_NET_F_MAC);
- if (!cq) {
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
- /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
- dev->features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
- dev->features &= ~(1ull << VIRTIO_NET_F_MQ);
- dev->features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
- } else {
- /* vhost user backend does not need to know ctrl-q, so
- * actually we need add this bit into features. However,
- * DPDK vhost-user does send features with this bit, so we
- * check it instead of OR it for now.
+ if (cq) {
+ /* device does not really need to know anything about CQ,
+ * so if necessary, we just claim to support CQ
*/
- if (!(dev->features & (1ull << VIRTIO_NET_F_CTRL_VQ)))
- PMD_INIT_LOG(INFO, "vhost does not support ctrl-q");
+ dev->device_features |= (1ull << VIRTIO_NET_F_CTRL_VQ);
+ } else {
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
+ /* Also disable features depends on VIRTIO_NET_F_CTRL_VQ */
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_RX);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_VLAN);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_GUEST_ANNOUNCE);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_MQ);
+ dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
if (dev->max_queue_pairs > 1) {
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 33690b5..28fc788 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -46,7 +46,10 @@ struct virtio_user_dev {
uint32_t max_queue_pairs;
uint32_t queue_pairs;
uint32_t queue_size;
- uint64_t features;
+ uint64_t features; /* the negotiated features with driver,
+ * and will be sync with device
+ */
+ uint64_t device_features; /* supported features by device */
uint8_t status;
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 8cb983c..6c16d2d 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -117,7 +117,8 @@ virtio_user_get_features(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- return dev->features;
+ /* unmask feature bits defined in vhost user protocol */
+ return dev->device_features & VIRTIO_PMD_SUPPORTED_GUEST_FEATURES;
}
static void
@@ -125,7 +126,7 @@ virtio_user_set_features(struct virtio_hw *hw, uint64_t features)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- dev->features = features;
+ dev->features = features & dev->device_features;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 2/8] net/virtio_user: fix not properly reset device
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 1/8] net/virtio_user: fix wrongly get/set features Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 3/8] net/virtio_user: move vhost user specific code Jianfeng Tan
` (6 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan, stable
virtio_user is not properly reset when users call vtpci_reset(),
as it ignores VIRTIO_CONFIG_STATUS_RESET status in
virtio_user_set_status().
This might lead to initialization failure as it starts to re-init
the device before sending RESET messege to backend. Besides, previous
callfds and kickfds are not closed.
To fix it, we add support to disable virtqueues when it's set to
DRIVER OK status, and re-init fields in struct virtio_user_dev.
Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
Fixes: 37a7eb2ae816 ("net/virtio-user: add device emulation layer")
CC: stable@dpdk.org
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/virtio_user_dev.c | 26 ++++++++++++++++--------
drivers/net/virtio/virtio_user_ethdev.c | 15 ++++++++------
2 files changed, 27 insertions(+), 14 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 0d7e17b..a38398b 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -182,7 +182,17 @@ virtio_user_start_device(struct virtio_user_dev *dev)
int virtio_user_stop_device(struct virtio_user_dev *dev)
{
- return vhost_user_sock(dev->vhostfd, VHOST_USER_RESET_OWNER, NULL);
+ uint32_t i;
+
+ for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
+ close(dev->callfds[i]);
+ close(dev->kickfds[i]);
+ }
+
+ for (i = 0; i < dev->max_queue_pairs; ++i)
+ vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+
+ return 0;
}
static inline void
@@ -210,6 +220,8 @@ int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
int cq, int queue_size, const char *mac)
{
+ uint32_t i;
+
snprintf(dev->path, PATH_MAX, "%s", path);
dev->max_queue_pairs = queues;
dev->queue_pairs = 1; /* mq disabled by default */
@@ -218,6 +230,11 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
parse_mac(dev, mac);
dev->vhostfd = -1;
+ for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
+ dev->kickfds[i] = -1;
+ dev->callfds[i] = -1;
+ }
+
dev->vhostfd = vhost_user_setup(dev->path);
if (dev->vhostfd < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
@@ -264,13 +281,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
- uint32_t i;
-
- for (i = 0; i < dev->max_queue_pairs * 2; ++i) {
- close(dev->callfds[i]);
- close(dev->kickfds[i]);
- }
-
close(dev->vhostfd);
}
diff --git a/drivers/net/virtio/virtio_user_ethdev.c b/drivers/net/virtio/virtio_user_ethdev.c
index 6c16d2d..298232a 100644
--- a/drivers/net/virtio/virtio_user_ethdev.c
+++ b/drivers/net/virtio/virtio_user_ethdev.c
@@ -87,21 +87,24 @@ virtio_user_write_dev_config(struct virtio_hw *hw, size_t offset,
}
static void
-virtio_user_set_status(struct virtio_hw *hw, uint8_t status)
+virtio_user_reset(struct virtio_hw *hw)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
- virtio_user_start_device(dev);
- dev->status = status;
+ if (dev->status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+ virtio_user_stop_device(dev);
}
static void
-virtio_user_reset(struct virtio_hw *hw)
+virtio_user_set_status(struct virtio_hw *hw, uint8_t status)
{
struct virtio_user_dev *dev = virtio_user_get_dev(hw);
- virtio_user_stop_device(dev);
+ if (status & VIRTIO_CONFIG_STATUS_DRIVER_OK)
+ virtio_user_start_device(dev);
+ else if (status == VIRTIO_CONFIG_STATUS_RESET)
+ virtio_user_reset(hw);
+ dev->status = status;
}
static uint8_t
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 3/8] net/virtio_user: move vhost user specific code
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 1/8] net/virtio_user: fix wrongly get/set features Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 2/8] net/virtio_user: fix not properly reset device Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 4/8] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
` (5 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
To support vhost kernel as the backend of net_virtio_user in coming
patches, we move vhost_user specific structs and macros into
vhost_user.c, and only keep common definitions in vhost.h.
Besides, remove VHOST_USER_MQ feature check.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 36 ------------------------
drivers/net/virtio/virtio_user/vhost_user.c | 32 +++++++++++++++++++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 9 ------
3 files changed, 32 insertions(+), 45 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 7adb55f..e54ac35 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -42,8 +42,6 @@
#include "../virtio_logs.h"
#include "../virtqueue.h"
-#define VHOST_MEMORY_MAX_NREGIONS 8
-
struct vhost_vring_state {
unsigned int index;
unsigned int num;
@@ -105,40 +103,6 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-struct vhost_memory {
- uint32_t nregions;
- uint32_t padding;
- struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
-};
-
-struct vhost_user_msg {
- enum vhost_user_request request;
-
-#define VHOST_USER_VERSION_MASK 0x3
-#define VHOST_USER_REPLY_MASK (0x1 << 2)
- uint32_t flags;
- uint32_t size; /* the following payload size */
- union {
-#define VHOST_USER_VRING_IDX_MASK 0xff
-#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
- uint64_t u64;
- struct vhost_vring_state state;
- struct vhost_vring_addr addr;
- struct vhost_memory memory;
- } payload;
- int fds[VHOST_MEMORY_MAX_NREGIONS];
-} __attribute((packed));
-
-#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
-#define VHOST_USER_PAYLOAD_SIZE \
- (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
-
-/* The version of the protocol we support */
-#define VHOST_USER_VERSION 0x1
-
-#define VHOST_USER_F_PROTOCOL_FEATURES 30
-#define VHOST_USER_MQ (1ULL << VHOST_USER_F_PROTOCOL_FEATURES)
-
int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
int vhost_user_setup(const char *path);
int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 082e821..295ce16 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -42,6 +42,38 @@
#include "vhost.h"
+/* The version of the protocol we support */
+#define VHOST_USER_VERSION 0x1
+
+#define VHOST_MEMORY_MAX_NREGIONS 8
+struct vhost_memory {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[VHOST_MEMORY_MAX_NREGIONS];
+};
+
+struct vhost_user_msg {
+ enum vhost_user_request request;
+
+#define VHOST_USER_VERSION_MASK 0x3
+#define VHOST_USER_REPLY_MASK (0x1 << 2)
+ uint32_t flags;
+ uint32_t size; /* the following payload size */
+ union {
+#define VHOST_USER_VRING_IDX_MASK 0xff
+#define VHOST_USER_VRING_NOFD_MASK (0x1 << 8)
+ uint64_t u64;
+ struct vhost_vring_state state;
+ struct vhost_vring_addr addr;
+ struct vhost_memory memory;
+ } payload;
+ int fds[VHOST_MEMORY_MAX_NREGIONS];
+} __attribute((packed));
+
+#define VHOST_USER_HDR_SIZE offsetof(struct vhost_user_msg, payload.u64)
+#define VHOST_USER_PAYLOAD_SIZE \
+ (sizeof(struct vhost_user_msg) - VHOST_USER_HDR_SIZE)
+
static int
vhost_user_write(int fd, void *buf, int len, int *fds, int fd_num)
{
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index a38398b..8dd563a 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -151,8 +151,6 @@ virtio_user_start_device(struct virtio_user_dev *dev)
* VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
*/
features = dev->features;
- if (dev->max_queue_pairs > 1)
- features |= VHOST_USER_MQ;
features &= ~(1ull << VIRTIO_NET_F_MAC);
features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
@@ -268,13 +266,6 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
dev->device_features &= ~(1ull << VIRTIO_NET_F_CTRL_MAC_ADDR);
}
- if (dev->max_queue_pairs > 1) {
- if (!(dev->features & VHOST_USER_MQ)) {
- PMD_INIT_LOG(ERR, "MQ not supported by the backend");
- return -1;
- }
- }
-
return 0;
}
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 4/8] net/virtio_user: abstract virtio user backend ops
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (2 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 3/8] net/virtio_user: move vhost user specific code Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 5/8] net/virtio_user: add vhost kernel support Jianfeng Tan
` (4 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Add a struct virtio_user_backend_ops to abstract three kinds of backend
operations:
- setup, create the unix socket connection;
- send_request, sync messages with backend;
- enable_qp, enable some queue pair.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost.h | 17 +++++-
drivers/net/virtio/virtio_user/vhost_user.c | 66 +++++++++++---------
drivers/net/virtio/virtio_user/virtio_user_dev.c | 77 +++++++++++++++---------
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 ++
4 files changed, 106 insertions(+), 59 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index e54ac35..515e4fc 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -96,6 +96,8 @@ enum vhost_user_request {
VHOST_USER_MAX
};
+const char * const vhost_msg_strings[VHOST_USER_MAX];
+
struct vhost_memory_region {
uint64_t guest_phys_addr;
uint64_t memory_size; /* bytes */
@@ -103,8 +105,17 @@ struct vhost_memory_region {
uint64_t mmap_offset;
};
-int vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg);
-int vhost_user_setup(const char *path);
-int vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable);
+struct virtio_user_dev;
+
+struct virtio_user_backend_ops {
+ int (*setup)(struct virtio_user_dev *dev);
+ int (*send_request)(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg);
+ int (*enable_qp)(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable);
+};
+struct virtio_user_backend_ops ops_user;
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c b/drivers/net/virtio/virtio_user/vhost_user.c
index 295ce16..4ad7b21 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -41,6 +41,7 @@
#include <errno.h>
#include "vhost.h"
+#include "virtio_user_dev.h"
/* The version of the protocol we support */
#define VHOST_USER_VERSION 0x1
@@ -255,24 +256,25 @@ prepare_vhost_memory_user(struct vhost_user_msg *msg, int fds[])
static struct vhost_user_msg m;
-static const char * const vhost_msg_strings[] = {
- [VHOST_USER_SET_OWNER] = "VHOST_USER_SET_OWNER",
- [VHOST_USER_RESET_OWNER] = "VHOST_USER_RESET_OWNER",
- [VHOST_USER_SET_FEATURES] = "VHOST_USER_SET_FEATURES",
- [VHOST_USER_GET_FEATURES] = "VHOST_USER_GET_FEATURES",
- [VHOST_USER_SET_VRING_CALL] = "VHOST_USER_SET_VRING_CALL",
- [VHOST_USER_SET_VRING_NUM] = "VHOST_USER_SET_VRING_NUM",
- [VHOST_USER_SET_VRING_BASE] = "VHOST_USER_SET_VRING_BASE",
- [VHOST_USER_GET_VRING_BASE] = "VHOST_USER_GET_VRING_BASE",
- [VHOST_USER_SET_VRING_ADDR] = "VHOST_USER_SET_VRING_ADDR",
- [VHOST_USER_SET_VRING_KICK] = "VHOST_USER_SET_VRING_KICK",
- [VHOST_USER_SET_MEM_TABLE] = "VHOST_USER_SET_MEM_TABLE",
- [VHOST_USER_SET_VRING_ENABLE] = "VHOST_USER_SET_VRING_ENABLE",
- NULL,
+const char * const vhost_msg_strings[] = {
+ [VHOST_USER_SET_OWNER] = "VHOST_SET_OWNER",
+ [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",
+ [VHOST_USER_GET_VRING_BASE] = "VHOST_GET_VRING_BASE",
+ [VHOST_USER_SET_VRING_ADDR] = "VHOST_SET_VRING_ADDR",
+ [VHOST_USER_SET_VRING_KICK] = "VHOST_SET_VRING_KICK",
+ [VHOST_USER_SET_MEM_TABLE] = "VHOST_SET_MEM_TABLE",
+ [VHOST_USER_SET_VRING_ENABLE] = "VHOST_SET_VRING_ENABLE",
};
-int
-vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
+static int
+vhost_user_sock(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
{
struct vhost_user_msg msg;
struct vhost_vring_file *file = 0;
@@ -280,9 +282,9 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
int fds[VHOST_MEMORY_MAX_NREGIONS];
int fd_num = 0;
int i, len;
+ int vhostfd = dev->vhostfd;
RTE_SET_USED(m);
- RTE_SET_USED(vhost_msg_strings);
PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
@@ -403,15 +405,13 @@ vhost_user_sock(int vhostfd, enum vhost_user_request req, void *arg)
/**
* Set up environment to talk with a vhost user backend.
- * @param path
- * - The path to vhost user unix socket file.
*
* @return
- * - (-1) if fail to set up;
- * - (>=0) if successful, and it is the fd to vhostfd.
+ * - (-1) if fail;
+ * - (0) if succeed.
*/
-int
-vhost_user_setup(const char *path)
+static int
+vhost_user_setup(struct virtio_user_dev *dev)
{
int fd;
int flag;
@@ -429,18 +429,21 @@ vhost_user_setup(const char *path)
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
- snprintf(un.sun_path, sizeof(un.sun_path), "%s", path);
+ snprintf(un.sun_path, sizeof(un.sun_path), "%s", dev->path);
if (connect(fd, (struct sockaddr *)&un, sizeof(un)) < 0) {
PMD_DRV_LOG(ERR, "connect error, %s", strerror(errno));
close(fd);
return -1;
}
- return fd;
+ dev->vhostfd = fd;
+ return 0;
}
-int
-vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
+static int
+vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
{
int i;
@@ -450,10 +453,15 @@ vhost_user_enable_queue_pair(int vhostfd, uint16_t pair_idx, int enable)
.num = enable,
};
- if (vhost_user_sock(vhostfd,
- VHOST_USER_SET_VRING_ENABLE, &state))
+ if (vhost_user_sock(dev, VHOST_USER_SET_VRING_ENABLE, &state))
return -1;
}
return 0;
}
+
+struct virtio_user_backend_ops ops_user = {
+ .setup = vhost_user_setup,
+ .send_request = vhost_user_sock,
+ .enable_qp = vhost_user_enable_queue_pair
+};
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 8dd563a..32039a1 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -39,6 +39,9 @@
#include <sys/mman.h>
#include <unistd.h>
#include <sys/eventfd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
#include "vhost.h"
#include "virtio_user_dev.h"
@@ -64,7 +67,7 @@ virtio_user_create_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = callfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_CALL, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_CALL, &file);
dev->callfds[queue_sel] = callfd;
return 0;
@@ -88,12 +91,12 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.index = queue_sel;
state.num = vring->num;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_NUM, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
state.num = 0; /* no reservation */
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_BASE, &state);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_ADDR, &addr);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_ADDR, &addr);
/* Of all per virtqueue MSGs, make sure VHOST_USER_SET_VRING_KICK comes
* lastly because vhost depends on this msg to judge if
@@ -106,7 +109,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
}
file.index = queue_sel;
file.fd = kickfd;
- vhost_user_sock(dev->vhostfd, VHOST_USER_SET_VRING_KICK, &file);
+ dev->ops->send_request(dev, VHOST_USER_SET_VRING_KICK, &file);
dev->kickfds[queue_sel] = kickfd;
return 0;
@@ -146,20 +149,19 @@ virtio_user_start_device(struct virtio_user_dev *dev)
if (virtio_user_queue_setup(dev, virtio_user_create_queue) < 0)
goto error;
- /* Step 1: set features
- * Make sure VHOST_USER_F_PROTOCOL_FEATURES is added if mq is enabled,
- * VIRTIO_NET_F_MAC and VIRTIO_NET_F_CTRL_VQ is stripped.
- */
+ /* Step 1: set features */
features = dev->features;
+ /* Strip VIRTIO_NET_F_MAC, as MAC address is handled in vdev init */
features &= ~(1ull << VIRTIO_NET_F_MAC);
+ /* Strip VIRTIO_NET_F_CTRL_VQ, as devices do not really need to know */
features &= ~(1ull << VIRTIO_NET_F_CTRL_VQ);
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_FEATURES, &features);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_FEATURES, &features);
if (ret < 0)
goto error;
PMD_DRV_LOG(INFO, "set features: %" PRIx64, features);
/* Step 2: share memory regions */
- ret = vhost_user_sock(dev->vhostfd, VHOST_USER_SET_MEM_TABLE, NULL);
+ ret = dev->ops->send_request(dev, VHOST_USER_SET_MEM_TABLE, NULL);
if (ret < 0)
goto error;
@@ -170,7 +172,7 @@ virtio_user_start_device(struct virtio_user_dev *dev)
/* Step 4: enable queues
* we enable the 1st queue pair by default.
*/
- vhost_user_enable_queue_pair(dev->vhostfd, 0, 1);
+ dev->ops->enable_qp(dev, 0, 1);
return 0;
error:
@@ -188,7 +190,7 @@ int virtio_user_stop_device(struct virtio_user_dev *dev)
}
for (i = 0; i < dev->max_queue_pairs; ++i)
- vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ dev->ops->enable_qp(dev, i, 0);
return 0;
}
@@ -214,36 +216,57 @@ parse_mac(struct virtio_user_dev *dev, const char *mac)
}
}
+static int
+is_vhost_user_by_type(const char *path)
+{
+ struct stat sb;
+
+ if (stat(path, &sb) == -1)
+ return 0;
+
+ return S_ISSOCK(sb.st_mode);
+}
+
+static int
+virtio_user_dev_setup(struct virtio_user_dev *dev)
+{
+ uint32_t i;
+
+ dev->vhostfd = -1;
+ for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
+ dev->kickfds[i] = -1;
+ dev->callfds[i] = -1;
+ }
+
+ if (is_vhost_user_by_type(dev->path)) {
+ dev->ops = &ops_user;
+ return dev->ops->setup(dev);
+ }
+
+ return -1;
+}
+
int
virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
int cq, int queue_size, const char *mac)
{
- uint32_t i;
-
snprintf(dev->path, PATH_MAX, "%s", path);
dev->max_queue_pairs = queues;
dev->queue_pairs = 1; /* mq disabled by default */
dev->queue_size = queue_size;
dev->mac_specified = 0;
parse_mac(dev, mac);
- dev->vhostfd = -1;
-
- for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
- dev->kickfds[i] = -1;
- dev->callfds[i] = -1;
- }
- dev->vhostfd = vhost_user_setup(dev->path);
- if (dev->vhostfd < 0) {
+ if (virtio_user_dev_setup(dev) < 0) {
PMD_INIT_LOG(ERR, "backend set up fails");
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_SET_OWNER, NULL) < 0) {
+ if (dev->ops->send_request(dev, VHOST_USER_SET_OWNER, NULL) < 0) {
PMD_INIT_LOG(ERR, "set_owner fails: %s", strerror(errno));
return -1;
}
- if (vhost_user_sock(dev->vhostfd, VHOST_USER_GET_FEATURES,
+ if (dev->ops->send_request(dev, VHOST_USER_GET_FEATURES,
&dev->device_features) < 0) {
PMD_INIT_LOG(ERR, "get_features failed: %s", strerror(errno));
return -1;
@@ -288,9 +311,9 @@ virtio_user_handle_mq(struct virtio_user_dev *dev, uint16_t q_pairs)
}
for (i = 0; i < q_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 1);
+ ret |= dev->ops->enable_qp(dev, i, 1);
for (i = q_pairs; i < dev->max_queue_pairs; ++i)
- ret |= vhost_user_enable_queue_pair(dev->vhostfd, i, 0);
+ ret |= dev->ops->enable_qp(dev, i, 0);
dev->queue_pairs = q_pairs;
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 28fc788..9f2f82e 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -37,9 +37,13 @@
#include <limits.h>
#include "../virtio_pci.h"
#include "../virtio_ring.h"
+#include "vhost.h"
struct virtio_user_dev {
+ /* for vhost_user backend */
int vhostfd;
+
+ /* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int mac_specified;
@@ -54,6 +58,7 @@ struct virtio_user_dev {
uint8_t mac_addr[ETHER_ADDR_LEN];
char path[PATH_MAX];
struct vring vrings[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
+ struct virtio_user_backend_ops *ops;
};
int virtio_user_start_device(struct virtio_user_dev *dev);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 5/8] net/virtio_user: add vhost kernel support
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (3 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 4/8] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 6/8] net/virtio_user: enable offloading Jianfeng Tan
` (3 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
This patch add support vhost kernel as the backend for virtio_user.
Three main hook functions are added:
- vhost_kernel_setup() to open char device, each vq pair needs one
vhostfd;
- vhost_kernel_ioctl() to communicate control messages with vhost
kernel module;
- vhost_kernel_enable_queue_pair() to open tap device and set it
as the backend of corresonding vhost fd (that is to say, vq pair).
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
doc/guides/rel_notes/release_17_02.rst | 20 ++
drivers/net/virtio/Makefile | 2 +
drivers/net/virtio/virtio_user/vhost.h | 2 +
drivers/net/virtio/virtio_user/vhost_kernel.c | 311 ++++++++++++++++++++++
drivers/net/virtio/virtio_user/vhost_kernel_tap.c | 116 ++++++++
drivers/net/virtio/virtio_user/vhost_kernel_tap.h | 60 +++++
drivers/net/virtio/virtio_user/virtio_user_dev.c | 36 ++-
drivers/net/virtio/virtio_user/virtio_user_dev.h | 5 +
8 files changed, 549 insertions(+), 3 deletions(-)
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel.c
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel_tap.c
create mode 100644 drivers/net/virtio/virtio_user/vhost_kernel_tap.h
diff --git a/doc/guides/rel_notes/release_17_02.rst b/doc/guides/rel_notes/release_17_02.rst
index 180af82..7354df5 100644
--- a/doc/guides/rel_notes/release_17_02.rst
+++ b/doc/guides/rel_notes/release_17_02.rst
@@ -52,6 +52,26 @@ New Features
See the :ref:`Generic flow API <Generic_flow_API>` documentation for more
information.
+* **virtio_user with vhost-kernel as another exceptional path.**
+
+ Previously, we upstreamed a virtual device, virtio_user with vhost-user
+ as the backend, as a way for IPC (Inter-Process Communication) and user
+ space container networking.
+
+ Virtio_user with vhost-kernel as the backend is a solution for exceptional
+ path, such as KNI, which exchanges packets with kernel networking stack.
+ This solution is very promising in:
+
+ * maintenance: vhost and vhost-net (kernel) is upstreamed and extensively
+ used kernel module.
+ * features: vhost-net is born to be a networking solution, which has
+ lots of networking related featuers, like multi queue, tso, multi-seg
+ mbuf, etc.
+ * performance: similar to KNI, this solution would use one or more
+ kthreads to send/receive packets from user space DPDK applications,
+ which has little impact on user space polling thread (except that
+ it might enter into kernel space to wake up those kthreads if
+ necessary).
Resolved Issues
---------------
diff --git a/drivers/net/virtio/Makefile b/drivers/net/virtio/Makefile
index 97972a6..8b5b5d6 100644
--- a/drivers/net/virtio/Makefile
+++ b/drivers/net/virtio/Makefile
@@ -60,6 +60,8 @@ endif
ifeq ($(CONFIG_RTE_VIRTIO_USER),y)
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_user.c
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel.c
+SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/vhost_kernel_tap.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user/virtio_user_dev.c
SRCS-$(CONFIG_RTE_LIBRTE_VIRTIO_PMD) += virtio_user_ethdev.c
endif
diff --git a/drivers/net/virtio/virtio_user/vhost.h b/drivers/net/virtio/virtio_user/vhost.h
index 515e4fc..5c983bd 100644
--- a/drivers/net/virtio/virtio_user/vhost.h
+++ b/drivers/net/virtio/virtio_user/vhost.h
@@ -118,4 +118,6 @@ struct virtio_user_backend_ops {
};
struct virtio_user_backend_ops ops_user;
+struct virtio_user_backend_ops ops_kernel;
+
#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
new file mode 100644
index 0000000..9301f96
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -0,0 +1,311 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <rte_memory.h>
+#include <rte_eal_memconfig.h>
+
+#include "vhost.h"
+#include "virtio_user_dev.h"
+#include "vhost_kernel_tap.h"
+
+struct vhost_memory_kernel {
+ uint32_t nregions;
+ uint32_t padding;
+ struct vhost_memory_region regions[0];
+};
+
+/* vhost kernel ioctls */
+#define VHOST_VIRTIO 0xAF
+#define VHOST_GET_FEATURES _IOR(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_FEATURES _IOW(VHOST_VIRTIO, 0x00, __u64)
+#define VHOST_SET_OWNER _IO(VHOST_VIRTIO, 0x01)
+#define VHOST_RESET_OWNER _IO(VHOST_VIRTIO, 0x02)
+#define VHOST_SET_MEM_TABLE _IOW(VHOST_VIRTIO, 0x03, struct vhost_memory_kernel)
+#define VHOST_SET_LOG_BASE _IOW(VHOST_VIRTIO, 0x04, __u64)
+#define VHOST_SET_LOG_FD _IOW(VHOST_VIRTIO, 0x07, int)
+#define VHOST_SET_VRING_NUM _IOW(VHOST_VIRTIO, 0x10, struct vhost_vring_state)
+#define VHOST_SET_VRING_ADDR _IOW(VHOST_VIRTIO, 0x11, struct vhost_vring_addr)
+#define VHOST_SET_VRING_BASE _IOW(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_GET_VRING_BASE _IOWR(VHOST_VIRTIO, 0x12, struct vhost_vring_state)
+#define VHOST_SET_VRING_KICK _IOW(VHOST_VIRTIO, 0x20, struct vhost_vring_file)
+#define VHOST_SET_VRING_CALL _IOW(VHOST_VIRTIO, 0x21, struct vhost_vring_file)
+#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)
+
+static uint64_t max_regions = 64;
+
+static void
+get_vhost_kernel_max_regions(void)
+{
+ int fd;
+ char buf[20] = {'\0'};
+
+ fd = open("/sys/module/vhost/parameters/max_mem_regions", O_RDONLY);
+ if (fd < 0)
+ return;
+
+ if (read(fd, buf, 20-1) > 0)
+ max_regions = strtoull(buf, NULL, 10);
+
+ close(fd);
+}
+
+static uint64_t vhost_req_user_to_kernel[] = {
+ [VHOST_USER_SET_OWNER] = VHOST_SET_OWNER,
+ [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,
+ [VHOST_USER_GET_VRING_BASE] = VHOST_GET_VRING_BASE,
+ [VHOST_USER_SET_VRING_ADDR] = VHOST_SET_VRING_ADDR,
+ [VHOST_USER_SET_VRING_KICK] = VHOST_SET_VRING_KICK,
+ [VHOST_USER_SET_MEM_TABLE] = VHOST_SET_MEM_TABLE,
+};
+
+/* By default, vhost kernel module allows 64 regions, but DPDK allows
+ * 256 segments. As a relief, below function merges those virtually
+ * adjacent memsegs into one region.
+ */
+static struct vhost_memory_kernel *
+prepare_vhost_memory_kernel(void)
+{
+ uint32_t i, j, k = 0;
+ struct rte_memseg *seg;
+ struct vhost_memory_region *mr;
+ struct vhost_memory_kernel *vm;
+
+ vm = malloc(sizeof(struct vhost_memory_kernel) +
+ max_regions *
+ sizeof(struct vhost_memory_region));
+
+ for (i = 0; i < RTE_MAX_MEMSEG; ++i) {
+ seg = &rte_eal_get_configuration()->mem_config->memseg[i];
+ if (!seg->addr)
+ break;
+
+ int new_region = 1;
+
+ for (j = 0; j < k; ++j) {
+ mr = &vm->regions[j];
+
+ if (mr->userspace_addr + mr->memory_size ==
+ (uint64_t)(uintptr_t)seg->addr) {
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+
+ if ((uint64_t)(uintptr_t)seg->addr + seg->len ==
+ mr->userspace_addr) {
+ mr->guest_phys_addr =
+ (uint64_t)(uintptr_t)seg->addr;
+ mr->userspace_addr =
+ (uint64_t)(uintptr_t)seg->addr;
+ mr->memory_size += seg->len;
+ new_region = 0;
+ break;
+ }
+ }
+
+ if (new_region == 0)
+ continue;
+
+ mr = &vm->regions[k++];
+ /* use vaddr here! */
+ mr->guest_phys_addr = (uint64_t)(uintptr_t)seg->addr;
+ mr->userspace_addr = (uint64_t)(uintptr_t)seg->addr;
+ mr->memory_size = seg->len;
+ mr->mmap_offset = 0;
+
+ if (k >= max_regions) {
+ free(vm);
+ return NULL;
+ }
+ }
+
+ vm->nregions = k;
+ vm->padding = 0;
+ return vm;
+}
+
+static int
+vhost_kernel_ioctl(struct virtio_user_dev *dev,
+ enum vhost_user_request req,
+ void *arg)
+{
+ int ret = -1;
+ unsigned int i;
+ uint64_t req_kernel;
+ struct vhost_memory_kernel *vm = NULL;
+
+ PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
+
+ req_kernel = vhost_req_user_to_kernel[req];
+
+ if (req_kernel == VHOST_SET_MEM_TABLE) {
+ vm = prepare_vhost_memory_kernel();
+ if (!vm)
+ return -1;
+ arg = (void *)vm;
+ }
+
+ /* We don't need memory protection here */
+ if (req_kernel == VHOST_SET_FEATURES)
+ *(uint64_t *)arg &= ~(1ULL << VIRTIO_F_IOMMU_PLATFORM);
+
+ for (i = 0; i < dev->max_queue_pairs; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
+
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+
+ if (vm)
+ free(vm);
+
+ if (ret < 0)
+ PMD_DRV_LOG(ERR, "%s failed: %s",
+ vhost_msg_strings[req], strerror(errno));
+
+ return ret;
+}
+
+/**
+ * Set up environment to talk with a vhost kernel backend.
+ *
+ * @return
+ * - (-1) if fail to set up;
+ * - (>=0) if successful.
+ */
+static int
+vhost_kernel_setup(struct virtio_user_dev *dev)
+{
+ int vhostfd;
+ uint32_t i;
+
+ get_vhost_kernel_max_regions();
+
+ for (i = 0; i < dev->max_queue_pairs; ++i) {
+ vhostfd = open(dev->path, O_RDWR);
+ if (vhostfd < 0) {
+ PMD_DRV_LOG(ERR, "fail to open %s, %s",
+ dev->path, strerror(errno));
+ return -1;
+ }
+
+ dev->vhostfds[i] = vhostfd;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_set_backend(int vhostfd, int tapfd)
+{
+ struct vhost_vring_file f;
+
+ f.fd = tapfd;
+ f.index = 0;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ f.index = 1;
+ if (ioctl(vhostfd, VHOST_NET_SET_BACKEND, &f) < 0) {
+ PMD_DRV_LOG(ERR, "VHOST_NET_SET_BACKEND fails, %s",
+ strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int
+vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
+ uint16_t pair_idx,
+ int enable)
+{
+ int hdr_size;
+ int vhostfd;
+ int tapfd;
+
+ vhostfd = dev->vhostfds[pair_idx];
+
+ if (!enable) {
+ if (dev->tapfds[pair_idx]) {
+ close(dev->tapfds[pair_idx]);
+ dev->tapfds[pair_idx] = -1;
+ }
+ return vhost_kernel_set_backend(vhostfd, -1);
+ } else if (dev->tapfds[pair_idx] >= 0) {
+ return 0;
+ }
+
+ if ((dev->features & (1ULL << VIRTIO_NET_F_MRG_RXBUF)) ||
+ (dev->features & (1ULL << VIRTIO_F_VERSION_1)))
+ hdr_size = sizeof(struct virtio_net_hdr_mrg_rxbuf);
+ else
+ hdr_size = sizeof(struct virtio_net_hdr);
+
+ tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size);
+ if (tapfd < 0) {
+ PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel");
+ return -1;
+ }
+
+ if (vhost_kernel_set_backend(vhostfd, tapfd) < 0) {
+ PMD_DRV_LOG(ERR, "fail to set backend for vhost kernel");
+ close(tapfd);
+ return -1;
+ }
+
+ dev->tapfds[pair_idx] = tapfd;
+ return 0;
+}
+
+struct virtio_user_backend_ops ops_kernel = {
+ .setup = vhost_kernel_setup,
+ .send_request = vhost_kernel_ioctl,
+ .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
new file mode 100644
index 0000000..797713b
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
@@ -0,0 +1,116 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <net/if.h>
+#include <errno.h>
+#include <string.h>
+#include <limits.h>
+
+#include "vhost_kernel_tap.h"
+#include "../virtio_logs.h"
+
+int
+vhost_kernel_open_tap(char **p_ifname, int hdr_size)
+{
+ unsigned int tap_features;
+ int sndbuf = INT_MAX;
+ struct ifreq ifr;
+ int tapfd;
+
+ /* TODO:
+ * 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
+ * 2. get number of memory regions from vhost module parameter
+ * max_mem_regions, supported in newer version linux kernel
+ */
+ 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;
+ }
+
+ /* Construct ifr */
+ memset(&ifr, 0, sizeof(ifr));
+ ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
+
+ if (ioctl(tapfd, TUNGETFEATURES, &tap_features) == -1) {
+ PMD_DRV_LOG(ERR, "TUNGETFEATURES failed: %s", strerror(errno));
+ goto error;
+ }
+ if (tap_features & IFF_ONE_QUEUE)
+ ifr.ifr_flags |= IFF_ONE_QUEUE;
+
+ /* Let tap instead of vhost-net handle vnet header, as the latter does
+ * not support offloading. And in this case, we should not set feature
+ * bit VHOST_NET_F_VIRTIO_NET_HDR.
+ */
+ if (tap_features & IFF_VNET_HDR) {
+ ifr.ifr_flags |= IFF_VNET_HDR;
+ } else {
+ PMD_DRV_LOG(ERR, "TAP does not support IFF_VNET_HDR");
+ goto error;
+ }
+
+ if (*p_ifname)
+ strncpy(ifr.ifr_name, *p_ifname, IFNAMSIZ);
+ else
+ strncpy(ifr.ifr_name, "tap%d", IFNAMSIZ);
+ if (ioctl(tapfd, TUNSETIFF, (void *)&ifr) == -1) {
+ PMD_DRV_LOG(ERR, "TUNSETIFF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ fcntl(tapfd, F_SETFL, O_NONBLOCK);
+
+ if (ioctl(tapfd, TUNSETVNETHDRSZ, &hdr_size) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETVNETHDRSZ failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (ioctl(tapfd, TUNSETSNDBUF, &sndbuf) < 0) {
+ PMD_DRV_LOG(ERR, "TUNSETSNDBUF failed: %s", strerror(errno));
+ goto error;
+ }
+
+ if (!(*p_ifname))
+ *p_ifname = strdup(ifr.ifr_name);
+
+ return tapfd;
+error:
+ close(tapfd);
+ return -1;
+}
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
new file mode 100644
index 0000000..1fc58ff
--- /dev/null
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
@@ -0,0 +1,60 @@
+/*-
+ * BSD LICENSE
+ *
+ * Copyright(c) 2016 Intel Corporation. All rights reserved.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Intel Corporation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/ioctl.h>
+
+/* TUN ioctls */
+#define TUNSETIFF _IOW('T', 202, int)
+#define TUNGETFEATURES _IOR('T', 207, unsigned int)
+#define TUNSETOFFLOAD _IOW('T', 208, unsigned int)
+#define TUNGETIFF _IOR('T', 210, unsigned int)
+#define TUNSETSNDBUF _IOW('T', 212, int)
+#define TUNGETVNETHDRSZ _IOR('T', 215, int)
+#define TUNSETVNETHDRSZ _IOW('T', 216, int)
+#define TUNSETQUEUE _IOW('T', 217, int)
+#define TUNSETVNETLE _IOW('T', 220, int)
+#define TUNSETVNETBE _IOW('T', 222, int)
+
+/* TUNSETIFF ifr flags */
+#define IFF_TAP 0x0002
+#define IFF_NO_PI 0x1000
+#define IFF_ONE_QUEUE 0x2000
+#define IFF_VNET_HDR 0x4000
+#define IFF_MULTI_QUEUE 0x0100
+#define IFF_ATTACH_QUEUE 0x0200
+#define IFF_DETACH_QUEUE 0x0400
+
+/* Constants */
+#define PATH_NET_TUN "/dev/net/tun"
+
+int vhost_kernel_open_tap(char **p_ifname, int hdr_size);
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index 32039a1..ae0824c 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -192,6 +192,9 @@ int virtio_user_stop_device(struct virtio_user_dev *dev)
for (i = 0; i < dev->max_queue_pairs; ++i)
dev->ops->enable_qp(dev, i, 0);
+ free(dev->ifname);
+ dev->ifname = NULL;
+
return 0;
}
@@ -230,7 +233,7 @@ is_vhost_user_by_type(const char *path)
static int
virtio_user_dev_setup(struct virtio_user_dev *dev)
{
- uint32_t i;
+ uint32_t i, q;
dev->vhostfd = -1;
for (i = 0; i < VIRTIO_MAX_VIRTQUEUES * 2 + 1; ++i) {
@@ -238,12 +241,28 @@ virtio_user_dev_setup(struct virtio_user_dev *dev)
dev->callfds[i] = -1;
}
+ dev->vhostfds = NULL;
+ dev->tapfds = NULL;
+
if (is_vhost_user_by_type(dev->path)) {
dev->ops = &ops_user;
- return dev->ops->setup(dev);
+ } else {
+ dev->ops = &ops_kernel;
+
+ dev->vhostfds = malloc(dev->max_queue_pairs * sizeof(int));
+ dev->tapfds = malloc(dev->max_queue_pairs * sizeof(int));
+ if (!dev->vhostfds || !dev->tapfds) {
+ PMD_INIT_LOG(ERR, "Failed to malloc");
+ return -1;
+ }
+
+ for (q = 0; q < dev->max_queue_pairs; ++q) {
+ dev->vhostfds[q] = -1;
+ dev->tapfds[q] = -1;
+ }
}
- return -1;
+ return dev->ops->setup(dev);
}
int
@@ -295,7 +314,18 @@ virtio_user_dev_init(struct virtio_user_dev *dev, char *path, int queues,
void
virtio_user_dev_uninit(struct virtio_user_dev *dev)
{
+ uint32_t i;
+
+ virtio_user_stop_device(dev);
+
close(dev->vhostfd);
+
+ if (dev->vhostfds) {
+ for (i = 0; i < dev->max_queue_pairs; ++i)
+ close(dev->vhostfds[i]);
+ free(dev->vhostfds);
+ free(dev->tapfds);
+ }
}
static uint8_t
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index 9f2f82e..0d39f40 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -43,6 +43,11 @@ struct virtio_user_dev {
/* for vhost_user backend */
int vhostfd;
+ /* for vhost_kernel backend */
+ char *ifname;
+ int *vhostfds;
+ int *tapfds;
+
/* for both vhost_user and vhost_kernel */
int callfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
int kickfds[VIRTIO_MAX_VIRTQUEUES * 2 + 1];
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 6/8] net/virtio_user: enable offloading
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (4 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 5/8] net/virtio_user: add vhost kernel support Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 7/8] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
` (2 subsequent siblings)
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
When used with vhost kernel backend, we can offload at both directions.
- From vhost kernel to virtio_user, the offload is enabled so that
DPDK app can trust the flow is checksum-correct; and if DPDK app
sends it through another port, the checksum needs to be
recalculated or offloaded. It also applies to TSO.
- From virtio_user to vhost_kernel, the offload is enabled so that
kernel can trust the flow is L4-checksum-correct, no need to verify
it; if kernel will consume it, DPDK app should make sure the
l3-checksum is correctly set.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 40 +++++++++++++++++++++--
drivers/net/virtio/virtio_user/vhost_kernel_tap.c | 14 ++++++++
drivers/net/virtio/virtio_user/vhost_kernel_tap.h | 7 ++++
3 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index 9301f96..a5d3e1e 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -165,6 +165,28 @@ 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 int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
@@ -186,10 +208,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
arg = (void *)vm;
}
- /* We don't need memory protection here */
- if (req_kernel == VHOST_SET_FEATURES)
+ 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;
+ }
+
for (i = 0; i < dev->max_queue_pairs; ++i) {
if (dev->vhostfds[i] < 0)
continue;
@@ -199,6 +226,15 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
break;
}
+ if (!ret && req_kernel == VHOST_GET_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.
+ */
+ *((uint64_t *)arg) |= VHOST_KERNEL_GUEST_OFFLOADS_MASK;
+ *((uint64_t *)arg) |= VHOST_KERNEL_HOST_OFFLOADS_MASK;
+ }
+
if (vm)
free(vm);
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
index 797713b..cdb5c3c 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
@@ -50,6 +50,12 @@ vhost_kernel_open_tap(char **p_ifname, int hdr_size)
int sndbuf = INT_MAX;
struct ifreq ifr;
int tapfd;
+ unsigned int offload =
+ TUN_F_CSUM |
+ TUN_F_TSO4 |
+ TUN_F_TSO6 |
+ TUN_F_TSO_ECN |
+ TUN_F_UFO;
/* TODO:
* 1. verify we can get/set vnet_hdr_len, tap_probe_vnet_hdr_len
@@ -106,6 +112,14 @@ vhost_kernel_open_tap(char **p_ifname, int hdr_size)
goto error;
}
+ /* TODO: before set the offload capabilities, we'd better (1) check
+ * negotiated features to see if necessary to offload; (2) query tap
+ * to see if it supports the offload capabilities.
+ */
+ if (ioctl(tapfd, TUNSETOFFLOAD, offload) != 0)
+ PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s",
+ strerror(errno));
+
if (!(*p_ifname))
*p_ifname = strdup(ifr.ifr_name);
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
index 1fc58ff..8535116 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
@@ -54,6 +54,13 @@
#define IFF_ATTACH_QUEUE 0x0200
#define IFF_DETACH_QUEUE 0x0400
+/* Features for GSO (TUNSETOFFLOAD). */
+#define TUN_F_CSUM 0x01 /* You can hand me unchecksummed packets. */
+#define TUN_F_TSO4 0x02 /* I can handle TSO for IPv4 packets */
+#define TUN_F_TSO6 0x04 /* I can handle TSO for IPv6 packets */
+#define TUN_F_TSO_ECN 0x08 /* I can handle TSO with ECN bits. */
+#define TUN_F_UFO 0x10 /* I can handle UFO packets */
+
/* Constants */
#define PATH_NET_TUN "/dev/net/tun"
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 7/8] net/virtio_user: enable multiqueue with vhost kernel
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (5 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 6/8] net/virtio_user: enable offloading Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path Jianfeng Tan
2017-01-16 15:05 ` [dpdk-dev] [PATCH v4 0/8] virtio_user as an alternative exception path Yuanhan Liu
8 siblings, 0 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
With vhost kernel, to enable multiqueue, we need backend device
in kernel support multiqueue feature. Specifically, with tap
as the backend, as linux/Documentation/networking/tuntap.txt shows,
we check if tap supports IFF_MULTI_QUEUE feature.
And for vhost kernel, each queue pair has a vhost fd, and with a tap
fd binding this vhost fd. All tap fds are set with the same tap
interface name.
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
drivers/net/virtio/virtio_user/vhost_kernel.c | 68 ++++++++++++++++++++---
drivers/net/virtio/virtio_user/vhost_kernel_tap.c | 5 +-
drivers/net/virtio/virtio_user/vhost_kernel_tap.h | 2 +-
drivers/net/virtio/virtio_user/virtio_user_dev.c | 1 +
4 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c b/drivers/net/virtio/virtio_user/vhost_kernel.c
index a5d3e1e..0e45dd6 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -188,6 +188,29 @@ prepare_vhost_memory_kernel(void)
(1ULL << VIRTIO_NET_F_CSUM))
static int
+tap_supporte_mq(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 & IFF_MULTI_QUEUE;
+}
+
+static int
vhost_kernel_ioctl(struct virtio_user_dev *dev,
enum vhost_user_request req,
void *arg)
@@ -196,6 +219,8 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
unsigned int i;
uint64_t req_kernel;
struct vhost_memory_kernel *vm = NULL;
+ int vhostfd;
+ unsigned int queue_sel;
PMD_DRV_LOG(INFO, "%s", vhost_msg_strings[req]);
@@ -215,15 +240,37 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
/* 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);
}
- for (i = 0; i < dev->max_queue_pairs; ++i) {
- if (dev->vhostfds[i] < 0)
- continue;
+ switch (req_kernel) {
+ case VHOST_SET_VRING_NUM:
+ case VHOST_SET_VRING_ADDR:
+ case VHOST_SET_VRING_BASE:
+ case VHOST_GET_VRING_BASE:
+ case VHOST_SET_VRING_KICK:
+ case VHOST_SET_VRING_CALL:
+ queue_sel = *(unsigned int *)arg;
+ vhostfd = dev->vhostfds[queue_sel / 2];
+ *(unsigned int *)arg = queue_sel % 2;
+ PMD_DRV_LOG(DEBUG, "vhostfd=%d, index=%u",
+ vhostfd, *(unsigned int *)arg);
+ break;
+ default:
+ vhostfd = -1;
+ }
+ if (vhostfd == -1) {
+ for (i = 0; i < dev->max_queue_pairs; ++i) {
+ if (dev->vhostfds[i] < 0)
+ continue;
- ret = ioctl(dev->vhostfds[i], req_kernel, arg);
- if (ret < 0)
- break;
+ ret = ioctl(dev->vhostfds[i], req_kernel, arg);
+ if (ret < 0)
+ break;
+ }
+ } else {
+ ret = ioctl(vhostfd, req_kernel, arg);
}
if (!ret && req_kernel == VHOST_GET_FEATURES) {
@@ -233,6 +280,12 @@ vhost_kernel_ioctl(struct virtio_user_dev *dev,
*/
*((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 (tap_supporte_mq())
+ *(uint64_t *)arg |= (1ull << VIRTIO_NET_F_MQ);
}
if (vm)
@@ -305,6 +358,7 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
int hdr_size;
int vhostfd;
int tapfd;
+ int req_mq = (dev->max_queue_pairs > 1);
vhostfd = dev->vhostfds[pair_idx];
@@ -324,7 +378,7 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev *dev,
else
hdr_size = sizeof(struct virtio_net_hdr);
- tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size);
+ tapfd = vhost_kernel_open_tap(&dev->ifname, hdr_size, req_mq);
if (tapfd < 0) {
PMD_DRV_LOG(ERR, "fail to open tap for vhost kernel");
return -1;
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
index cdb5c3c..f585de8 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
@@ -44,7 +44,7 @@
#include "../virtio_logs.h"
int
-vhost_kernel_open_tap(char **p_ifname, int hdr_size)
+vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq)
{
unsigned int tap_features;
int sndbuf = INT_MAX;
@@ -91,6 +91,9 @@ vhost_kernel_open_tap(char **p_ifname, int hdr_size)
goto error;
}
+ if (req_mq)
+ ifr.ifr_flags |= IFF_MULTI_QUEUE;
+
if (*p_ifname)
strncpy(ifr.ifr_name, *p_ifname, IFNAMSIZ);
else
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
index 8535116..eae340c 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
@@ -64,4 +64,4 @@
/* Constants */
#define PATH_NET_TUN "/dev/net/tun"
-int vhost_kernel_open_tap(char **p_ifname, int hdr_size);
+int vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq);
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index ae0824c..6617bc8 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -93,6 +93,7 @@ virtio_user_kick_queue(struct virtio_user_dev *dev, uint32_t queue_sel)
state.num = vring->num;
dev->ops->send_request(dev, VHOST_USER_SET_VRING_NUM, &state);
+ state.index = queue_sel;
state.num = 0; /* no reservation */
dev->ops->send_request(dev, VHOST_USER_SET_VRING_BASE, &state);
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (6 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 7/8] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
@ 2017-01-13 12:18 ` Jianfeng Tan
2017-01-16 6:00 ` Yuanhan Liu
2017-01-22 0:46 ` Aws Ismail
2017-01-16 15:05 ` [dpdk-dev] [PATCH v4 0/8] virtio_user as an alternative exception path Yuanhan Liu
8 siblings, 2 replies; 72+ messages in thread
From: Jianfeng Tan @ 2017-01-13 12:18 UTC (permalink / raw)
To: dev; +Cc: yuanhan.liu, ferruh.yigit, cunming.liang, Jianfeng Tan
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
.../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
doc/guides/prog_guide/index.rst | 1 +
.../prog_guide/virtio_user_as_exceptional_path.rst | 104 +++++++++++++++++++++
3 files changed, 105 insertions(+)
create mode 100644 doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
create mode 100644 doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
diff --git a/doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png b/doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
new file mode 100644
index 0000000000000000000000000000000000000000..9fd45e13eefc2ef39ac2c389f407d60ab4144c59
GIT binary patch
literal 38600
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
literal 0
HcmV?d00001
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.rst
index ed7f770..008a43b 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -55,6 +55,7 @@ Programmer's Guide
pdump_lib
multi_proc_support
kernel_nic_interface
+ virtio_user_as_exception_path
thread_safety_dpdk_functions
qos_framework
power_man
diff --git a/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst b/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
new file mode 100644
index 0000000..5cfeb10
--- /dev/null
+++ b/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
@@ -0,0 +1,104 @@
+.. BSD LICENSE
+ Copyright(c) 2016 Intel Corporation. All rights reserved.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+.. _virtio_user:
+
+Virtio_user as Exceptional Path
+===============================
+
+The virtual device, virtio_user, was originally introduced with vhost-user backend, as a high performance solution for IPC (Inter-Process Communication) and user space container networking.
+
+Virtio_user with vhost-kernel backend is a solution for exceptional path, such as KNI which exchanges packets with kernel networking stack. This solution is very promising in:
+
+* Maintenance: vhost and vhost-net (kernel) is upstreamed and extensively used kernel module.
+
+* Features: vhost-net is born to be a networking solution, which has lots of networking related featuers, like multi queue, tso, multi-seg mbuf, etc.
+
+* Performance: similar to KNI, this solution would use one or more kthreads to send/receive packets from user space DPDK applications, which has little impact on user space polling thread (except that it might enter into kernel space to wake up those kthreads if necessary).
+
+The overview of an application using virtio_user as exceptional path is shown in :numref:`figure_virtio_user_as_exceptional_path`.
+
+.. _figure_virtio_user_as_exceptional_path:
+
+.. figure:: img/virtio_user_as_exceptional_path.*
+
+ Overview of a DPDK app using virtio_user as excpetional path
+
+
+Sample Usage
+------------
+
+As a prerequisite, the vhost/vhost-net kernel CONFIG should be chosen before compiling the kernel and those kernel modules should be inserted.
+
+#. Compile the DPDK and bind a physical NIC (for communicating with outside) to igb_uio/uio_pci_generic/vfio-pci.
+
+#. Run testpmd:
+
+ .. code-block:: console
+
+ examples/kni/build/app/kni -c -0xf0 -n 4 -- -p 0x3 -P --config="(0,4,6),(1,5,7)"
+ $(testpmd) -c 0xc -n 4 --vdev=virtio_user0,path=/dev/vhost-net,queue_size=1024 -- -i --txqflags=0x0 --disable-hw-vlan --enable-lro --crc-strip --enable-rx-cksum --rxd=1024 --txd=1024
+
+ This command runs testpmd with two ports, one physical NIC to communicate with outside, and one virtio_user to communicate with kernel.
+
+* ``--enable-lro`` This is used to negotiate VIRTIO_NET_F_GUEST_TSO4 and VIRTIO_NET_F_GUEST_TSO6 feature so that large packets from kernel can be transmitted DPDK application and further TSOed by physical NIC.
+
+* ``--enable-rx-cksum`` This is used to negotiate VIRTIO_NET_F_GUEST_CSUM so that packets from kernel can be deemed as valid Rx checksumed.
+
+* ``queue_size`` of virtio_user, 256 by default. To avoid shortage of descriptors, we can increase it to 1024.
+
+* ``queues`` of virtio_user to specify multi-queues. Each qeueue will be served by a kthread:
+
+ .. code-block:: console
+
+ $(testpmd) -c 0xc -n 4 --vdev=virtio_user0,path=/dev/vhost-net,queues=2,queue_size=1024 -- -i --txqflags=0x0 --disable-hw-vlan --enable-lro --crc-strip --enable-rx-cksum --txq=2 --rxq=2 --rxd=1024 --txd=1024
+
+#. Start testpmd:
+
+ .. code-block:: console
+
+ (testpmd) start
+
+#. Configure IP address and start tap:
+
+ .. code-block:: console
+
+ ifconfig tap0 1.1.1.1/24 up
+
+.. note::
+
+ The tap device will be named tap0, tap1, etc, by kernel.
+
+Then, all traffic from physical NIC can be forwarded into kernel stack, and all traffic on the tap0 can be sent out from physical NIC.
+
+Limitations
+-----------
+
+This solution is only available on Linux systems.
--
2.7.4
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path Jianfeng Tan
@ 2017-01-16 6:00 ` Yuanhan Liu
2017-01-16 6:04 ` Yuanhan Liu
2017-01-16 9:44 ` Thomas Monjalon
2017-01-22 0:46 ` Aws Ismail
1 sibling, 2 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-16 6:00 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Jan 13, 2017 at 12:18:41PM +0000, Jianfeng Tan wrote:
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> ---
> .../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
> doc/guides/prog_guide/index.rst | 1 +
> .../prog_guide/virtio_user_as_exceptional_path.rst | 104 +++++++++++++++++++++
> 3 files changed, 105 insertions(+)
> create mode 100644 doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
> create mode 100644 doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
Doc build fails:
$ make doc-guides-html
sphinx processing guides-html...
/home/yliu/dpdk/doc/guides/prog_guide/index.rst:34: WARNING: toctree contains reference to nonexisting document u'prog_guide/virtio_user_as_exception_path'
/home/yliu/dpdk/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst:: WARNING: document isn't included in any toctree
> +Virtio_user as Exceptional Path
> +===============================
> +
> +The virtual device, virtio_user, was originally introduced with vhost-user backend, as a high performance solution for IPC (Inter-Process Communication) and user space container networking.
Doc should also be well-wrapped, say not execeed 80 chars.
> +Sample Usage
> +------------
> +
> +As a prerequisite, the vhost/vhost-net kernel CONFIG should be chosen before compiling the kernel and those kernel modules should be inserted.
> +
> +#. Compile the DPDK and bind a physical NIC (for communicating with outside) to igb_uio/uio_pci_generic/vfio-pci.
> +
> +#. Run testpmd:
> +
> + .. code-block:: console
> +
> + examples/kni/build/app/kni -c -0xf0 -n 4 -- -p 0x3 -P --config="(0,4,6),(1,5,7)"
^^^
KNI?
Besides, it's more like a howto doc instead of prog guide to me, that
it's better to put it to doc/guides/howto?
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-16 6:00 ` Yuanhan Liu
@ 2017-01-16 6:04 ` Yuanhan Liu
2017-01-16 9:44 ` Thomas Monjalon
1 sibling, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-16 6:04 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang, Thomas Monjalon
>
> Besides, it's more like a howto doc instead of prog guide to me, that
> it's better to put it to doc/guides/howto?
Cc Thomas.
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-16 6:00 ` Yuanhan Liu
2017-01-16 6:04 ` Yuanhan Liu
@ 2017-01-16 9:44 ` Thomas Monjalon
2017-01-16 9:49 ` Yuanhan Liu
1 sibling, 1 reply; 72+ messages in thread
From: Thomas Monjalon @ 2017-01-16 9:44 UTC (permalink / raw)
To: Yuanhan Liu, Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
2017-01-16 14:00, Yuanhan Liu:
> On Fri, Jan 13, 2017 at 12:18:41PM +0000, Jianfeng Tan wrote:
> > Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> > ---
> > .../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
PNG is a binary format.
This repository targets hosting of sources.
Please try SVG.
> > doc/guides/prog_guide/index.rst | 1 +
> Besides, it's more like a howto doc instead of prog guide to me, that
> it's better to put it to doc/guides/howto?
Sure, the prog guide is for API usage.
You can consider the howto section.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-16 9:44 ` Thomas Monjalon
@ 2017-01-16 9:49 ` Yuanhan Liu
2017-01-16 14:45 ` Thomas Monjalon
0 siblings, 1 reply; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-16 9:49 UTC (permalink / raw)
To: Thomas Monjalon; +Cc: Jianfeng Tan, dev, ferruh.yigit, cunming.liang
On Mon, Jan 16, 2017 at 10:44:04AM +0100, Thomas Monjalon wrote:
> 2017-01-16 14:00, Yuanhan Liu:
> > On Fri, Jan 13, 2017 at 12:18:41PM +0000, Jianfeng Tan wrote:
> > > Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> > > ---
> > > .../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
>
> PNG is a binary format.
> This repository targets hosting of sources.
> Please try SVG.
Agreed. I think I saw the trend. But I don't think Jianfeng has the
time now. Maybe it's better if I apply all of them (except the doc)
now and let him dram the SVG later?
--yliu
>
> > > doc/guides/prog_guide/index.rst | 1 +
>
> > Besides, it's more like a howto doc instead of prog guide to me, that
> > it's better to put it to doc/guides/howto?
>
> Sure, the prog guide is for API usage.
> You can consider the howto section.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-16 9:49 ` Yuanhan Liu
@ 2017-01-16 14:45 ` Thomas Monjalon
0 siblings, 0 replies; 72+ messages in thread
From: Thomas Monjalon @ 2017-01-16 14:45 UTC (permalink / raw)
To: Yuanhan Liu; +Cc: Jianfeng Tan, dev, ferruh.yigit, cunming.liang
2017-01-16 17:49, Yuanhan Liu:
> On Mon, Jan 16, 2017 at 10:44:04AM +0100, Thomas Monjalon wrote:
> > 2017-01-16 14:00, Yuanhan Liu:
> > > On Fri, Jan 13, 2017 at 12:18:41PM +0000, Jianfeng Tan wrote:
> > > > Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
> > > > ---
> > > > .../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
> >
> > PNG is a binary format.
> > This repository targets hosting of sources.
> > Please try SVG.
>
> Agreed. I think I saw the trend. But I don't think Jianfeng has the
> time now. Maybe it's better if I apply all of them (except the doc)
> now and let him dram the SVG later?
Yes you can
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 0/8] virtio_user as an alternative exception path
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
` (7 preceding siblings ...)
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path Jianfeng Tan
@ 2017-01-16 15:05 ` Yuanhan Liu
8 siblings, 0 replies; 72+ messages in thread
From: Yuanhan Liu @ 2017-01-16 15:05 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang
On Fri, Jan 13, 2017 at 12:18:33PM +0000, Jianfeng Tan wrote:
> v4:
> - Fix a clang compiling error by removing "NULL" line in the definition
> of vhost_msg_strings. This error does not show up when it's defined
> as a static variable, so not necessary to fix it in stable branch.
> - Query kernel to get how many regions are supported, default 64 regions.
> - Set TUNSETSNDBUF to INT_MAX.
> - When get_features, unmask those backend-specific feature bits.
> - Remove VHOST_KERNEL_MAX_QUEUES (8) restriction, but due to another
> restriction by VIRTIO_MAX_VIRTQUEUES (8), we still cannot configure
> more than 8 queues.
> - Add a howto document.
Series (except the doc patch) applied to dpdk-next-virtio.
Thanks.
--yliu
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path Jianfeng Tan
2017-01-16 6:00 ` Yuanhan Liu
@ 2017-01-22 0:46 ` Aws Ismail
2017-01-22 7:16 ` Tan, Jianfeng
1 sibling, 1 reply; 72+ messages in thread
From: Aws Ismail @ 2017-01-22 0:46 UTC (permalink / raw)
To: Jianfeng Tan; +Cc: DPDK, cunming.liang, yuanhan.liu, Ferruh Yigit
On Jan 13, 2017 9:42 AM, "Jianfeng Tan" <jianfeng.tan@intel.com> wrote:
Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>
---
.../img/virtio_user_as_excpetional_path.png | Bin 0 -> 38600 bytes
doc/guides/prog_guide/index.rst | 1 +
.../prog_guide/virtio_user_as_exceptional_path.rst | 104
+++++++++++++++++++++
3 files changed, 105 insertions(+)
create mode 100644 doc/guides/prog_guide/img/virtio_user_as_excpetional_
path.png
create mode 100644 doc/guides/prog_guide/virtio_
user_as_exceptional_path.rst
diff --git a/doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
b/doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
new file mode 100644
index 0000000000000000000000000000000000000000..
9fd45e13eefc2ef39ac2c389f407d60ab4144c59
GIT binary patch
literal 38600
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
literal 0
HcmV?d00001
diff --git a/doc/guides/prog_guide/index.rst b/doc/guides/prog_guide/index.
rst
index ed7f770..008a43b 100644
--- a/doc/guides/prog_guide/index.rst
+++ b/doc/guides/prog_guide/index.rst
@@ -55,6 +55,7 @@ Programmer's Guide
pdump_lib
multi_proc_support
kernel_nic_interface
+ virtio_user_as_exception_path
thread_safety_dpdk_functions
qos_framework
power_man
diff --git a/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
b/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
new file mode 100644
index 0000000..5cfeb10
--- /dev/null
+++ b/doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
@@ -0,0 +1,104 @@
+.. BSD LICENSE
+ Copyright(c) 2016 Intel Corporation. All rights reserved.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in
+ the documentation and/or other materials provided with the
+ distribution.
+ * Neither the name of Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+.. _virtio_user:
+
+Virtio_user as Exceptional Path
+===============================
+
+The virtual device, virtio_user, was originally introduced with vhost-user
backend, as a high performance solution for IPC (Inter-Process
Communication) and user space container networking.
+
+Virtio_user with vhost-kernel backend is a solution for exceptional path,
such as KNI which exchanges packets with kernel networking stack. This
solution is very promising in:
+
+* Maintenance: vhost and vhost-net (kernel) is upstreamed and
extensively used kernel module.
+
+* Features: vhost-net is born to be a networking solution, which has
lots of networking related featuers, like multi queue, tso, multi-seg mbuf,
etc.
+
+* Performance: similar to KNI, this solution would use one or more
kthreads to send/receive packets from user space DPDK applications, which
has little impact on user space polling thread (except that it might enter
into kernel space to wake up those kthreads if necessary).
+
+The overview of an application using virtio_user as exceptional path is
shown in :numref:`figure_virtio_user_as_exceptional_path`.
+
+.. _figure_virtio_user_as_exceptional_path:
+
+.. figure:: img/virtio_user_as_exceptional_path.*
+
+ Overview of a DPDK app using virtio_user as excpetional path
+
+
+Sample Usage
+------------
+
+As a prerequisite, the vhost/vhost-net kernel CONFIG should be chosen
before compiling the kernel and those kernel modules should be inserted.
+
+#. Compile the DPDK and bind a physical NIC (for communicating with
outside) to igb_uio/uio_pci_generic/vfio-pci.
+
+#. Run testpmd:
+
+ .. code-block:: console
+
+ examples/kni/build/app/kni -c -0xf0 -n 4 -- -p 0x3 -P
--config="(0,4,6),(1,5,7)"
Is this correct? The section says "run testpmd" but the first thing you are
doing here is run the KNI example app.
Aws.
^ permalink raw reply [flat|nested] 72+ messages in thread
* Re: [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path
2017-01-22 0:46 ` Aws Ismail
@ 2017-01-22 7:16 ` Tan, Jianfeng
0 siblings, 0 replies; 72+ messages in thread
From: Tan, Jianfeng @ 2017-01-22 7:16 UTC (permalink / raw)
To: Aws Ismail; +Cc: DPDK, cunming.liang, yuanhan.liu, Ferruh Yigit
On 1/22/2017 8:46 AM, Aws Ismail wrote:
>
>
> On Jan 13, 2017 9:42 AM, "Jianfeng Tan" <jianfeng.tan@intel.com
> <mailto:jianfeng.tan@intel.com>> wrote:
>
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com
> <mailto:jianfeng.tan@intel.com>>
> ---
> .../img/virtio_user_as_excpetional_path.png | Bin 0 ->
> 38600 bytes
> doc/guides/prog_guide/index.rst | 1 +
> .../prog_guide/virtio_user_as_exceptional_path.rst | 104
> +++++++++++++++++++++
> 3 files changed, 105 insertions(+)
> create mode 100644
> doc/guides/prog_guide/img/virtio_user_as_excpetional_path.png
> create mode 100644
> doc/guides/prog_guide/virtio_user_as_exceptional_path.rst
>
> [...]
> +Sample Usage
> +------------
> +
> +As a prerequisite, the vhost/vhost-net kernel CONFIG should be
> chosen before compiling the kernel and those kernel modules should
> be inserted.
> +
> +#. Compile the DPDK and bind a physical NIC (for communicating
> with outside) to igb_uio/uio_pci_generic/vfio-pci.
> +
> +#. Run testpmd:
> +
> + .. code-block:: console
> +
> + examples/kni/build/app/kni -c -0xf0 -n 4 -- -p 0x3 -P
> --config="(0,4,6),(1,5,7)"
>
>
> Is this correct? The section says "run testpmd" but the first thing
> you are doing here is run the KNI example app.
Oops, forget to delete this line.
Thanks,
Jianfeng
^ permalink raw reply [flat|nested] 72+ messages in thread
end of thread, other threads:[~2017-01-22 7:17 UTC | newest]
Thread overview: 72+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-02 14:31 [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 1/3] net/virtio_user: add vhost layer Jianfeng Tan
2016-12-08 7:21 ` Yuanhan Liu
2016-12-02 14:31 ` [dpdk-dev] [PATCH 2/3] net/virtio_user: add vhost kernel support Jianfeng Tan
2016-12-02 14:31 ` [dpdk-dev] [PATCH 3/3] net/virtio_user: fix wrongly set features Jianfeng Tan
2016-12-08 8:32 ` Yuanhan Liu
2016-12-02 14:44 ` [dpdk-dev] [PATCH 0/3] virtio_user as an alternative exception path Thomas Monjalon
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 0/7] " Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 2/7] net/virtio_user: postpone DRIVER OK notification Jianfeng Tan
[not found] ` <20161226062719.GA19288@yliu-dev.sh.intel.com>
2016-12-26 6:55 ` Tan, Jianfeng
2016-12-26 8:02 ` Yuanhan Liu
2016-12-26 8:26 ` Tan, Jianfeng
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
2016-12-26 6:28 ` Yuanhan Liu
2016-12-26 6:58 ` Tan, Jianfeng
2016-12-26 7:57 ` Yuanhan Liu
2016-12-29 9:40 ` Tan, Jianfeng
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
2016-12-26 6:41 ` Yuanhan Liu
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
2016-12-26 7:44 ` Yuanhan Liu
2017-01-04 7:22 ` Tan, Jianfeng
2017-01-04 7:46 ` Yuanhan Liu
2017-01-09 4:51 ` Jason Wang
2017-01-09 4:39 ` Jason Wang
2017-01-10 6:11 ` Tan, Jianfeng
2017-01-10 8:46 ` Thomas Monjalon
2017-01-10 9:11 ` Tan, Jianfeng
2017-01-11 2:42 ` Jason Wang
2017-01-11 3:13 ` Tan, Jianfeng
2017-01-11 3:23 ` Jason Wang
2017-01-12 9:40 ` Tan, Jianfeng
2017-01-13 2:27 ` Jason Wang
2017-01-11 2:30 ` Tan, Jianfeng
2017-01-11 2:45 ` Jason Wang
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 6/7] net/virtio_user: enable offloading Jianfeng Tan
2016-12-26 7:53 ` Yuanhan Liu
2016-12-23 7:14 ` [dpdk-dev] [PATCH v2 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 1/7] net/virtio_user: fix wrongly set features Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 2/7] net/virtio_user: fix not properly reset device Jianfeng Tan
2017-01-04 5:46 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 3/7] net/virtio_user: move vhost user specific code Jianfeng Tan
2017-01-04 6:02 ` Yuanhan Liu
2017-01-04 6:46 ` Tan, Jianfeng
2017-01-04 7:08 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 4/7] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
2017-01-04 6:11 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 5/7] net/virtio_user: add vhost kernel support Jianfeng Tan
2017-01-04 6:13 ` Yuanhan Liu
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 6/7] net/virtio_user: enable offloading Jianfeng Tan
2017-01-04 3:59 ` [dpdk-dev] [PATCH v3 7/7] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
2017-01-09 14:06 ` [dpdk-dev] [PATCH v3 0/7] virtio_user as an alternative exception path Bruce Richardson
2017-01-10 8:46 ` Tan, Jianfeng
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 0/8] " Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 1/8] net/virtio_user: fix wrongly get/set features Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 2/8] net/virtio_user: fix not properly reset device Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 3/8] net/virtio_user: move vhost user specific code Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 4/8] net/virtio_user: abstract virtio user backend ops Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 5/8] net/virtio_user: add vhost kernel support Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 6/8] net/virtio_user: enable offloading Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 7/8] net/virtio_user: enable multiqueue with vhost kernel Jianfeng Tan
2017-01-13 12:18 ` [dpdk-dev] [PATCH v4 8/8] doc: add guide to use virtio_user as exceptional path Jianfeng Tan
2017-01-16 6:00 ` Yuanhan Liu
2017-01-16 6:04 ` Yuanhan Liu
2017-01-16 9:44 ` Thomas Monjalon
2017-01-16 9:49 ` Yuanhan Liu
2017-01-16 14:45 ` Thomas Monjalon
2017-01-22 0:46 ` Aws Ismail
2017-01-22 7:16 ` Tan, Jianfeng
2017-01-16 15:05 ` [dpdk-dev] [PATCH v4 0/8] virtio_user as an alternative exception path Yuanhan Liu
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).