[PATCH] batman-adv: fix throughput detection for VLAN interfaces
Amitesh Singh <[email protected]> Sun, 5 Jul 2026 17:51:34 +0530
| Newsgroups | org.open-mesh.lists.batman,org.kernel.vger.linux-kernel |
|---|---|
| Message-ID | <[email protected]> |
From: Amitesh Singh <[email protected]> batadv_v_elp_get_throughput() calls __ethtool_get_link_ksettings() to determine the link speed of an Ethernet interface. Virtual interfaces such as a VLAN on a bridge return success but with SPEED_UNKNOWN, so the function falls through to the hardcoded default even when the underlying physical device has a known speed. Move rtnl_unlock() into each exit branch so the lock is still held after the initial query. When the speed is unknown, walk the lower device stack with netdev_walk_all_lower_dev() to find the first real device that reports a valid speed, which covers DSA ports and physical NICs sitting below a VLAN or bridge interface. Signed-off-by: Amitesh Singh <[email protected]> --- net/batman-adv/bat_v_elp.c | 48 +++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c index 6ad6042a..3d4d82c5 100644 --- a/net/batman-adv/bat_v_elp.c +++ b/net/batman-adv/bat_v_elp.c @@ -70,6 +70,32 @@ static void batadv_v_elp_start_timer(struct batadv_hard_iface *hard_iface) msecs_to_jiffies(msecs)); } +/** + * batadv_v_elp_lower_dev_throughput() - netdev_walk_all_lower_dev callback + * to query ethtool link speed on a lower device in the stack + * @dev: current lower device being visited + * @priv: nested priv; priv->data points to a u32 to receive the throughput + * value in multiples of 100kbps + * + * Return: 1 to stop the walk once a valid speed is found, 0 to continue. + */ +static int batadv_v_elp_lower_dev_throughput(struct net_device *dev, + struct netdev_nested_priv *priv) +{ + struct ethtool_link_ksettings settings; + u32 speed; + + if (__ethtool_get_link_ksettings(dev, &settings) != 0) + return 0; + + speed = settings.base.speed; + if (!speed || speed == SPEED_UNKNOWN) + return 0; + + *(u32 *)priv->data = speed * 10; + return 1; +} + /** * batadv_v_elp_get_throughput() - get the throughput towards a neighbour * @neigh: the neighbour for which the throughput has to be obtained @@ -167,7 +193,6 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, * ethtool (e.g. an Ethernet adapter) */ ret = __ethtool_get_link_ksettings(hard_iface->net_dev, &link_settings); - rtnl_unlock(); if (ret == 0) { /* link characteristics might change over time */ if (link_settings.base.duplex == DUPLEX_FULL) @@ -177,9 +202,30 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh, throughput = link_settings.base.speed; if (throughput && throughput != SPEED_UNKNOWN) { + rtnl_unlock(); *pthroughput = throughput * 10; return true; } + + /* Virtual interfaces (e.g. VLAN on bridge) don't report link + * speed directly. Walk the lower device stack to find a real + * device that does, such as a DSA port or physical NIC. + */ + throughput = 0; + { + struct netdev_nested_priv priv = { .data = &throughput }; + + netdev_walk_all_lower_dev(hard_iface->net_dev, + batadv_v_elp_lower_dev_throughput, + &priv); + } + rtnl_unlock(); + if (throughput) { + *pthroughput = throughput; + return true; + } + } else { + rtnl_unlock(); } default_throughput: -- 2.43.0