From: Simon Wunderlich <sw@simonwunderlich.de>
To: netdev@vger.kernel.org
Cc: "David S. Miller" <davem@davemloft.net>,
Eric Dumazet <edumazet@google.com>,
Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
Simon Horman <horms@kernel.org>,
b.a.t.m.a.n@lists.open-mesh.org,
Sven Eckelmann <sven@narfation.org>,
Simon Wunderlich <sw@simonwunderlich.de>
Subject: [PATCH net-next 06/15] batman-adv: extract netdev wifi detection information object
Date: Thu, 28 May 2026 16:29:15 +0200 [thread overview]
Message-ID: <20260528142924.329658-7-sw@simonwunderlich.de> (raw)
In-Reply-To: <20260528142924.329658-1-sw@simonwunderlich.de>
From: Sven Eckelmann <sven@narfation.org>
Previously, wifi_flags were stored directly in batadv_hard_iface, which is
created for every network interface on the system (including those never
attached to a mesh interface). This wastes memory and complicates the
long-term goal of lazily allocating batadv_hard_iface only for interfaces
that actually join a mesh.
The problem is that several batman-adv features need wifi detection for
net_devices (and their underlying devices) regardless of whether a
batadv_hard_iface exists for them:
* B.A.T.M.A.N. IV TQ hop penalty calculation
* B.A.T.M.A.N. V ELP probing / throughput estimation
* AP isolation
To decouple wifi detection from batadv_hard_iface lifetime, introduce a
global rhashtable (batadv_wifi_net_devices) mapping net_device pointers to
batadv_wifi_net_device_state objects. Only net_devices that are actually
detected as (indirect) wifi interfaces occupy an entry, keeping the common
(non-wifi) case allocation-free.
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de>
---
net/batman-adv/bat_v_elp.c | 6 +-
net/batman-adv/hard-interface.c | 189 +++++++++++++++++++++++++++++---
net/batman-adv/hard-interface.h | 34 +++++-
net/batman-adv/main.c | 16 ++-
net/batman-adv/types.h | 30 ++++-
5 files changed, 250 insertions(+), 25 deletions(-)
diff --git a/net/batman-adv/bat_v_elp.c b/net/batman-adv/bat_v_elp.c
index fdc2abe96d777..e207093de79fe 100644
--- a/net/batman-adv/bat_v_elp.c
+++ b/net/batman-adv/bat_v_elp.c
@@ -85,6 +85,7 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
struct ethtool_link_ksettings link_settings;
struct net_device *real_netdev;
struct station_info sinfo;
+ u32 wifi_flags;
u32 throughput;
int ret;
@@ -106,8 +107,9 @@ static bool batadv_v_elp_get_throughput(struct batadv_hardif_neigh_node *neigh,
/* if this is a wireless device, then ask its throughput through
* cfg80211 API
*/
- if (batadv_is_wifi_hardif(hard_iface)) {
- if (!batadv_is_cfg80211_hardif(hard_iface))
+ wifi_flags = batadv_hardif_get_wifi_flags(hard_iface);
+ if (batadv_is_wifi(wifi_flags)) {
+ if (!batadv_is_cfg80211(wifi_flags))
/* unsupported WiFi driver version */
goto default_throughput;
diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c
index 4b3804ef70b6f..ca9f5c37fb543 100644
--- a/net/batman-adv/hard-interface.c
+++ b/net/batman-adv/hard-interface.c
@@ -26,6 +26,8 @@
#include <linux/notifier.h>
#include <linux/printk.h>
#include <linux/rculist.h>
+#include <linux/rhashtable-types.h>
+#include <linux/rhashtable.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
@@ -43,6 +45,15 @@
#include "send.h"
#include "translation-table.h"
+static const struct rhashtable_params batadv_wifi_net_devices_params = {
+ .key_len = sizeof(struct net_device *),
+ .key_offset = offsetof(struct batadv_wifi_net_device_state, netdev),
+ .head_offset = offsetof(struct batadv_wifi_net_device_state, l),
+ .automatic_shrinking = true,
+};
+
+static struct rhashtable batadv_wifi_net_devices;
+
/**
* batadv_hardif_release() - release hard interface from lists and queue for
* free after rcu grace period
@@ -356,21 +367,28 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device)
}
/**
- * batadv_is_cfg80211_hardif() - check if the given hardif is a cfg80211 wifi
- * interface
+ * batadv_hardif_get_wifi_flags() - retrieve wifi flags for hard_iface
* @hard_iface: the device to check
*
- * Return: true if the net device is a cfg80211 wireless device, false
- * otherwise.
+ * Return: batadv_hard_iface_wifi_flags flags of the device
*/
-bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
+u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface)
{
- u32 allowed_flags = 0;
+ struct batadv_wifi_net_device_state *device_state;
+ u32 wifi_flags = 0;
+
+ if (!hard_iface)
+ return 0;
- allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
- allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
+ rcu_read_lock();
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &hard_iface->net_dev,
+ batadv_wifi_net_devices_params);
+ if (device_state)
+ wifi_flags = READ_ONCE(device_state->wifi_flags);
+ rcu_read_unlock();
- return !!(hard_iface->wifi_flags & allowed_flags);
+ return wifi_flags;
}
/**
@@ -381,10 +399,9 @@ bool batadv_is_cfg80211_hardif(struct batadv_hard_iface *hard_iface)
*/
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface)
{
- if (!hard_iface)
- return false;
+ u32 wifi_flags = batadv_hardif_get_wifi_flags(hard_iface);
- return hard_iface->wifi_flags != 0;
+ return batadv_is_wifi(wifi_flags);
}
/**
@@ -890,7 +907,6 @@ batadv_hardif_add_interface(struct net_device *net_dev)
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
- hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
@@ -942,6 +958,131 @@ static int batadv_hard_if_event_meshif(unsigned long event,
return NOTIFY_DONE;
}
+/**
+ * batadv_wifi_net_device_insert() - save information about wifi net_device
+ * @net_dev: net_device to add to batadv_wifi_net_devices
+ * @wifi_flags: net_device which generated an event
+ *
+ * Return: 0 on result, negative value on error
+ */
+static int
+batadv_wifi_net_device_insert(struct net_device *net_dev, u32 wifi_flags)
+{
+ struct batadv_wifi_net_device_state *device_state;
+ int ret;
+
+ ASSERT_RTNL();
+
+ device_state = kzalloc_obj(*device_state, GFP_ATOMIC);
+ if (!device_state)
+ return -ENOMEM;
+
+ device_state->wifi_flags = wifi_flags;
+ netdev_hold(net_dev, &device_state->dev_tracker, GFP_ATOMIC);
+ device_state->netdev = net_dev;
+ WRITE_ONCE(device_state->wifi_flags, wifi_flags);
+
+ ret = rhashtable_insert_fast(&batadv_wifi_net_devices, &device_state->l,
+ batadv_wifi_net_devices_params);
+ if (ret < 0)
+ goto err_free;
+
+ return 0;
+
+err_free:
+ netdev_put(device_state->netdev, &device_state->dev_tracker);
+ kfree(device_state);
+ return ret;
+}
+
+/**
+ * batadv_wifi_net_device_remove() - remove information about wifi net_device
+ * @device_state: wifi net_device state to remove from batadv_wifi_net_device_state
+ */
+static void
+batadv_wifi_net_device_remove(struct batadv_wifi_net_device_state *device_state)
+{
+ ASSERT_RTNL();
+
+ rhashtable_remove_fast(&batadv_wifi_net_devices, &device_state->l,
+ batadv_wifi_net_devices_params);
+ netdev_put(device_state->netdev, &device_state->dev_tracker);
+ kfree_rcu(device_state, rcu);
+}
+
+/**
+ * batadv_wifi_net_device_update() - update wifi state of net_device
+ * @net_dev: net_device to update in batadv_wifi_net_devices
+ *
+ * The device will only be stored in batadv_wifi_net_devices when
+ * it could be identified as wifi device. If the net_device is no
+ * longer a wifi device, it is automatically removed from
+ * batadv_wifi_net_devices.
+ */
+static void
+batadv_wifi_net_device_update(struct net_device *net_dev)
+{
+ struct batadv_wifi_net_device_state *device_state;
+ u32 wifi_flags;
+
+ ASSERT_RTNL();
+
+ wifi_flags = batadv_wifi_flags_evaluate(net_dev);
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &net_dev,
+ batadv_wifi_net_devices_params);
+
+ if (device_state) {
+ if (batadv_is_wifi(wifi_flags))
+ WRITE_ONCE(device_state->wifi_flags, wifi_flags);
+ else
+ batadv_wifi_net_device_remove(device_state);
+ } else if (batadv_is_wifi(wifi_flags)) {
+ batadv_wifi_net_device_insert(net_dev, wifi_flags);
+ }
+}
+
+/**
+ * batadv_wifi_net_device_unregister() - remove wifi state of net_device
+ * @net_dev: net_device to remove from batadv_wifi_net_devices
+ */
+static void
+batadv_wifi_net_device_unregister(struct net_device *net_dev)
+{
+ struct batadv_wifi_net_device_state *device_state;
+
+ ASSERT_RTNL();
+
+ device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices,
+ &net_dev,
+ batadv_wifi_net_devices_params);
+ if (!device_state)
+ return;
+
+ batadv_wifi_net_device_remove(device_state);
+}
+
+/**
+ * batadv_wifi_net_device_event() - handle network events for batadv_wifi_net_devices
+ * @event: enum netdev_cmd event to handle
+ * @net_dev: net_device to update in batadv_wifi_net_devices
+ */
+static void batadv_wifi_net_device_event(unsigned long event,
+ struct net_device *net_dev)
+{
+ switch (event) {
+ case NETDEV_REGISTER:
+ case NETDEV_POST_TYPE_CHANGE:
+ case NETDEV_CHANGEUPPER:
+ batadv_wifi_net_device_update(net_dev);
+ break;
+ case NETDEV_UNREGISTER:
+ case NETDEV_PRE_TYPE_CHANGE:
+ batadv_wifi_net_device_unregister(net_dev);
+ break;
+ }
+}
+
static int batadv_hard_if_event(struct notifier_block *this,
unsigned long event, void *ptr)
{
@@ -953,6 +1094,8 @@ static int batadv_hard_if_event(struct notifier_block *this,
if (batadv_meshif_is_valid(net_dev))
return batadv_hard_if_event_meshif(event, net_dev);
+ batadv_wifi_net_device_event(event, net_dev);
+
hard_iface = batadv_hardif_get_by_netdev(net_dev);
if (!hard_iface && (event == NETDEV_REGISTER ||
event == NETDEV_POST_TYPE_CHANGE))
@@ -996,8 +1139,9 @@ static int batadv_hard_if_event(struct notifier_block *this,
if (hard_iface == primary_if)
batadv_primary_if_update_addr(bat_priv, NULL);
break;
+ case NETDEV_REGISTER:
+ case NETDEV_POST_TYPE_CHANGE:
case NETDEV_CHANGEUPPER:
- hard_iface->wifi_flags = batadv_wifi_flags_evaluate(net_dev);
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
break;
@@ -1015,3 +1159,20 @@ static int batadv_hard_if_event(struct notifier_block *this,
struct notifier_block batadv_hard_if_notifier = {
.notifier_call = batadv_hard_if_event,
};
+
+/**
+ * batadv_wifi_net_devices_init() - Initialize wifi devices cache
+ */
+int __init batadv_wifi_net_devices_init(void)
+{
+ return rhashtable_init(&batadv_wifi_net_devices,
+ &batadv_wifi_net_devices_params);
+}
+
+/**
+ * batadv_wifi_net_devices_deinit() - Deinitialize wifi devices cache
+ */
+void batadv_wifi_net_devices_deinit(void)
+{
+ rhashtable_destroy(&batadv_wifi_net_devices);
+}
diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h
index 9ba8fb2bdceb4..089e65c8a4817 100644
--- a/net/batman-adv/hard-interface.h
+++ b/net/batman-adv/hard-interface.h
@@ -10,6 +10,7 @@
#include "main.h"
#include <linux/compiler.h>
+#include <linux/init.h>
#include <linux/kref.h>
#include <linux/netdevice.h>
#include <linux/rcupdate.h>
@@ -69,7 +70,7 @@ 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);
+u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface);
bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface);
struct batadv_hard_iface*
batadv_hardif_get_by_netdev(const struct net_device *net_dev);
@@ -81,6 +82,8 @@ void batadv_update_min_mtu(struct net_device *mesh_iface);
void batadv_hardif_release(struct kref *ref);
int batadv_hardif_no_broadcast(struct batadv_hard_iface *if_outgoing,
u8 *orig_addr, u8 *orig_neigh);
+int __init batadv_wifi_net_devices_init(void);
+void batadv_wifi_net_devices_deinit(void);
/**
* batadv_hardif_put() - decrement the hard interface refcounter and possibly
@@ -119,4 +122,33 @@ batadv_primary_if_get_selected(struct batadv_priv *bat_priv)
return hard_iface;
}
+/**
+ * batadv_is_cfg80211() - check if the given hardif is a cfg80211
+ * wifi interface
+ * @wifi_flags: extracted batadv_hard_iface_wifi_flagss of an net_device
+ *
+ * Return: true if the net device is a cfg80211 wireless device, false
+ * otherwise.
+ */
+static inline bool batadv_is_cfg80211(u32 wifi_flags)
+{
+ u32 allowed_flags = 0;
+
+ allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
+ allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
+
+ return !!(wifi_flags & allowed_flags);
+}
+
+/**
+ * batadv_is_wifi() - check if flags belong to wifi interface
+ * @wifi_flags: extracted batadv_hard_iface_wifi_flagss of an net_device
+ *
+ * Return: true if the net device is a 802.11 wireless device, false otherwise.
+ */
+static inline bool batadv_is_wifi(u32 wifi_flags)
+{
+ return wifi_flags != 0;
+}
+
#endif /* _NET_BATMAN_ADV_HARD_INTERFACE_H_ */
diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index b7acccdf3ce05..a3993a8337648 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -105,8 +105,14 @@ static int __init batadv_init(void)
batadv_tp_meter_init();
batadv_event_workqueue = create_singlethread_workqueue("bat_events");
- if (!batadv_event_workqueue)
+ if (!batadv_event_workqueue) {
+ ret = -ENOMEM;
goto err_create_wq;
+ }
+
+ ret = batadv_wifi_net_devices_init();
+ if (ret < 0)
+ goto err_init_wifi;
register_netdevice_notifier(&batadv_hard_if_notifier);
rtnl_link_register(&batadv_link_ops);
@@ -117,10 +123,15 @@ static int __init batadv_init(void)
return 0;
+err_init_wifi:
+ destroy_workqueue(batadv_event_workqueue);
+ batadv_event_workqueue = NULL;
+ rcu_barrier();
+
err_create_wq:
batadv_tt_cache_destroy();
- return -ENOMEM;
+ return ret;
}
static void __exit batadv_exit(void)
@@ -134,6 +145,7 @@ static void __exit batadv_exit(void)
rcu_barrier();
+ batadv_wifi_net_devices_deinit();
batadv_tt_cache_destroy();
}
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index a01ee46d97f34..16fe843577421 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -21,6 +21,7 @@
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/netlink.h>
+#include <linux/rhashtable-types.h>
#include <linux/sched.h> /* for linux/wait.h */
#include <linux/skbuff.h>
#include <linux/spinlock.h>
@@ -166,6 +167,29 @@ enum batadv_hard_iface_wifi_flags {
BATADV_HARDIF_WIFI_CFG80211_INDIRECT = BIT(3),
};
+/**
+ * struct batadv_wifi_net_device_state - cache of wifi information of net_devices
+ */
+struct batadv_wifi_net_device_state {
+ /** @l: anchor in rhashtable */
+ struct rhash_head l;
+
+ /** @netdev: pointer to the net_device */
+ struct net_device *netdev;
+
+ /** @dev_tracker: device tracker for @netdev */
+ netdevice_tracker dev_tracker;
+
+ /**
+ * @wifi_flags: flags whether this is (directly or indirectly) a wifi
+ * interface
+ */
+ u32 wifi_flags;
+
+ /** @rcu: struct used for freeing in an RCU-safe manner */
+ struct rcu_head rcu;
+};
+
/**
* struct batadv_hard_iface - network device known to batman-adv
*/
@@ -181,12 +205,6 @@ struct batadv_hard_iface {
*/
u8 num_bcasts;
- /**
- * @wifi_flags: flags whether this is (directly or indirectly) a wifi
- * interface
- */
- u32 wifi_flags;
-
/** @net_dev: pointer to the net_device */
struct net_device *net_dev;
--
2.47.3
next prev parent reply other threads:[~2026-05-28 14:29 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-28 14:29 [PATCH net-next 00/15] pull request for net-next: batman-adv 2026-05-28 Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 01/15] batman-adv: drop batman-adv specific version Simon Wunderlich
2026-05-29 0:14 ` Jakub Kicinski
2026-05-29 7:06 ` Sven Eckelmann
2026-05-29 18:24 ` Jakub Kicinski
2026-05-29 19:23 ` Sven Eckelmann
2026-05-29 19:47 ` Jakub Kicinski
2026-05-28 14:29 ` [PATCH net-next 02/15] MAINTAINERS: Rename batman-adv T(ree) Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 03/15] MAINTAINERS: Don't send batman-adv patches to netdev Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 04/15] batman-adv: add missing includes Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 05/15] batman-adv: use atomic_xchg() for gw.reselect check Simon Wunderlich
2026-05-28 14:29 ` Simon Wunderlich [this message]
2026-05-28 14:29 ` [PATCH net-next 07/15] batman-adv: replace non-atomic meshif config fields with (READ|WRITE)_ONCE Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 08/15] batman-adv: replace non-atomic hardif " Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 09/15] batman-adv: replace non-atomic vlan " Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 10/15] batman-adv: replace non-atomic mesh state " Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 11/15] batman-adv: replace non-atomic packet_size_max " Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 12/15] batman-adv: replace non-atomic last_ttvn " Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 13/15] batman-adv: tt: replace open-coded overflow check with helper Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 14/15] batman-adv: tvlv: avoid unnecessary OGM buffer reallocations Simon Wunderlich
2026-05-28 14:29 ` [PATCH net-next 15/15] batman-adv: use neigh_node's orig_node only as id Simon Wunderlich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260528142924.329658-7-sw@simonwunderlich.de \
--to=sw@simonwunderlich.de \
--cc=b.a.t.m.a.n@lists.open-mesh.org \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sven@narfation.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox