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.

Fixes: 33a3bb4a33 ("batman-adv: throughput meter implementation")
Cc: stable@kernel.org
Reported-by: Yuan Tan <yuantan098@gmail.com>
Reported-by: Yifan Wu <yifanwucs@gmail.com>
Reported-by: Juefei Pu <tomapufckgml@gmail.com>
Reported-by: Xin Liu <bird@lzu.edu.cn>
Co-developed-by: Luxing Yin <tr0jan@lzu.edu.cn>
Signed-off-by: Luxing Yin <tr0jan@lzu.edu.cn>
Signed-off-by: Jiexun Wang <wangjiexun2025@gmail.com>
Signed-off-by: Ren Wei <n05ec@lzu.edu.cn>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
This commit is contained in:
Jiexun Wang 2026-04-27 14:43:34 +08:00 committed by Sven Eckelmann
parent 3243543592
commit 3d3cf6a731
No known key found for this signature in database
GPG Key ID: 4D0F772BD314F5CB
4 changed files with 82 additions and 18 deletions

View File

@ -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);

View File

@ -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 @@ static int batadv_tp_send(void *arg)
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 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
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
*/

View File

@ -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_ */

View File

@ -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;