]> git.proxmox.com Git - ovs.git/commitdiff
netdev-dpdk: Fix deadlock in destroy_device().
authorDaniele Di Proietto <diproiettod@vmware.com>
Fri, 5 Aug 2016 20:03:59 +0000 (13:03 -0700)
committerDaniele Di Proietto <diproiettod@vmware.com>
Tue, 9 Aug 2016 18:15:29 +0000 (11:15 -0700)
netdev_dpdk_vhost_destruct() calls rte_vhost_driver_unregister(), which
can trigger the destroy_device() callback.  destroy_device() will try to
take two mutexes already held by netdev_dpdk_vhost_destruct(), causing a
deadlock.

This problem can be solved by dropping the mutexes before calling
rte_vhost_driver_unregister().  The netdev_dpdk_vhost_destruct() and
construct() call are already serialized by netdev_mutex.

This commit also makes clear that dev->vhost_id is constant and can be
accessed without taking any mutexes in the lifetime of the devices.

Fixes: 8d38823bdf8b("netdev-dpdk: fix memory leak")
Reported-by: Ilya Maximets <i.maximets@samsung.com>
Signed-off-by: Daniele Di Proietto <diproiettod@vmware.com>
Acked-by: Ilya Maximets <i.maximets@samsung.com>
Acked-by: Mark Kavanagh <mark.b.kavanagh@intel.com>
lib/netdev-dpdk.c

index bf3a8989ff89105261fa5a6caeca87b1c8e0e097..9be6bab2434c00a832e5f28b0ecc9c18b7e7b724 100644 (file)
@@ -355,8 +355,10 @@ struct netdev_dpdk {
     /* True if vHost device is 'up' and has been reconfigured at least once */
     bool vhost_reconfigured;
 
-    /* Identifier used to distinguish vhost devices from each other */
-    char vhost_id[PATH_MAX];
+    /* Identifier used to distinguish vhost devices from each other.  It does
+     * not change during the lifetime of a struct netdev_dpdk.  It can be read
+     * without holding any mutex. */
+    const char vhost_id[PATH_MAX];
 
     /* In dpdk_list. */
     struct ovs_list list_node OVS_GUARDED_BY(dpdk_mutex);
@@ -846,7 +848,8 @@ netdev_dpdk_vhost_cuse_construct(struct netdev *netdev)
     }
 
     ovs_mutex_lock(&dpdk_mutex);
-    strncpy(dev->vhost_id, netdev->name, sizeof(dev->vhost_id));
+    strncpy(CONST_CAST(char *, dev->vhost_id), netdev->name,
+            sizeof dev->vhost_id);
     err = vhost_construct_helper(netdev);
     ovs_mutex_unlock(&dpdk_mutex);
     return err;
@@ -878,7 +881,7 @@ netdev_dpdk_vhost_user_construct(struct netdev *netdev)
     /* Take the name of the vhost-user port and append it to the location where
      * the socket is to be created, then register the socket.
      */
-    snprintf(dev->vhost_id, sizeof(dev->vhost_id), "%s/%s",
+    snprintf(CONST_CAST(char *, dev->vhost_id), sizeof dev->vhost_id, "%s/%s",
              vhost_sock_dir, name);
 
     err = rte_vhost_driver_register(dev->vhost_id, flags);
@@ -938,6 +941,17 @@ netdev_dpdk_destruct(struct netdev *netdev)
     ovs_mutex_unlock(&dpdk_mutex);
 }
 
+/* rte_vhost_driver_unregister() can call back destroy_device(), which will
+ * try to acquire 'dpdk_mutex' and possibly 'dev->mutex'.  To avoid a
+ * deadlock, none of the mutexes must be held while calling this function. */
+static int
+dpdk_vhost_driver_unregister(struct netdev_dpdk *dev)
+    OVS_EXCLUDED(dpdk_mutex)
+    OVS_EXCLUDED(dev->mutex)
+{
+    return rte_vhost_driver_unregister(dev->vhost_id);
+}
+
 static void
 netdev_dpdk_vhost_destruct(struct netdev *netdev)
 {
@@ -955,12 +969,6 @@ netdev_dpdk_vhost_destruct(struct netdev *netdev)
                  dev->vhost_id);
     }
 
-    if (rte_vhost_driver_unregister(dev->vhost_id)) {
-        VLOG_ERR("Unable to remove vhost-user socket %s", dev->vhost_id);
-    } else {
-        fatal_signal_remove_file_to_unlink(dev->vhost_id);
-    }
-
     free(ovsrcu_get_protected(struct ingress_policer *,
                               &dev->ingress_policer));
 
@@ -970,6 +978,12 @@ netdev_dpdk_vhost_destruct(struct netdev *netdev)
 
     ovs_mutex_unlock(&dev->mutex);
     ovs_mutex_unlock(&dpdk_mutex);
+
+    if (dpdk_vhost_driver_unregister(dev)) {
+        VLOG_ERR("Unable to remove vhost-user socket %s", dev->vhost_id);
+    } else {
+        fatal_signal_remove_file_to_unlink(dev->vhost_id);
+    }
 }
 
 static void