mirror of
https://github.com/torvalds/linux.git
synced 2026-09-29 21:04:02 +02:00
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:
parent
805185b7c7
commit
fcba102e0e
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user