]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
batman-adv: Avoid double-rtnl_lock ELP metric worker
authorSven Eckelmann <sven@narfation.org>
Mon, 16 Feb 2026 10:20:29 +0000 (11:20 +0100)
committerSimon Wunderlich <sw@simonwunderlich.de>
Sat, 21 Feb 2026 12:01:55 +0000 (13:01 +0100)
batadv_v_elp_get_throughput() might be called when the RTNL lock is already
held. This could be problematic when the work queue item is cancelled via
cancel_delayed_work_sync() in batadv_v_elp_iface_disable(). In this case,
an rtnl_lock() would cause a deadlock.

To avoid this, rtnl_trylock() was used in this function to skip the
retrieval of the ethtool information in case the RTNL lock was already
held.

But for cfg80211 interfaces, batadv_get_real_netdev() was called - which
also uses rtnl_lock(). The approach for __ethtool_get_link_ksettings() must
also be used instead and the lockless version __batadv_get_real_netdev()
has to be called.

Cc: stable@vger.kernel.org
Fixes: 8c8ecc98f5c6 ("batman-adv: Drop unmanaged ELP metric worker")
Reported-by: Christian Schmidbauer <github@grische.xyz>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Tested-by: Sören Skaarup <freifunk_nordm4nn@gmx.de>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
net/batman-adv/bat_v_elp.c
net/batman-adv/hard-interface.c
net/batman-adv/hard-interface.h

index cb16c1ed2a58fcbb2a7b7c682bbc3e58f9c6a7f9..fe832093d421fb6d4c87fce0ad8d874270d005b1 100644 (file)
@@ -111,7 +111,15 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
                        /* unsupported WiFi driver version */
                        goto default_throughput;
 
-               real_netdev = batadv_get_real_netdev(hard_iface->net_dev);
+               /* only use rtnl_trylock because the elp worker will be cancelled while
+                * the rntl_lock is held. the cancel_delayed_work_sync() would otherwise
+                * wait forever when the elp work_item was started and it is then also
+                * trying to rtnl_lock
+                */
+               if (!rtnl_trylock())
+                       return false;
+               real_netdev = __batadv_get_real_netdev(hard_iface->net_dev);
+               rtnl_unlock();
                if (!real_netdev)
                        goto default_throughput;
 
index 5113f879736b54f0231d0a030dd4bef5a320e9ae..1c488049d55463e51a0fee4d3c045f28b47cf156 100644 (file)
@@ -204,7 +204,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev)
 }
 
 /**
- * batadv_get_real_netdevice() - check if the given netdev struct is a virtual
+ * __batadv_get_real_netdev() - check if the given netdev struct is a virtual
  *  interface on top of another 'real' interface
  * @netdev: the device to check
  *
@@ -214,7 +214,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev)
  * Return: the 'real' net device or the original net device and NULL in case
  *  of an error.
  */
-static struct net_device *batadv_get_real_netdevice(struct net_device *netdev)
+struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
 {
        struct batadv_hard_iface *hard_iface = NULL;
        struct net_device *real_netdev = NULL;
@@ -267,7 +267,7 @@ struct net_device *batadv_get_real_netdev(struct net_device *net_device)
        struct net_device *real_netdev;
 
        rtnl_lock();
-       real_netdev = batadv_get_real_netdevice(net_device);
+       real_netdev = __batadv_get_real_netdev(net_device);
        rtnl_unlock();
 
        return real_netdev;
@@ -336,7 +336,7 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
        if (batadv_is_cfg80211_netdev(net_device))
                wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
 
-       real_netdev = batadv_get_real_netdevice(net_device);
+       real_netdev = __batadv_get_real_netdev(net_device);
        if (!real_netdev)
                return wifi_flags;
 
index 9db8a310961ead452a9775eb77d01ce51854b9cc..9ba8fb2bdceb48f221d31333d7bcef765130062a 100644 (file)
@@ -67,6 +67,7 @@ enum batadv_hard_if_bcast {
 
 extern struct notifier_block batadv_hard_if_notifier;
 
+struct net_device *__batadv_get_real_netdev(struct net_device *net_device);
 struct net_device *batadv_get_real_netdev(struct net_device *net_device);
 bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface);
 bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);