Do not close the tap fds when disabling queue pairs, instead,
we just need to unbind the backend. Otherwise, tap port can be
destroyed unexpectedly.

Fixes: e3b434818bbb ("net/virtio-user: support kernel vhost")
Cc: sta...@dpdk.org

Reported-by: Stephen Hemminger <step...@networkplumber.org>
Signed-off-by: Tiwei Bie <tiwei....@intel.com>
---
v2:
- Squash attach_queue/detach_queue into one routine (Stephen);
- Use initializer instead of memset (Stephen);
- Drop the cast to void * (Stephen);

 drivers/net/virtio/virtio_user/vhost_kernel.c | 34 +++++++++++++++----
 .../net/virtio/virtio_user/vhost_kernel_tap.c | 34 ++++++++++++-------
 .../net/virtio/virtio_user/vhost_kernel_tap.h |  8 +++++
 drivers/net/virtio/virtio_user/vhost_user.c   |  4 +++
 .../net/virtio/virtio_user/virtio_user_dev.c  |  5 ++-
 .../net/virtio/virtio_user/virtio_user_dev.h  |  1 +
 6 files changed, 65 insertions(+), 21 deletions(-)

diff --git a/drivers/net/virtio/virtio_user/vhost_kernel.c 
b/drivers/net/virtio/virtio_user/vhost_kernel.c
index 5c81e8dd9..2c805077a 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel.c
@@ -330,16 +330,34 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev 
*dev,
 
        vhostfd = dev->vhostfds[pair_idx];
 
+       if (dev->qp_enabled[pair_idx] == enable)
+               return 0;
+
        if (!enable) {
-               if (dev->tapfds[pair_idx] >= 0) {
-                       close(dev->tapfds[pair_idx]);
-                       dev->tapfds[pair_idx] = -1;
+               tapfd = dev->tapfds[pair_idx];
+               if (vhost_kernel_set_backend(vhostfd, -1) < 0) {
+                       PMD_DRV_LOG(ERR, "fail to set backend for vhost 
kernel");
+                       return -1;
                }
-               return vhost_kernel_set_backend(vhostfd, -1);
-       } else if (dev->tapfds[pair_idx] >= 0) {
+               if (req_mq && vhost_kernel_tap_set_queue(tapfd, false) < 0) {
+                       PMD_DRV_LOG(ERR, "fail to disable tap for vhost 
kernel");
+                       return -1;
+               }
+               dev->qp_enabled[pair_idx] = false;
                return 0;
        }
 
+       if (dev->tapfds[pair_idx] >= 0) {
+               tapfd = dev->tapfds[pair_idx];
+               if (vhost_kernel_tap_set_offload(tapfd, dev->features) == -1)
+                       return -1;
+               if (req_mq && vhost_kernel_tap_set_queue(tapfd, true) < 0) {
+                       PMD_DRV_LOG(ERR, "fail to enable tap for vhost kernel");
+                       return -1;
+               }
+               goto set_backend;
+       }
+
        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);
@@ -353,13 +371,15 @@ vhost_kernel_enable_queue_pair(struct virtio_user_dev 
*dev,
                return -1;
        }
 
+       dev->tapfds[pair_idx] = tapfd;
+
+set_backend:
        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;
+       dev->qp_enabled[pair_idx] = true;
        return 0;
 }
 
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c 
b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
index 76bf75423..2fbfecba1 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.c
@@ -18,7 +18,7 @@
 #include "../virtio_logs.h"
 #include "../virtio_pci.h"
 
-static int
+int
 vhost_kernel_tap_set_offload(int fd, uint64_t features)
 {
        unsigned int offload = 0;
@@ -37,26 +37,34 @@ vhost_kernel_tap_set_offload(int fd, uint64_t features)
                        offload |= TUN_F_UFO;
        }
 
-       if (offload != 0) {
-               /* Check if our kernel supports TUNSETOFFLOAD */
-               if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
-                       PMD_DRV_LOG(ERR, "Kernel does't support 
TUNSETOFFLOAD\n");
-                       return -ENOTSUP;
-               }
+       /* Check if our kernel supports TUNSETOFFLOAD */
+       if (ioctl(fd, TUNSETOFFLOAD, 0) != 0 && errno == EINVAL) {
+               PMD_DRV_LOG(ERR, "Kernel does't support TUNSETOFFLOAD\n");
+               return -ENOTSUP;
+       }
 
+       if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
+               offload &= ~TUN_F_UFO;
                if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
-                       offload &= ~TUN_F_UFO;
-                       if (ioctl(fd, TUNSETOFFLOAD, offload) != 0) {
-                               PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: 
%s\n",
-                                       strerror(errno));
-                               return -1;
-                       }
+                       PMD_DRV_LOG(ERR, "TUNSETOFFLOAD ioctl() failed: %s\n",
+                               strerror(errno));
+                       return -1;
                }
        }
 
        return 0;
 }
 
+int
+vhost_kernel_tap_set_queue(int fd, bool attach)
+{
+       struct ifreq ifr = {
+               .ifr_flags = attach ? IFF_ATTACH_QUEUE : IFF_DETACH_QUEUE,
+       };
+
+       return ioctl(fd, TUNSETQUEUE, &ifr);
+}
+
 int
 vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq,
                         const char *mac, uint64_t features)
diff --git a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h 
b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
index e0e95b4f5..5c4447b29 100644
--- a/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
+++ b/drivers/net/virtio/virtio_user/vhost_kernel_tap.h
@@ -2,6 +2,10 @@
  * Copyright(c) 2016 Intel Corporation
  */
 
+#ifndef _VHOST_KERNEL_TAP_H
+#define _VHOST_KERNEL_TAP_H
+
+#include <stdbool.h>
 #include <sys/ioctl.h>
 
 /* TUN ioctls */
@@ -37,3 +41,7 @@
 
 int vhost_kernel_open_tap(char **p_ifname, int hdr_size, int req_mq,
                         const char *mac, uint64_t features);
+int vhost_kernel_tap_set_offload(int fd, uint64_t features);
+int vhost_kernel_tap_set_queue(int fd, bool attach);
+
+#endif
diff --git a/drivers/net/virtio/virtio_user/vhost_user.c 
b/drivers/net/virtio/virtio_user/vhost_user.c
index a4b5c25cd..d8e083ba8 100644
--- a/drivers/net/virtio/virtio_user/vhost_user.c
+++ b/drivers/net/virtio/virtio_user/vhost_user.c
@@ -456,6 +456,9 @@ vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
 {
        int i;
 
+       if (dev->qp_enabled[pair_idx] == enable)
+               return 0;
+
        for (i = 0; i < 2; ++i) {
                struct vhost_vring_state state = {
                        .index = pair_idx * 2 + i,
@@ -466,6 +469,7 @@ vhost_user_enable_queue_pair(struct virtio_user_dev *dev,
                        return -1;
        }
 
+       dev->qp_enabled[pair_idx] = enable;
        return 0;
 }
 
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c 
b/drivers/net/virtio/virtio_user/virtio_user_dev.c
index a4400e772..177e829c9 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
@@ -545,8 +545,11 @@ virtio_user_dev_uninit(struct virtio_user_dev *dev)
        }
 
        if (dev->vhostfds) {
-               for (i = 0; i < dev->max_queue_pairs; ++i)
+               for (i = 0; i < dev->max_queue_pairs; ++i) {
                        close(dev->vhostfds[i]);
+                       if (dev->tapfds[i] >= 0)
+                               close(dev->tapfds[i]);
+               }
                free(dev->vhostfds);
                free(dev->tapfds);
        }
diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.h 
b/drivers/net/virtio/virtio_user/virtio_user_dev.h
index ad8683771..3b6b6065a 100644
--- a/drivers/net/virtio/virtio_user/virtio_user_dev.h
+++ b/drivers/net/virtio/virtio_user/virtio_user_dev.h
@@ -49,6 +49,7 @@ struct virtio_user_dev {
                struct vring_packed     packed_vrings[VIRTIO_MAX_VIRTQUEUES];
        };
        struct virtio_user_queue packed_queues[VIRTIO_MAX_VIRTQUEUES];
+       bool            qp_enabled[VIRTIO_MAX_VIRTQUEUE_PAIRS];
 
        struct virtio_user_backend_ops *ops;
        pthread_mutex_t mutex;
-- 
2.24.0

Reply via email to