net/virtio-user: enable multiqueue with kernel vhost
authorJianfeng Tan <jianfeng.tan@intel.com>
Fri, 13 Jan 2017 12:18:40 +0000 (12:18 +0000)
committerYuanhan Liu <yuanhan.liu@linux.intel.com>
Tue, 17 Jan 2017 08:24:56 +0000 (09:24 +0100)
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>
Acked-by: Yuanhan Liu <yuanhan.liu@linux.intel.com>
drivers/net/virtio/virtio_user/vhost_kernel.c
drivers/net/virtio/virtio_user/vhost_kernel_tap.c
drivers/net/virtio/virtio_user/vhost_kernel_tap.h
drivers/net/virtio/virtio_user/virtio_user_dev.c

index aeb5075..9b6637d 100644 (file)
@@ -187,6 +187,29 @@ prepare_vhost_memory_kernel(void)
         (1ULL << VIRTIO_NET_F_HOST_TSO6) |     \
         (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,
@@ -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;
index cdb5c3c..f585de8 100644 (file)
@@ -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
index 8535116..eae340c 100644 (file)
@@ -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);
index ae0824c..6617bc8 100644 (file)
@@ -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);