mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
batman-adv: extract netdev wifi detection information object
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>
This commit is contained in:
parent
9550dd11e3
commit
c055946584
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_DIRECT;
|
||||
allowed_flags |= BATADV_HARDIF_WIFI_CFG80211_INDIRECT;
|
||||
if (!hard_iface)
|
||||
return 0;
|
||||
|
||||
return !!(hard_iface->wifi_flags & allowed_flags);
|
||||
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 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,22 @@ 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
|
||||
*
|
||||
* Return: 0 on success, negative error code on failure
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user