]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
net/mlx5: qos: Restrict RTNL area to avoid a lock cycle
authorCosmin Ratiu <cratiu@nvidia.com>
Mon, 16 Mar 2026 09:46:01 +0000 (11:46 +0200)
committerJakub Kicinski <kuba@kernel.org>
Thu, 19 Mar 2026 00:54:53 +0000 (17:54 -0700)
A lock dependency cycle exists where:
1. mlx5_ib_roce_init -> mlx5_core_uplink_netdev_event_replay ->
mlx5_blocking_notifier_call_chain (takes notifier_rwsem) ->
mlx5e_mdev_notifier_event -> mlx5_netdev_notifier_register ->
register_netdevice_notifier_dev_net (takes rtnl)
=> notifier_rwsem -> rtnl

2. mlx5e_probe -> _mlx5e_probe ->
mlx5_core_uplink_netdev_set (takes uplink_netdev_lock) ->
mlx5_blocking_notifier_call_chain (takes notifier_rwsem)
=> uplink_netdev_lock -> notifier_rwsem

3: devlink_nl_rate_set_doit -> devlink_nl_rate_set ->
mlx5_esw_devlink_rate_leaf_tx_max_set -> esw_qos_devlink_rate_to_mbps ->
mlx5_esw_qos_max_link_speed_get (takes rtnl) ->
mlx5_esw_qos_lag_link_speed_get_locked ->
mlx5_uplink_netdev_get (takes uplink_netdev_lock)
=> rtnl -> uplink_netdev_lock
=> BOOM! (lock cycle)

Fix that by restricting the rtnl-protected section to just the necessary
part, the call to netdev_master_upper_dev_get and speed querying, so
that the last lock dependency is avoided and the cycle doesn't close.
This is safe because mlx5_uplink_netdev_get uses netdev_hold to keep the
uplink netdev alive while its master device is queried.

Use this opportunity to rename the ambiguously-named "hold_rtnl_lock"
argument to "take_rtnl" and remove the "_locked" suffix from
mlx5_esw_qos_lag_link_speed_get_locked.

Fixes: 6b4be64fd9fe ("net/mlx5e: Harden uplink netdev access against device unbind")
Signed-off-by: Cosmin Ratiu <cratiu@nvidia.com>
Reviewed-by: Dragos Tatulea <dtatulea@nvidia.com>
Signed-off-by: Tariq Toukan <tariqt@nvidia.com>
Link: https://patch.msgid.link/20260316094603.6999-2-tariqt@nvidia.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/mellanox/mlx5/core/esw/qos.c

index 26178d0bac925ab7ce33620f5a0002b01c712d98..faccc60fc93a8bb251dc6ba0f3aa314dbbd3ce9d 100644 (file)
@@ -1489,24 +1489,24 @@ out:
        return err;
 }
 
-static u32 mlx5_esw_qos_lag_link_speed_get_locked(struct mlx5_core_dev *mdev)
+static u32 mlx5_esw_qos_lag_link_speed_get(struct mlx5_core_dev *mdev,
+                                          bool take_rtnl)
 {
        struct ethtool_link_ksettings lksettings;
        struct net_device *slave, *master;
        u32 speed = SPEED_UNKNOWN;
 
-       /* Lock ensures a stable reference to master and slave netdevice
-        * while port speed of master is queried.
-        */
-       ASSERT_RTNL();
-
        slave = mlx5_uplink_netdev_get(mdev);
        if (!slave)
                goto out;
 
+       if (take_rtnl)
+               rtnl_lock();
        master = netdev_master_upper_dev_get(slave);
        if (master && !__ethtool_get_link_ksettings(master, &lksettings))
                speed = lksettings.base.speed;
+       if (take_rtnl)
+               rtnl_unlock();
 
 out:
        mlx5_uplink_netdev_put(mdev, slave);
@@ -1514,20 +1514,15 @@ out:
 }
 
 static int mlx5_esw_qos_max_link_speed_get(struct mlx5_core_dev *mdev, u32 *link_speed_max,
-                                          bool hold_rtnl_lock, struct netlink_ext_ack *extack)
+                                          bool take_rtnl,
+                                          struct netlink_ext_ack *extack)
 {
        int err;
 
        if (!mlx5_lag_is_active(mdev))
                goto skip_lag;
 
-       if (hold_rtnl_lock)
-               rtnl_lock();
-
-       *link_speed_max = mlx5_esw_qos_lag_link_speed_get_locked(mdev);
-
-       if (hold_rtnl_lock)
-               rtnl_unlock();
+       *link_speed_max = mlx5_esw_qos_lag_link_speed_get(mdev, take_rtnl);
 
        if (*link_speed_max != (u32)SPEED_UNKNOWN)
                return 0;