mirror of
https://github.com/torvalds/linux.git
synced 2026-05-30 10:04:04 +02:00
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:
parent
3243543592
commit
3d3cf6a731
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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_ */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user