]> git.itanic.dy.fi Git - linux-stable/commitdiff
devlink: Acquire device lock during netns dismantle
authorIdo Schimmel <idosch@nvidia.com>
Wed, 15 Nov 2023 12:17:11 +0000 (13:17 +0100)
committerDavid S. Miller <davem@davemloft.net>
Sat, 18 Nov 2023 17:38:50 +0000 (17:38 +0000)
Device drivers register with devlink from their probe routines (under
the device lock) by acquiring the devlink instance lock and calling
devl_register().

Drivers that support a devlink reload usually implement the
reload_{down, up}() operations in a similar fashion to their remove and
probe routines, respectively.

However, while the remove and probe routines are invoked with the device
lock held, the reload operations are only invoked with the devlink
instance lock held. It is therefore impossible for drivers to acquire
the device lock from their reload operations, as this would result in
lock inversion.

The motivating use case for invoking the reload operations with the
device lock held is in mlxsw which needs to trigger a PCI reset as part
of the reload. The driver cannot call pci_reset_function() as this
function acquires the device lock. Instead, it needs to call
__pci_reset_function_locked which expects the device lock to be held.

To that end, adjust devlink to always acquire the device lock before the
devlink instance lock when performing a reload.

For now, only do that when reload is triggered as part of netns
dismantle. Subsequent patches will handle the case where reload is
explicitly triggered by user space.

Signed-off-by: Ido Schimmel <idosch@nvidia.com>
Reviewed-by: Jiri Pirko <jiri@nvidia.com>
Signed-off-by: Petr Machata <petrm@nvidia.com>
Reviewed-by: Simon Horman <horms@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/devlink/core.c
net/devlink/devl_internal.h

index 6984877e9f10da8ce39050d8335ea79243a831b9..4275a2bc6d8e062052a88503b731d9599ca55d2a 100644 (file)
@@ -503,14 +503,14 @@ static void __net_exit devlink_pernet_pre_exit(struct net *net)
         * all devlink instances from this namespace into init_net.
         */
        devlinks_xa_for_each_registered_get(net, index, devlink) {
-               devl_lock(devlink);
+               devl_dev_lock(devlink, true);
                err = 0;
                if (devl_is_registered(devlink))
                        err = devlink_reload(devlink, &init_net,
                                             DEVLINK_RELOAD_ACTION_DRIVER_REINIT,
                                             DEVLINK_RELOAD_LIMIT_UNSPEC,
                                             &actions_performed, NULL);
-               devl_unlock(devlink);
+               devl_dev_unlock(devlink, true);
                devlink_put(devlink);
                if (err && err != -EOPNOTSUPP)
                        pr_warn("Failed to reload devlink instance into init_net\n");
index 2a9b263300a4baac4012f2b342fee233af60484a..178abaf74a10721435259693fd7b8cb71870043e 100644 (file)
@@ -3,6 +3,7 @@
  * Copyright (c) 2016 Jiri Pirko <jiri@mellanox.com>
  */
 
+#include <linux/device.h>
 #include <linux/etherdevice.h>
 #include <linux/mutex.h>
 #include <linux/netdevice.h>
@@ -96,6 +97,20 @@ static inline bool devl_is_registered(struct devlink *devlink)
        return xa_get_mark(&devlinks, devlink->index, DEVLINK_REGISTERED);
 }
 
+static inline void devl_dev_lock(struct devlink *devlink, bool dev_lock)
+{
+       if (dev_lock)
+               device_lock(devlink->dev);
+       devl_lock(devlink);
+}
+
+static inline void devl_dev_unlock(struct devlink *devlink, bool dev_lock)
+{
+       devl_unlock(devlink);
+       if (dev_lock)
+               device_unlock(devlink->dev);
+}
+
 typedef void devlink_rel_notify_cb_t(struct devlink *devlink, u32 obj_index);
 typedef void devlink_rel_cleanup_cb_t(struct devlink *devlink, u32 obj_index,
                                      u32 rel_index);