VYPR
Unrated severityNVD Advisory· Published May 28, 2026

CVE-2026-46208

CVE-2026-46208

Description

In the Linux kernel, the following vulnerability has been resolved:

batman-adv: stop tp_meter sessions during mesh teardown

TP meter sessions remain linked on bat_priv->tp_list after the netlink request has already finished. When the mesh interface is removed, batadv_mesh_free() currently tears down the mesh without first draining these sessions.

A running sender thread or a late incoming tp_meter packet can then keep processing against a mesh instance which is already shutting down. Synchronize tp_meter with the mesh lifetime by stopping all active sessions from batadv_mesh_free() and waiting for sender threads to exit before teardown continues.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

The Linux kernel's batman-adv module fails to terminate active TP meter sessions during mesh interface removal, allowing use-after-free when a sender thread or late packet accesses freed mesh structures.

Vulnerability

In the Linux kernel's batman-adv module, TP meter (throughput meter) sessions remain linked on bat_priv->tp_list after a netlink request completes. When the mesh interface is removed, batadv_mesh_free() tears down the mesh without first draining these sessions. The flaw affects all kernel versions with the TP meter feature and is fixed by commit 79bc0eaeef2c [1].

Exploitation

An attacker with a local account on the system can initiate TP meter sessions via netlink requests. When the mesh interface is then deleted (e.g., by a privileged user or through network management scripts), the sender thread or a late incoming TP meter packet can continue processing against a mesh instance that is already partially freed, leading to a use-after-free condition [1].

Impact

A successful exploit can trigger a use-after-free in the kernel, potentially leading to a system crash (denial of service) or, in rare circumstances, local privilege escalation due to corrupted kernel memory [1].

Mitigation

The fix was committed to the Linux kernel stable tree as 79bc0eaeef2c [1]. Users should apply this patch or update to a kernel version that includes it. No workaround other than avoiding removal of the mesh interface while TP meter sessions are active is available; such sessions are otherwise unattended after netlink completion, making reliable avoidance difficult.

AI Insight generated on May 28, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

1

Patches

10
79bc0eaeef2c

batman-adv: stop tp_meter sessions during mesh teardown

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJiexun WangMay 15, 2026Fixed in 6.6.140via kernel-cna
4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index e8a4499155667b..18b32c39ed4bce 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -262,6 +262,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index 56831f9fb071f5..1951c50b8d176b 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the soft interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	del_timer_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index d6854c109cd291..788507b29f9a99 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1396,6 +1397,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
26dfeee8db81

batman-adv: stop tp_meter sessions during mesh teardown

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJiexun WangMay 15, 2026Fixed in 6.12.90via kernel-cna
4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 8e0f44c71696f6..e989a81084e926 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -263,6 +263,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index 56831f9fb071f5..1951c50b8d176b 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the soft interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	del_timer_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 85a50096f5b24d..c801d1db7a12f7 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1466,6 +1467,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
3d3cf6a7314a

batman-adv: stop tp_meter sessions during mesh teardown

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJiexun WangApr 27, 2026Fixed in 7.1-rc4via kernel-cna
4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index d9a80e459c2e4a..58ca59a2799ed1 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 8fc5fe0e9b0539..daa06f42115429 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
8634c1dbd73a

batman-adv: stop tp_meter sessions during mesh teardown

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJiexun WangApr 27, 2026Fixed in 7.0.9via kernel-cna
4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index d9a80e459c2e4a..58ca59a2799ed1 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 8fc5fe0e9b0539..daa06f42115429 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
03660dab86f9

batman-adv: stop tp_meter sessions during mesh teardown

https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.gitJiexun WangApr 27, 2026Fixed in 6.18.32via kernel-cna
4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index f3c4b5e8427291..190affbad3c949 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index ae1d7a8dc480fd..41c1d19f786bbb 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
79bc0eaeef2c

batman-adv: stop tp_meter sessions during mesh teardown

4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index e8a4499155667b..18b32c39ed4bce 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -262,6 +262,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index 56831f9fb071f5..1951c50b8d176b 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the soft interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	del_timer_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index d6854c109cd291..788507b29f9a99 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1396,6 +1397,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
26dfeee8db81

batman-adv: stop tp_meter sessions during mesh teardown

4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 8e0f44c71696f6..e989a81084e926 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -263,6 +263,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index 56831f9fb071f5..1951c50b8d176b 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the soft interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	del_timer_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 85a50096f5b24d..c801d1db7a12f7 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1466,6 +1467,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
8634c1dbd73a

batman-adv: stop tp_meter sessions during mesh teardown

4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index d9a80e459c2e4a..58ca59a2799ed1 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 8fc5fe0e9b0539..daa06f42115429 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
03660dab86f9

batman-adv: stop tp_meter sessions during mesh teardown

4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index f3c4b5e8427291..190affbad3c949 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index ae1d7a8dc480fd..41c1d19f786bbb 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    
3d3cf6a7314a

batman-adv: stop tp_meter sessions during mesh teardown

4 files changed · +82 19
  • net/batman-adv/main.c+1 0 modified
    diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
    index 3a35aadd8b4191..a4d33ee0fda59e 100644
    --- a/net/batman-adv/main.c
    +++ b/net/batman-adv/main.c
    @@ -249,6 +249,7 @@ void batadv_mesh_free(struct net_device *mesh_iface)
     	atomic_set(&bat_priv->mesh_state, BATADV_MESH_DEACTIVATING);
     
     	batadv_purge_outstanding_packets(bat_priv, NULL);
    +	batadv_tp_stop_all(bat_priv);
     
     	batadv_gw_node_free(bat_priv);
     
    
  • net/batman-adv/tp_meter.c+76 18 modified
    diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
    index d9a80e459c2e4a..58ca59a2799ed1 100644
    --- a/net/batman-adv/tp_meter.c
    +++ b/net/batman-adv/tp_meter.c
    @@ -12,6 +12,7 @@
     #include <linux/byteorder/generic.h>
     #include <linux/cache.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/container_of.h>
     #include <linux/err.h>
     #include <linux/etherdevice.h>
    @@ -365,23 +366,38 @@ static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
     }
     
     /**
    - * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    - * @bat_priv: the bat priv with all the mesh interface information
    - * @tp_vars: the private data of the current TP meter session to cleanup
    + * batadv_tp_list_detach() - remove tp session from mesh session list once
    + * @tp_vars: the private data of the current TP meter session
      */
    -static void batadv_tp_sender_cleanup(struct batadv_priv *bat_priv,
    -				     struct batadv_tp_vars *tp_vars)
    +static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
     {
    -	cancel_delayed_work(&tp_vars->finish_work);
    +	bool detached = false;
     
     	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    +	if (!hlist_unhashed(&tp_vars->list)) {
    +		hlist_del_init_rcu(&tp_vars->list);
    +		detached = true;
    +	}
     	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
     
    +	if (!detached)
    +		return;
    +
    +	atomic_dec(&tp_vars->bat_priv->tp_num);
    +
     	/* drop list reference */
     	batadv_tp_vars_put(tp_vars);
    +}
     
    -	atomic_dec(&tp_vars->bat_priv->tp_num);
    +/**
    + * batadv_tp_sender_cleanup() - cleanup sender data and drop and timer
    + * @tp_vars: the private data of the current TP meter session to cleanup
    + */
    +static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
    +{
    +	cancel_delayed_work_sync(&tp_vars->finish_work);
    +
    +	batadv_tp_list_detach(tp_vars);
     
     	/* kill the timer and remove its reference */
     	timer_delete_sync(&tp_vars->timer);
    @@ -886,7 +902,8 @@ out:
     	batadv_orig_node_put(orig_node);
     
     	batadv_tp_sender_end(bat_priv, tp_vars);
    -	batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +	batadv_tp_sender_cleanup(tp_vars);
    +	complete(&tp_vars->finished);
     
     	batadv_tp_vars_put(tp_vars);
     
    @@ -918,7 +935,8 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
     		batadv_tp_vars_put(tp_vars);
     
     		/* cleanup of failed tp meter variables */
    -		batadv_tp_sender_cleanup(bat_priv, tp_vars);
    +		batadv_tp_sender_cleanup(tp_vars);
    +		complete(&tp_vars->finished);
     		return;
     	}
     
    @@ -1024,6 +1042,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     	tp_vars->start_time = jiffies;
     
     	init_waitqueue_head(&tp_vars->more_bytes);
    +	init_completion(&tp_vars->finished);
     
     	spin_lock_init(&tp_vars->unacked_lock);
     	INIT_LIST_HEAD(&tp_vars->unacked_list);
    @@ -1126,14 +1145,7 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
     		   "Shutting down for inactivity (more than %dms) from %pM\n",
     		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
     
    -	spin_lock_bh(&tp_vars->bat_priv->tp_list_lock);
    -	hlist_del_rcu(&tp_vars->list);
    -	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
    -
    -	/* drop list reference */
    -	batadv_tp_vars_put(tp_vars);
    -
    -	atomic_dec(&bat_priv->tp_num);
    +	batadv_tp_list_detach(tp_vars);
     
     	spin_lock_bh(&tp_vars->unacked_lock);
     	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
    @@ -1496,6 +1508,52 @@ out:
     	consume_skb(skb);
     }
     
    +/**
    + * batadv_tp_stop_all() - stop all currently running tp meter sessions
    + * @bat_priv: the bat priv with all the mesh interface information
    + */
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv)
    +{
    +	struct batadv_tp_vars *tp_vars[BATADV_TP_MAX_NUM];
    +	struct batadv_tp_vars *tp_var;
    +	size_t count = 0;
    +	size_t i;
    +
    +	spin_lock_bh(&bat_priv->tp_list_lock);
    +	hlist_for_each_entry(tp_var, &bat_priv->tp_list, list) {
    +		if (WARN_ON_ONCE(count >= BATADV_TP_MAX_NUM))
    +			break;
    +
    +		if (!kref_get_unless_zero(&tp_var->refcount))
    +			continue;
    +
    +		tp_vars[count++] = tp_var;
    +	}
    +	spin_unlock_bh(&bat_priv->tp_list_lock);
    +
    +	for (i = 0; i < count; i++) {
    +		tp_var = tp_vars[i];
    +
    +		switch (tp_var->role) {
    +		case BATADV_TP_SENDER:
    +			batadv_tp_sender_shutdown(tp_var,
    +						  BATADV_TP_REASON_CANCEL);
    +			wake_up(&tp_var->more_bytes);
    +			wait_for_completion(&tp_var->finished);
    +			break;
    +		case BATADV_TP_RECEIVER:
    +			batadv_tp_list_detach(tp_var);
    +			if (timer_shutdown_sync(&tp_var->timer))
    +				batadv_tp_vars_put(tp_var);
    +			break;
    +		}
    +
    +		batadv_tp_vars_put(tp_var);
    +	}
    +
    +	synchronize_net();
    +}
    +
     /**
      * batadv_tp_meter_init() - initialize global tp_meter structures
      */
    
  • net/batman-adv/tp_meter.h+1 0 modified
    diff --git a/net/batman-adv/tp_meter.h b/net/batman-adv/tp_meter.h
    index f0046d366eac65..4e97cd10cd0259 100644
    --- a/net/batman-adv/tp_meter.h
    +++ b/net/batman-adv/tp_meter.h
    @@ -17,6 +17,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
     		     u32 test_length, u32 *cookie);
     void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
     		    u8 return_value);
    +void batadv_tp_stop_all(struct batadv_priv *bat_priv);
     void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb);
     
     #endif /* _NET_BATMAN_ADV_TP_METER_H_ */
    
  • net/batman-adv/types.h+4 1 modified
    diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
    index 8fc5fe0e9b0539..daa06f42115429 100644
    --- a/net/batman-adv/types.h
    +++ b/net/batman-adv/types.h
    @@ -14,6 +14,7 @@
     #include <linux/average.h>
     #include <linux/bitops.h>
     #include <linux/compiler.h>
    +#include <linux/completion.h>
     #include <linux/if.h>
     #include <linux/if_ether.h>
     #include <linux/kref.h>
    @@ -1328,6 +1329,9 @@ struct batadv_tp_vars {
     	/** @finish_work: work item for the finishing procedure */
     	struct delayed_work finish_work;
     
    +	/** @finished: completion signaled when a sender thread exits */
    +	struct completion finished;
    +
     	/** @test_length: test length in milliseconds */
     	u32 test_length;
     
    -- 
    cgit 1.3-korg
    
    
    

Vulnerability mechanics

Root cause

"Missing lifecycle synchronization: TP meter sessions are not drained during mesh teardown, allowing a running sender thread or late incoming packet to access a shutting-down mesh instance."

Attack vector

An attacker who can initiate a batman-adv throughput meter (TP meter) session via netlink can trigger a use-after-free or other memory safety issue by removing the mesh interface while a sender thread is still running or while a late incoming tp_meter packet is being processed [patch_id=2897744]. The TP meter sessions remain linked on `bat_priv->tp_list` after the netlink request finishes, and `batadv_mesh_free()` previously tore down the mesh without first draining these sessions. A running sender thread or a late incoming tp_meter packet can then keep processing against a mesh instance that is already shutting down, leading to undefined behavior.

Affected code

The vulnerability is in `net/batman-adv/tp_meter.c` where TP meter sessions (both sender and receiver) remain linked on `bat_priv->tp_list` after the netlink request finishes. The teardown path in `batadv_mesh_free()` in `net/batman-adv/main.c` did not drain these sessions before proceeding with mesh destruction [patch_id=2897744].

What the fix does

The patch adds a new function `batadv_tp_stop_all()` in `net/batman-adv/tp_meter.c` that iterates all active TP meter sessions, cancels sender threads (waiting for their `finished` completion), detaches receiver sessions, and shuts down their timers [patch_id=2897744]. This function is called from `batadv_mesh_free()` in `net/batman-adv/main.c` before any other teardown proceeds. A new `struct completion finished` field is added to `struct batadv_tp_vars` in `net/batman-adv/types.h`, and sender threads signal completion via `complete(&tp_vars->finished)` after cleanup. The helper `batadv_tp_list_detach()` is also introduced to safely remove a session from the list with idempotency checks, replacing open-coded list deletion in multiple places.

Preconditions

  • inputThe attacker must be able to initiate a batman-adv throughput meter (TP meter) session via netlink on the mesh interface.
  • inputThe mesh interface must be removed (e.g., via `ip link delete`) while a TP meter session is active or shortly after it finishes.

Generated on May 28, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.