Skip to content

Commit f0c90d6

Browse files
ecsvgregkh
authored andcommitted
batman-adv: Avoid double-rtnl_lock ELP metric worker
commit cfc83a3 upstream. 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: [email protected] Fixes: 8c8ecc9 ("batman-adv: Drop unmanaged ELP metric worker") Reported-by: Christian Schmidbauer <[email protected]> Signed-off-by: Sven Eckelmann <[email protected]> Tested-by: Sören Skaarup <[email protected]> Signed-off-by: Simon Wunderlich <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 0613e7e commit f0c90d6

3 files changed

Lines changed: 14 additions & 5 deletions

File tree

net/batman-adv/bat_v_elp.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,15 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
111111
/* unsupported WiFi driver version */
112112
goto default_throughput;
113113

114-
real_netdev = batadv_get_real_netdev(hard_iface->net_dev);
114+
/* only use rtnl_trylock because the elp worker will be cancelled while
115+
* the rntl_lock is held. the cancel_delayed_work_sync() would otherwise
116+
* wait forever when the elp work_item was started and it is then also
117+
* trying to rtnl_lock
118+
*/
119+
if (!rtnl_trylock())
120+
return false;
121+
real_netdev = __batadv_get_real_netdev(hard_iface->net_dev);
122+
rtnl_unlock();
115123
if (!real_netdev)
116124
goto default_throughput;
117125

net/batman-adv/hard-interface.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev)
204204
}
205205

206206
/**
207-
* batadv_get_real_netdevice() - check if the given netdev struct is a virtual
207+
* __batadv_get_real_netdev() - check if the given netdev struct is a virtual
208208
* interface on top of another 'real' interface
209209
* @netdev: the device to check
210210
*
@@ -214,7 +214,7 @@ static bool batadv_is_valid_iface(const struct net_device *net_dev)
214214
* Return: the 'real' net device or the original net device and NULL in case
215215
* of an error.
216216
*/
217-
static struct net_device *batadv_get_real_netdevice(struct net_device *netdev)
217+
struct net_device *__batadv_get_real_netdev(struct net_device *netdev)
218218
{
219219
struct batadv_hard_iface *hard_iface = NULL;
220220
struct net_device *real_netdev = NULL;
@@ -267,7 +267,7 @@ struct net_device *batadv_get_real_netdev(struct net_device *net_device)
267267
struct net_device *real_netdev;
268268

269269
rtnl_lock();
270-
real_netdev = batadv_get_real_netdevice(net_device);
270+
real_netdev = __batadv_get_real_netdev(net_device);
271271
rtnl_unlock();
272272

273273
return real_netdev;
@@ -336,7 +336,7 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
336336
if (batadv_is_cfg80211_netdev(net_device))
337337
wifi_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
338338

339-
real_netdev = batadv_get_real_netdevice(net_device);
339+
real_netdev = __batadv_get_real_netdev(net_device);
340340
if (!real_netdev)
341341
return wifi_flags;
342342

net/batman-adv/hard-interface.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ enum batadv_hard_if_bcast {
6767

6868
extern struct notifier_block batadv_hard_if_notifier;
6969

70+
struct net_device *__batadv_get_real_netdev(struct net_device *net_device);
7071
struct net_device *batadv_get_real_netdev(struct net_device *net_device);
7172
bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface);
7273
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);

0 commit comments

Comments
 (0)