FAILED: patch "[PATCH] batman-adv: tp_meter: fix race condition in send error" failed to apply to 6.1-stable tree
Subsystems (2)
[email protected]
maintainerThe patch below does not apply to the 6.1-stable tree.
If someone wants it applied there, or to any other stable or longterm tree, then please email the backport, including the original git commit id to <[email protected]>.
To reproduce the conflict and resubmit, you may use the following commands:
git fetch https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/ linux-6.1.y
git checkout FETCH_HEAD
git cherry-pick -x 71dce47f0758537fff78fddb5fb0d4632d29b29f
# <resolve conflicts, build, test, etc.>
git commit -s
git send-email --to '<[email protected]>' --in-reply-to '2026052833-easing-gerbil-ae19@gregkh' --subject-prefix 'PATCH 6.1.y' HEAD^..
Possible dependencies:
thanks,
greg k-h
------------------ original commit in Linus's tree ------------------
From 71dce47f0758537fff78fddb5fb0d4632d29b29f Mon Sep 17 00:00:00 2001
From: Sven Eckelmann <[email protected]>
Date: Wed, 13 May 2026 23:38:54 +0200
Subject: [PATCH] batman-adv: tp_meter: fix race condition in send error
reporting
batadv_tp_sender_shutdown() previously used two separate variables to track session state: sending (an atomic flag indicating whether the session was active) and reason (a plain enum storing the stop reason). This introduced a race window between the two writes: after sending was cleared to 0, batadv_tp_send() could observe the stopped state and call batadv_tp_sender_end() before reason was written, causing the wrong stop reason to be reported to the caller.
Fix this by consolidating both variables into a single atomic send_result, which holds 0 while the session is running and the stop reason once it ends.
net/batman-adv/tp_meter.c +25 -15
@@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)static void batadv_tp_sender_end (struct batadv_priv *bat_priv,struct batadv_tp_vars *tp_vars) { +enum batadv_tp_meter_reason reason;u32 session_cookie; + reason =atomic_read (&tp_vars->send_result); +batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Test towards %pM finished..shutting down (reason=%d)\n" , - tp_vars->other_end, tp_vars->reason); + tp_vars->other_end, reason); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
@@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, session_cookie =batadv_tp_session_cookie (tp_vars->session, tp_vars->icmp_uid); - batadv_tp_batctl_notify(tp_vars->reason, + batadv_tp_batctl_notify(reason, tp_vars->other_end, bat_priv, tp_vars->start_time,
@@ -446,10 +449,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,static void batadv_tp_sender_shutdown (struct batadv_tp_vars *tp_vars,enum batadv_tp_meter_reason reason) { -if (atomic_xchg (&tp_vars->sending,0 ) !=1 ) -return ; +atomic_cmpxchg (&tp_vars->send_result,0 , reason); +} - tp_vars->reason = reason; +/** +* batadv_tp_sender_stopped() - check if tp session was stopped with reason +* @tp_vars: the private data of the current TP meter session +* +* Return: whether stop reason was found +*/ +static bool batadv_tp_sender_stopped (struct batadv_tp_vars *tp_vars) +{ +return atomic_read (&tp_vars->send_result) !=0 ; }/**
@@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)/* most of the time this function is invoked while normal packet * reception... */ -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars)))/* timer ref will be dropped in batadv_tp_sender_cleanup */ return ;
@@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)struct batadv_tp_vars *tp_vars =timer_container_of (tp_vars, t, timer);struct batadv_priv *bat_priv = tp_vars->bat_priv; -if (atomic_read (&tp_vars->sending) ==0 ) +if (batadv_tp_sender_stopped (tp_vars))return ;/* if the user waited long enough...shutdown the test */
@@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) goto out; -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars))) goto out;/* old ACK? silently drop it.. */
@@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg)if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } orig_node =batadv_orig_hash_find (bat_priv, tp_vars->other_end);if (unlikely (!orig_node)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } primary_if =batadv_primary_if_get_selected (bat_priv);if (unlikely (!primary_if)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; }
@@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg)queue_delayed_work (batadv_event_workqueue, &tp_vars->finish_work,msecs_to_jiffies (tp_vars->test_length)); -while (atomic_read (&tp_vars->sending) !=0 ) { +while (!batadv_tp_sender_stopped (tp_vars)) {if (unlikely (!batadv_tp_avail (tp_vars, payload_len))) {batadv_tp_wait_available (tp_vars, payload_len);continue ;
@@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg)"Meter: %s() cannot send packets (%d)\n" , __func__, err);/* ensure nobody else tries to stop the thread now */ -if (atomic_xchg (&tp_vars->sending,0 ) ==1 ) - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err);break ; }
@@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,ether_addr_copy (tp_vars->other_end, dst);kref_init (&tp_vars->refcount); tp_vars->role =BATADV_TP_SENDER ; -atomic_set (&tp_vars->sending,1 ); +atomic_set (&tp_vars->send_result,0 );memcpy (tp_vars->session, session_id,sizeof (session_id)); tp_vars->icmp_uid = icmp_uid;
net/batman-adv/types.h +5 -5
@@ -1320,15 +1320,15 @@ struct batadv_tp_vars {/** @role: receiver/sender modi */ enum batadv_tp_meter_role role; -/** @sending: sending binary semaphore: 1 if sending, 0 is not */ -atomic_t sending; +/** +* @send_result: 0 when sending is ongoing and otherwise +* enum batadv_tp_meter_reason +*/ +atomic_t send_result;/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ atomic_t receiving; -/** @reason: reason for a stopped session */ -enum batadv_tp_meter_reason reason; -/** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work;
Sven Eckelmann
maintainerPATCH 1/2 batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown
commit 77098e4bea37af51d3962efa88a5af2ea5e1ac57 upstream.
The receiver shutdown timer handler, batadv_tp_receiver_shutdown(), is responsible for releasing the tp_vars reference it holds. However, the existing logic for coordinating this release with batadv_tp_stop_all() was flawed.
timer_shutdown_sync() guarantees the timer will not fire again after it returns, but it returns non-zero only when the timer was pending at the time of the call. If the timer had already expired (and batadv_tp_stop_all() would unsucessfully try to rearm itself), batadv_tp_stop_all() skips its batadv_tp_vars_put(), and batadv_tp_receiver_shutdown() fails to put its own reference as well.
Fix this by introducing a new atomic variable receiving that is set to 1 when the receiver is initialized and cleared atomically with atomic_xchg() by whichever side claims it first. Only the side that observes the transition from 1 to 0 is responsible for releasing the tp_vars timer reference, eliminating the uncertainty.
--- net/batman-adv/tp_meter.c | 13 +++++++++++-- net/batman-adv/types.h | 3 +++ 2 files changed, 14 insertions(+), 2 deletions(-)
net/batman-adv/tp_meter.c +11 -2
@@ -8,6 +8,7 @@#include "main.h" #include <linux/atomic.h> +#include <linux/bug.h> #include <linux/build_bug.h> #include <linux/byteorder/generic.h> #include <linux/cache.h>
@@ -1157,6 +1158,9 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)spin_unlock_bh (&tp_vars->unacked_lock);/* drop reference of timer */ +if (WARN_ON (atomic_xchg (&tp_vars->receiving,0 ) !=1 )) +return ; +batadv_tp_vars_put (tp_vars); }
@@ -1375,6 +1379,7 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,ether_addr_copy (tp_vars->other_end, icmp->orig); tp_vars->role =BATADV_TP_RECEIVER ; +atomic_set (&tp_vars->receiving,1 );memcpy (tp_vars->session, icmp->session,sizeof (tp_vars->session)); tp_vars->last_recv =BATADV_TP_FIRST_SEQ ; tp_vars->bat_priv = bat_priv;
@@ -1547,8 +1552,12 @@ void batadv_tp_stop_all(struct batadv_priv *bat_priv)break ;case BATADV_TP_RECEIVER :batadv_tp_list_detach (tp_var); -if (timer_shutdown_sync (&tp_var->timer)) -batadv_tp_vars_put (tp_var); +timer_shutdown_sync (&tp_var->timer); + +if (atomic_xchg (&tp_var->receiving,0 ) !=1 ) +break ; + +batadv_tp_vars_put (tp_var);break ; }
net/batman-adv/types.h +3 -0
@@ -1391,6 +1391,9 @@ struct batadv_tp_vars {/** @sending: sending binary semaphore: 1 if sending, 0 is not */ atomic_t sending; +/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ +atomic_t receiving; +/** @reason: reason for a stopped session */ enum batadv_tp_meter_reason reason;
Signature · 1 line
2.47.3
Sven Eckelmann
maintainerPATCH 2/2 batman-adv: tp_meter: fix race condition in send error reporting
commit 71dce47f0758537fff78fddb5fb0d4632d29b29f upstream.
batadv_tp_sender_shutdown() previously used two separate variables to track session state: sending (an atomic flag indicating whether the session was active) and reason (a plain enum storing the stop reason). This introduced a race window between the two writes: after sending was cleared to 0, batadv_tp_send() could observe the stopped state and call batadv_tp_sender_end() before reason was written, causing the wrong stop reason to be reported to the caller.
Fix this by consolidating both variables into a single atomic send_result, which holds 0 while the session is running and the stop reason once it ends.
--- net/batman-adv/tp_meter.c | 40 ++++++++++++++++++++++++--------------- net/batman-adv/types.h | 10 +++++----- 2 files changed, 30 insertions(+), 20 deletions(-)
net/batman-adv/tp_meter.c +25 -15
@@ -413,11 +413,14 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)static void batadv_tp_sender_end (struct batadv_priv *bat_priv,struct batadv_tp_vars *tp_vars) { +enum batadv_tp_meter_reason reason;u32 session_cookie; + reason =atomic_read (&tp_vars->send_result); +batadv_dbg (BATADV_DBG_TP_METER , bat_priv,"Test towards %pM finished..shutting down (reason=%d)\n" , - tp_vars->other_end, tp_vars->reason); + tp_vars->other_end, reason); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
@@ -430,7 +433,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv, session_cookie =batadv_tp_session_cookie (tp_vars->session, tp_vars->icmp_uid); - batadv_tp_batctl_notify(tp_vars->reason, + batadv_tp_batctl_notify(reason, tp_vars->other_end, bat_priv, tp_vars->start_time,
@@ -446,10 +449,18 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,static void batadv_tp_sender_shutdown (struct batadv_tp_vars *tp_vars,enum batadv_tp_meter_reason reason) { -if (atomic_xchg (&tp_vars->sending,0 ) !=1 ) -return ; +atomic_cmpxchg (&tp_vars->send_result,0 , reason); +} - tp_vars->reason = reason; +/** +* batadv_tp_sender_stopped() - check if tp session was stopped with reason +* @tp_vars: the private data of the current TP meter session +* +* Return: whether stop reason was found +*/ +static bool batadv_tp_sender_stopped (struct batadv_tp_vars *tp_vars) +{ +return atomic_read (&tp_vars->send_result) !=0 ; }/**
@@ -479,7 +490,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)/* most of the time this function is invoked while normal packet * reception... */ -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars)))/* timer ref will be dropped in batadv_tp_sender_cleanup */ return ;
@@ -499,7 +510,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)struct batadv_tp_vars *tp_vars =from_timer (tp_vars, t, timer);struct batadv_priv *bat_priv = tp_vars->bat_priv; -if (atomic_read (&tp_vars->sending) ==0 ) +if (batadv_tp_sender_stopped (tp_vars))return ;/* if the user waited long enough...shutdown the test */
@@ -661,7 +672,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) goto out; -if (unlikely (atomic_read (&tp_vars->sending) ==0 )) +if (unlikely (batadv_tp_sender_stopped (tp_vars))) goto out;/* old ACK? silently drop it.. */
@@ -827,21 +838,21 @@ static int batadv_tp_send(void *arg)if (unlikely (tp_vars->role !=BATADV_TP_SENDER )) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } orig_node =batadv_orig_hash_find (bat_priv, tp_vars->other_end);if (unlikely (!orig_node)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; } primary_if =batadv_primary_if_get_selected (bat_priv);if (unlikely (!primary_if)) { err =BATADV_TP_REASON_DST_UNREACHABLE ; - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err); goto out; }
@@ -860,7 +871,7 @@ static int batadv_tp_send(void *arg)queue_delayed_work (batadv_event_workqueue, &tp_vars->finish_work,msecs_to_jiffies (tp_vars->test_length)); -while (atomic_read (&tp_vars->sending) !=0 ) { +while (!batadv_tp_sender_stopped (tp_vars)) {if (unlikely (!batadv_tp_avail (tp_vars, payload_len))) {batadv_tp_wait_available (tp_vars, payload_len);continue ;
@@ -883,8 +894,7 @@ static int batadv_tp_send(void *arg)"Meter: %s() cannot send packets (%d)\n" , __func__, err);/* ensure nobody else tries to stop the thread now */ -if (atomic_xchg (&tp_vars->sending,0 ) ==1 ) - tp_vars->reason = err; +batadv_tp_sender_shutdown (tp_vars, err);break ; }
@@ -1006,7 +1016,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,ether_addr_copy (tp_vars->other_end, dst);kref_init (&tp_vars->refcount); tp_vars->role =BATADV_TP_SENDER ; -atomic_set (&tp_vars->sending,1 ); +atomic_set (&tp_vars->send_result,0 );memcpy (tp_vars->session, session_id,sizeof (session_id)); tp_vars->icmp_uid = icmp_uid;
net/batman-adv/types.h +5 -5
@@ -1388,15 +1388,15 @@ struct batadv_tp_vars {/** @role: receiver/sender modi */ enum batadv_tp_meter_role role; -/** @sending: sending binary semaphore: 1 if sending, 0 is not */ -atomic_t sending; +/** +* @send_result: 0 when sending is ongoing and otherwise +* enum batadv_tp_meter_reason +*/ +atomic_t send_result;/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */ atomic_t receiving; -/** @reason: reason for a stopped session */ -enum batadv_tp_meter_reason reason; -/** @finish_work: work item for the finishing procedure */ struct delayed_work finish_work;
Signature · 1 line
2.47.3
Sven Eckelmann
maintainerRe: [PATCH 6.1.y 2/2] batman-adv: tp_meter: fix race condition in send error reporting
Quoted 2 lines · On Friday, 29 May 2026 21:49:08 CEST Sven Eckelmann wrote:
Cc <[email protected]> # 6.6.x: 5c1bf8d batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdownCc <[email protected]> # 6.6.x
Grml, meant 6.1.x. But I've sent the required patch too.
Btw. if these dependencies between patches annd the correct order causes
headaches: I have everything in
https://git.open-mesh.org/batadv.git/log/?h=batadv/lts/6.1 (and similar branches) and can easily rebase them on top of stable-rc.git/queue/6.1 and sent the remaining patches out as a patchset for a specific stable kernel (like 6.1).
Regards,
Sven
Re: [PATCH 6.1.y 1/2] batman-adv: tp_meter: fix tp_vars reference leak in receiver shutdown
In reply to Sven EckelmannQueued for 5.10.y, thanks.
Signature · 2 lines
Thanks, Sasha