batman-adv: create hardif only for netdevs that are part of a mesh

batman-adv is using netdev notifiers to create a hard_iface struct for
every Ethernet-like netdev in the system. These hardifs are tracked in a
global linked list, which results in a few performance issues:

Lookups in this list are O(n) in the total number of netdevs. As a
hardif is looked up when a netdev is removed, this also takes O(n) in
the number of netdevs, and removing n netdevs may take O(n^2). This
slowdown will always happen when the batman-adv module is loaded, no
mesh needs to be active.

With the hardif being referenced as iflink private data, the global list
is only needed for hardifs that are *not* part of a mesh (that is, the
hardif is unused). To prepare for removing the global list, only create
a hardif struct when an interface is added to a mesh and destroy it on
removal.

As adding/removing and enabling/disabling a hardif become one and the
same, batadv_hardif_add_interface() is merged into
batadv_hardif_enable_interface(), and batadv_hardif_remove_interface()
can be dropped altogether.

Signed-off-by: Nora Schiffer <neocturne@universe-factory.net>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Nora Schiffer 2026-06-28 17:07:28 +02:00 committed by Sven Eckelmann
parent 805185b7c7
commit fcba102e0e
No known key found for this signature in database
GPG Key ID: 4D0F772BD314F5CB
3 changed files with 44 additions and 91 deletions

View File

@ -723,33 +723,58 @@ batadv_hardif_deactivate_interface(struct batadv_hard_iface *hard_iface)
}
/**
* batadv_hardif_enable_interface() - Enslave hard interface to mesh interface
* @hard_iface: hard interface to add to mesh interface
* batadv_hardif_enable_interface() - Enslave interface to mesh interface
* @net_dev: netdev struct of the interface to add to mesh interface
* @mesh_iface: netdev struct of the mesh interface
*
* Return: 0 on success or negative error number in case of failure
*/
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface)
{
struct batadv_priv *bat_priv;
__be16 ethertype = htons(ETH_P_BATMAN);
int max_header_len = batadv_max_header_len();
struct batadv_hard_iface *hard_iface;
unsigned int required_mtu;
unsigned int hardif_mtu;
bool fragmentation;
int ret;
hardif_mtu = READ_ONCE(hard_iface->net_dev->mtu);
ASSERT_RTNL();
if (!batadv_is_valid_iface(net_dev))
return -EINVAL;
hardif_mtu = READ_ONCE(net_dev->mtu);
required_mtu = READ_ONCE(mesh_iface->mtu) + max_header_len;
if (hardif_mtu < ETH_MIN_MTU + max_header_len)
return -EINVAL;
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
goto out;
hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
if (!hard_iface)
return -ENOMEM;
kref_get(&hard_iface->refcount);
netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
hard_iface->net_dev = net_dev;
hard_iface->if_status = BATADV_IF_INACTIVE;
INIT_LIST_HEAD(&hard_iface->list);
INIT_HLIST_HEAD(&hard_iface->neigh_list);
mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
spin_lock_init(&hard_iface->neigh_list_lock);
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
WRITE_ONCE(hard_iface->hop_penalty, 0);
batadv_v_hardif_init(hard_iface);
netdev_hold(mesh_iface, &hard_iface->meshif_dev_tracker, GFP_ATOMIC);
hard_iface->mesh_iface = mesh_iface;
@ -764,9 +789,6 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (ret < 0)
goto err_upper;
hard_iface->if_status = BATADV_IF_INACTIVE;
kref_get(&hard_iface->refcount);
hard_iface->batman_adv_ptype.type = ethertype;
hard_iface->batman_adv_ptype.func = batadv_batman_skb_recv;
hard_iface->batman_adv_ptype.dev = hard_iface->net_dev;
@ -802,7 +824,9 @@ int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
if (bat_priv->algo_ops->iface.enabled)
bat_priv->algo_ops->iface.enabled(hard_iface);
out:
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
batadv_hardif_generation++;
return 0;
err_upper:
@ -823,15 +847,19 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
struct batadv_priv *bat_priv = netdev_priv(hard_iface->mesh_iface);
struct batadv_hard_iface *primary_if = NULL;
ASSERT_RTNL();
batadv_hardif_deactivate_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_INACTIVE)
goto out;
list_del_rcu(&hard_iface->list);
batadv_hardif_generation++;
batadv_info(hard_iface->mesh_iface, "Removing interface: %s\n",
hard_iface->net_dev->name);
dev_remove_pack(&hard_iface->batman_adv_ptype);
batadv_hardif_put(hard_iface);
primary_if = batadv_primary_if_get_selected(bat_priv);
if (hard_iface == primary_if) {
@ -844,7 +872,7 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
}
bat_priv->algo_ops->iface.disable(hard_iface);
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
/* delete all references to this hard_iface */
batadv_purge_orig_ref(bat_priv);
@ -865,63 +893,6 @@ void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface)
batadv_hardif_put(primary_if);
}
static struct batadv_hard_iface *
batadv_hardif_add_interface(struct net_device *net_dev)
{
struct batadv_hard_iface *hard_iface;
ASSERT_RTNL();
if (!batadv_is_valid_iface(net_dev))
return NULL;
hard_iface = kzalloc_obj(*hard_iface, GFP_ATOMIC);
if (!hard_iface)
return NULL;
netdev_hold(net_dev, &hard_iface->dev_tracker, GFP_ATOMIC);
hard_iface->net_dev = net_dev;
hard_iface->mesh_iface = NULL;
hard_iface->if_status = BATADV_IF_NOT_IN_USE;
INIT_LIST_HEAD(&hard_iface->list);
INIT_HLIST_HEAD(&hard_iface->neigh_list);
mutex_init(&hard_iface->bat_iv.ogm_buff_mutex);
spin_lock_init(&hard_iface->neigh_list_lock);
kref_init(&hard_iface->refcount);
hard_iface->num_bcasts = BATADV_NUM_BCASTS_DEFAULT;
if (batadv_is_wifi_hardif(hard_iface))
hard_iface->num_bcasts = BATADV_NUM_BCASTS_WIRELESS;
WRITE_ONCE(hard_iface->hop_penalty, 0);
batadv_v_hardif_init(hard_iface);
kref_get(&hard_iface->refcount);
list_add_tail_rcu(&hard_iface->list, &batadv_hardif_list);
batadv_hardif_generation++;
return hard_iface;
}
static void batadv_hardif_remove_interface(struct batadv_hard_iface *hard_iface)
{
ASSERT_RTNL();
/* first deactivate interface */
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
batadv_hardif_disable_interface(hard_iface);
if (hard_iface->if_status != BATADV_IF_NOT_IN_USE)
return;
hard_iface->if_status = BATADV_IF_TO_BE_REMOVED;
batadv_hardif_put(hard_iface);
}
/**
* batadv_hard_if_event_meshif() - Handle events for mesh interfaces
* @event: NETDEV_* event to handle
@ -1082,10 +1053,6 @@ static int batadv_hard_if_event(struct notifier_block *this,
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))
hard_iface = batadv_hardif_add_interface(net_dev);
if (!hard_iface)
goto out;
@ -1099,10 +1066,7 @@ static int batadv_hard_if_event(struct notifier_block *this,
break;
case NETDEV_UNREGISTER:
case NETDEV_PRE_TYPE_CHANGE:
list_del_rcu(&hard_iface->list);
batadv_hardif_generation++;
batadv_hardif_remove_interface(hard_iface);
batadv_hardif_disable_interface(hard_iface);
break;
case NETDEV_CHANGEMTU:
if (hard_iface->mesh_iface)

View File

@ -75,7 +75,7 @@ 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);
int batadv_hardif_enable_interface(struct batadv_hard_iface *hard_iface,
int batadv_hardif_enable_interface(struct net_device *net_dev,
struct net_device *mesh_iface);
void batadv_hardif_disable_interface(struct batadv_hard_iface *hard_iface);
int batadv_hardif_min_mtu(struct net_device *mesh_iface);

View File

@ -836,18 +836,7 @@ static int batadv_meshif_slave_add(struct net_device *dev,
struct net_device *slave_dev,
struct netlink_ext_ack *extack)
{
struct batadv_hard_iface *hard_iface;
int ret = -EINVAL;
hard_iface = batadv_hardif_get_by_netdev(slave_dev);
if (!hard_iface || hard_iface->mesh_iface)
goto out;
ret = batadv_hardif_enable_interface(hard_iface, dev);
out:
batadv_hardif_put(hard_iface);
return ret;
return batadv_hardif_enable_interface(slave_dev, dev);
}
/**