B.A.T.M.A.N Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups
@ 2026-05-31  8:28 Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 1/6] batman-adv: tp_meter: keep unacked list in ascending ordered Sven Eckelmann
                   ` (5 more replies)
  0 siblings, 6 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

After most of the problems regarding the role confusion were fixed, we
should use the opportunity to split the variables completly. This can
prevent some of the confusions and at the same time reduce the memory
consumption significantly.

Since the sender variables are separated from the rest, also clean up the
congestion control code. Make sure that everything is in short blocks which
ensure that the reader is aware of the locking and the results of the
calculation. Which also means that the actions (sending) are extracted from
the congestion control code.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
Changes in v4:
- rebase
- add patch to fix unacked_list order
- add patch to initialize dup_acks explicitly
- extract batadv_tp_avail fix to separate patch
- split congestion control variable patch in "locking" and "structure"
  patch
- Link to v3: https://patch.msgid.link/20260521-tp-reason-missing-v3-0-26855460087a@narfation.org

Changes in v3:
- rebase patches
- fix receiver cleanup "put"
- add patch to cleanup the congestion control locking
- Link to v2: https://patch.msgid.link/20260519-tp-reason-missing-v2-0-9c5cd1824e3b@narfation.org

Changes in v2:
- fix send_result kernel-doc
- rebase patch
- add patch to avoid role mixup in search functions
- add patch to split sender and receiver sessions
- Link to v1: https://patch.msgid.link/20260513-tp-reason-missing-v1-1-752a882bd61a@narfation.org

---
Sven Eckelmann (6):
      batman-adv: tp_meter: keep unacked list in ascending ordered
      batman-adv: tp_meter: initialize dup_acks explicitly
      batman-adv: tp_meter: avoid window underflow
      batman-adv: tp_meter: split vars into sender and receiver types
      batman-adv: tp_meter: use locking for all congestion control variables
      batman-adv: tp_meter: consolidate congestion control variables

 net/batman-adv/main.c     |   3 +-
 net/batman-adv/tp_meter.c | 724 +++++++++++++++++++++++++++-------------------
 net/batman-adv/types.h    | 145 +++++-----
 3 files changed, 503 insertions(+), 369 deletions(-)
---
base-commit: 80ee59dc80a8d6287b2af80dad4e7e1c04d3a2cf
change-id: 20260513-tp-reason-missing-f1aa51f3f374

Best regards,
--  
Sven Eckelmann <sven@narfation.org>


^ permalink raw reply	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 1/6] batman-adv: tp_meter: keep unacked list in ascending ordered
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 2/6] batman-adv: tp_meter: initialize dup_acks explicitly Sven Eckelmann
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

When batadv_tp_handle_out_of_order inserts a new entry in the list of
unacked (out of order) packets, it searches from the entry with the newest
sequence number towards oldest sequence number. If an entry is found which
is older than the newly entry, the new entry has to be added after the
found one to keep the ascending order.

But for this operation list_add_tail() was used. But this function adds an
entry _before_ another one. As result, the list would contain a lot of
swapped sequence numbers. The consumer of this list
(batadv_tp_ack_unordered()) would then fail to correctly ack packets.

Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/tp_meter.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 0fc4ca78..e8941f75 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -1325,7 +1325,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
 		 * one is attached _after_ it. In this way the list is kept in
 		 * ascending order
 		 */
-		list_add_tail(&new->list, &un->list);
+		list_add(&new->list, &un->list);
 		added = true;
 		break;
 	}

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 2/6] batman-adv: tp_meter: initialize dup_acks explicitly
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 1/6] batman-adv: tp_meter: keep unacked list in ascending ordered Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 3/6] batman-adv: tp_meter: avoid window underflow Sven Eckelmann
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

When an ack with a sequence number equal to the last_acked is received, the
dup_acks counter is increased to decide whether fast retransmit should be
performed. Only when the sequence numbers are not equal, the dup_acks is
set to the initial value (0).

But if the initial packet would have the sequence number
BATADV_TP_FIRST_SEQ, dup_acks would not be initialized and atomic_inc would
operate on an undefined starting value. It is therefore required to have it
explicitly initialized during the start of the sender session.

Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/tp_meter.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index e8941f75..0325b951 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -1045,6 +1045,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	tp_vars->icmp_uid = icmp_uid;
 
 	tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
+	atomic_set(&tp_vars->dup_acks, 0);
 	atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
 	tp_vars->fast_recovery = false;
 	tp_vars->recover = BATADV_TP_FIRST_SEQ;

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 3/6] batman-adv: tp_meter: avoid window underflow
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 1/6] batman-adv: tp_meter: keep unacked list in ascending ordered Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 2/6] batman-adv: tp_meter: initialize dup_acks explicitly Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 4/6] batman-adv: tp_meter: split vars into sender and receiver types Sven Eckelmann
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

In batadv_tp_avail(), win_left is calculated with 32-bit unsigned
arithmetic: win_left = win_limit - tp_vars->last_sent;

During Fast Recovery, cwnd is inflated and last_sent advances rapidly. When
Fast Recovery ends, cwnd drops abruptly back to ss_threshold. If the newly
shrunk win_limit is less than last_sent, the unsigned subtraction will
underflow, wrapping to a massive positive value. Instead of returning that
the window is full (unavailable), it returns that the sender can continue
sending.

To handle this situation, it must be checked whether the windows end
sequence number (win_limit) has to be compared with the last sent sequence
number. If it would be before the last sent sequence number, then more acks
are needed before the transmission can be started again.

Fixes: 98d7a766b645 ("batman-adv: throughput meter implementation")
Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/tp_meter.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 0325b951..09e46b04 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -817,10 +817,15 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
 			    size_t payload_len)
 {
+	u32 last_sent = tp_vars->last_sent;
 	u32 win_left, win_limit;
 
 	win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
-	win_left = win_limit - tp_vars->last_sent;
+
+	if (batadv_seq_before(last_sent, win_limit))
+		win_left = win_limit - last_sent;
+	else
+		win_left = 0;
 
 	return win_left >= payload_len;
 }

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 4/6] batman-adv: tp_meter: split vars into sender and receiver types
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
                   ` (2 preceding siblings ...)
  2026-05-31  8:28 ` [PATCH batadv v4 3/6] batman-adv: tp_meter: avoid window underflow Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 6/6] batman-adv: tp_meter: consolidate " Sven Eckelmann
  5 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

The monolithic batadv_tp_vars struct holds fields for both sender and
receiver roles, distinguished only by a runtime enum role. This makes it
easy to accidentally access a field intended for the opposite role, since
neither the compiler nor the type system provide any guard against such
mistakes. The role check also adds unnecessary branching in several code
paths.

Introduce batadv_tp_vars_common to hold fields shared across both roles,
then derive two separate types (sender/receiver) from it. The functions can
operate on them without any ambiguity about the available fields. This also
reduces the memory footprint of receiver sessions, which no longer carry
the substantial sender-only fields.

Care must be taken to prevent concurrent TP sessions between the same two
peers in opposite directions, since sender and receiver sessions are now
tracked in separate lists and a lookup in one list no longer detects a
session in the other.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/main.c     |   3 +-
 net/batman-adv/tp_meter.c | 396 ++++++++++++++++++++++++++--------------------
 net/batman-adv/types.h    | 101 ++++++------
 3 files changed, 281 insertions(+), 219 deletions(-)

diff --git a/net/batman-adv/main.c b/net/batman-adv/main.c
index a4d33ee0..fc80f958 100644
--- a/net/batman-adv/main.c
+++ b/net/batman-adv/main.c
@@ -181,7 +181,8 @@ int batadv_mesh_init(struct net_device *mesh_iface)
 	INIT_HLIST_HEAD(&bat_priv->tvlv.container_list);
 	INIT_HLIST_HEAD(&bat_priv->tvlv.handler_list);
 	INIT_HLIST_HEAD(&bat_priv->meshif_vlan_list);
-	INIT_HLIST_HEAD(&bat_priv->tp_list);
+	INIT_HLIST_HEAD(&bat_priv->tp_sender_list);
+	INIT_HLIST_HEAD(&bat_priv->tp_receiver_list);
 
 	bat_priv->gw.generation = 0;
 
diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index 09e46b04..c8afd1e0 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -142,7 +142,7 @@ static u32 batadv_tp_cwnd(u32 base, u32 increment, u32 min)
  * 2) if the session is in Congestion Avoidance, the CWND has to be
  * increased by MSS * MSS / CWND for every unique received ACK
  */
-static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
+static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss)
 {
 	spin_lock_bh(&tp_vars->cwnd_lock);
 
@@ -173,7 +173,7 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss)
  * @tp_vars: the private data of the current TP meter session
  * @new_rtt: new roundtrip time in msec
  */
-static void batadv_tp_update_rto(struct batadv_tp_vars *tp_vars,
+static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars,
 				 u32 new_rtt)
 {
 	long m = new_rtt;
@@ -252,35 +252,30 @@ static void batadv_tp_batctl_error_notify(enum batadv_tp_meter_reason reason,
 }
 
 /**
- * batadv_tp_list_find() - find a tp_vars object in the global list
+ * batadv_tp_list_find_sender() - find a sender tp_vars object in the global list
  * @bat_priv: the bat priv with all the mesh interface information
  * @dst: the other endpoint MAC address to look for
- * @role: role of the session
  *
  * Look for a tp_vars object matching dst as end_point and return it after
  * having increment the refcounter. Return NULL is not found
  *
  * Return: matching tp_vars or NULL when no tp_vars with @dst was found
  */
-static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
-						  const u8 *dst,
-						  enum batadv_tp_meter_role role)
+static struct batadv_tp_sender *
+batadv_tp_list_find_sender(struct batadv_priv *bat_priv, const u8 *dst)
 {
-	struct batadv_tp_vars *pos, *tp_vars = NULL;
+	struct batadv_tp_sender *pos, *tp_vars = NULL;
 
 	rcu_read_lock();
-	hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) {
-		if (!batadv_compare_eth(pos->other_end, dst))
-			continue;
-
-		if (pos->role != role)
+	hlist_for_each_entry_rcu(pos, &bat_priv->tp_sender_list, common.list) {
+		if (!batadv_compare_eth(pos->common.other_end, dst))
 			continue;
 
 		/* most of the time this function is invoked during the normal
 		 * process..it makes sens to pay more when the session is
 		 * finished and to speed the process up during the measurement
 		 */
-		if (unlikely(!kref_get_unless_zero(&pos->refcount)))
+		if (unlikely(!kref_get_unless_zero(&pos->common.refcount)))
 			continue;
 
 		tp_vars = pos;
@@ -301,10 +296,16 @@ static struct batadv_tp_vars *batadv_tp_list_find(struct batadv_priv *bat_priv,
 static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
 	__must_hold(&bat_priv->tp_list_lock)
 {
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_receiver *tp_receiver;
+	struct batadv_tp_sender *tp_sender;
 
-	hlist_for_each_entry_rcu(tp_vars, &bat_priv->tp_list, list) {
-		if (batadv_compare_eth(tp_vars->other_end, dst))
+	hlist_for_each_entry_rcu(tp_sender, &bat_priv->tp_sender_list, common.list) {
+		if (batadv_compare_eth(tp_sender->common.other_end, dst))
+			return true;
+	}
+
+	hlist_for_each_entry_rcu(tp_receiver, &bat_priv->tp_receiver_list, common.list) {
+		if (batadv_compare_eth(tp_receiver->common.other_end, dst))
 			return true;
 	}
 
@@ -312,12 +313,11 @@ static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
 }
 
 /**
- * batadv_tp_list_find_session() - find tp_vars session object in the global
- *  list
+ * batadv_tp_list_find_sender_session() - find tp_vars sender session
+ *  object in the global list
  * @bat_priv: the bat priv with all the mesh interface information
  * @dst: the other endpoint MAC address to look for
  * @session: session identifier
- * @role: role of the session
  *
  * Look for a tp_vars object matching dst as end_point, session as tp meter
  * session and return it after having increment the refcounter. Return NULL
@@ -325,28 +325,25 @@ static bool batadv_tp_list_active(struct batadv_priv *bat_priv, const u8 *dst)
  *
  * Return: matching tp_vars or NULL when no tp_vars was found
  */
-static struct batadv_tp_vars *
-batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
-			    const u8 *session, enum batadv_tp_meter_role role)
+static struct batadv_tp_sender *
+batadv_tp_list_find_sender_session(struct batadv_priv *bat_priv, const u8 *dst,
+				   const u8 *session)
 {
-	struct batadv_tp_vars *pos, *tp_vars = NULL;
+	struct batadv_tp_sender *pos, *tp_vars = NULL;
 
 	rcu_read_lock();
-	hlist_for_each_entry_rcu(pos, &bat_priv->tp_list, list) {
-		if (!batadv_compare_eth(pos->other_end, dst))
+	hlist_for_each_entry_rcu(pos, &bat_priv->tp_sender_list, common.list) {
+		if (!batadv_compare_eth(pos->common.other_end, dst))
 			continue;
 
-		if (memcmp(pos->session, session, sizeof(pos->session)) != 0)
-			continue;
-
-		if (pos->role != role)
+		if (memcmp(pos->common.session, session, sizeof(pos->common.session)) != 0)
 			continue;
 
 		/* most of the time this function is invoked during the normal
 		 * process..it makes sense to pay more when the session is
 		 * finished and to speed the process up during the measurement
 		 */
-		if (unlikely(!kref_get_unless_zero(&pos->refcount)))
+		if (unlikely(!kref_get_unless_zero(&pos->common.refcount)))
 			continue;
 
 		tp_vars = pos;
@@ -358,16 +355,16 @@ batadv_tp_list_find_session(struct batadv_priv *bat_priv, const u8 *dst,
 }
 
 /**
- * batadv_tp_vars_release() - release batadv_tp_vars from lists and queue for
- *  free after rcu grace period
+ * batadv_tp_vars_common_release() - release batadv_tp_vars_common from lists
+ *  and queue for free after rcu grace period
  * @ref: kref pointer of the batadv_tp_vars
  */
-static void batadv_tp_vars_release(struct kref *ref)
+static void batadv_tp_vars_common_release(struct kref *ref)
 {
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_vars_common *tp_vars;
 	struct batadv_tp_unacked *un, *safe;
 
-	tp_vars = container_of(ref, struct batadv_tp_vars, refcount);
+	tp_vars = container_of(ref, struct batadv_tp_vars_common, refcount);
 
 	/* lock should not be needed because this object is now out of any
 	 * context!
@@ -383,23 +380,25 @@ static void batadv_tp_vars_release(struct kref *ref)
 }
 
 /**
- * batadv_tp_vars_put() - decrement the batadv_tp_vars refcounter and possibly
- *  release it
+ * batadv_tp_sender_put() - decrement the batadv_tp_sender
+ *  refcounter and possibly release it
  * @tp_vars: the private data of the current TP meter session to be free'd
  */
-static void batadv_tp_vars_put(struct batadv_tp_vars *tp_vars)
+static void batadv_tp_sender_put(struct batadv_tp_sender *tp_vars)
 {
 	if (!tp_vars)
 		return;
 
-	kref_put(&tp_vars->refcount, batadv_tp_vars_release);
+	kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
 }
 
 /**
- * batadv_tp_list_detach() - remove tp session from mesh session list once
+ * batadv_tp_list_detach() - remove tp receiver session from mesh session list once
  * @tp_vars: the private data of the current TP meter session
+ *
+ * Return: whether tp_vars was detached from list and reference must be freed
  */
-static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
+static bool batadv_tp_list_detach(struct batadv_tp_vars_common *tp_vars)
 {
 	bool detached = false;
 
@@ -411,27 +410,27 @@ static void batadv_tp_list_detach(struct batadv_tp_vars *tp_vars)
 	spin_unlock_bh(&tp_vars->bat_priv->tp_list_lock);
 
 	if (!detached)
-		return;
+		return false;
 
 	atomic_dec(&tp_vars->bat_priv->tp_num);
 
-	/* drop list reference */
-	batadv_tp_vars_put(tp_vars);
+	return true;
 }
 
 /**
  * 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)
+static void batadv_tp_sender_cleanup(struct batadv_tp_sender *tp_vars)
 {
 	cancel_delayed_work_sync(&tp_vars->finish_work);
 
-	batadv_tp_list_detach(tp_vars);
+	if (batadv_tp_list_detach(&tp_vars->common))
+		batadv_tp_sender_put(tp_vars);
 
 	/* kill the timer and remove its reference */
-	timer_shutdown_sync(&tp_vars->timer);
-	batadv_tp_vars_put(tp_vars);
+	timer_shutdown_sync(&tp_vars->common.timer);
+	batadv_tp_sender_put(tp_vars);
 }
 
 /**
@@ -440,7 +439,7 @@ static void batadv_tp_sender_cleanup(struct batadv_tp_vars *tp_vars)
  * @tp_vars: the private data of the current TP meter session
  */
 static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
-				 struct batadv_tp_vars *tp_vars)
+				 struct batadv_tp_sender *tp_vars)
 {
 	enum batadv_tp_meter_reason reason;
 	u32 session_cookie;
@@ -449,7 +448,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Test towards %pM finished..shutting down (reason=%d)\n",
-		   tp_vars->other_end, reason);
+		   tp_vars->common.other_end, reason);
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
@@ -459,11 +458,11 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
 		   "Final values: cwnd=%u ss_threshold=%u\n",
 		   tp_vars->cwnd, tp_vars->ss_threshold);
 
-	session_cookie = batadv_tp_session_cookie(tp_vars->session,
+	session_cookie = batadv_tp_session_cookie(tp_vars->common.session,
 						  tp_vars->icmp_uid);
 
 	batadv_tp_batctl_notify(reason,
-				tp_vars->other_end,
+				tp_vars->common.other_end,
 				bat_priv,
 				tp_vars->start_time,
 				atomic64_read(&tp_vars->tot_sent),
@@ -475,7 +474,7 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
  * @tp_vars: the private data of the current TP meter session
  * @reason: reason for tp meter session stop
  */
-static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
+static void batadv_tp_sender_shutdown(struct batadv_tp_sender *tp_vars,
 				      enum batadv_tp_meter_reason reason)
 {
 	atomic_cmpxchg(&tp_vars->send_result, 0, reason);
@@ -487,7 +486,7 @@ static void batadv_tp_sender_shutdown(struct batadv_tp_vars *tp_vars,
  *
  * Return: whether stop reason was found
  */
-static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars)
+static bool batadv_tp_sender_stopped(struct batadv_tp_sender *tp_vars)
 {
 	return atomic_read(&tp_vars->send_result) != 0;
 }
@@ -499,10 +498,10 @@ static bool batadv_tp_sender_stopped(struct batadv_tp_vars *tp_vars)
 static void batadv_tp_sender_finish(struct work_struct *work)
 {
 	struct delayed_work *delayed_work;
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_sender *tp_vars;
 
 	delayed_work = to_delayed_work(work);
-	tp_vars = container_of(delayed_work, struct batadv_tp_vars,
+	tp_vars = container_of(delayed_work, struct batadv_tp_sender,
 			       finish_work);
 
 	batadv_tp_sender_shutdown(tp_vars, BATADV_TP_REASON_COMPLETE);
@@ -514,7 +513,7 @@ static void batadv_tp_sender_finish(struct work_struct *work)
  *
  * Reschedule the timer using tp_vars->rto as delay
  */
-static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
+static void batadv_tp_reset_sender_timer(struct batadv_tp_sender *tp_vars)
 {
 	/* most of the time this function is invoked while normal packet
 	 * reception...
@@ -523,7 +522,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
 		/* timer ref will be dropped in batadv_tp_sender_cleanup */
 		return;
 
-	mod_timer(&tp_vars->timer, jiffies + msecs_to_jiffies(tp_vars->rto));
+	mod_timer(&tp_vars->common.timer, jiffies + msecs_to_jiffies(tp_vars->rto));
 }
 
 /**
@@ -536,8 +535,8 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_vars *tp_vars)
  */
 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;
+	struct batadv_tp_sender *tp_vars = timer_container_of(tp_vars, t, common.timer);
+	struct batadv_priv *bat_priv = tp_vars->common.bat_priv;
 
 	if (batadv_tp_sender_stopped(tp_vars))
 		return;
@@ -562,7 +561,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Meter: RTO fired during test towards %pM! cwnd=%u new ss_thr=%u, resetting last_sent to %u\n",
-		   tp_vars->other_end, tp_vars->cwnd, tp_vars->ss_threshold,
+		   tp_vars->common.other_end, tp_vars->cwnd, tp_vars->ss_threshold,
 		   atomic_read(&tp_vars->last_acked));
 
 	tp_vars->cwnd = BATADV_TP_PLEN * 3;
@@ -582,7 +581,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
  * @buf: Buffer to fill with bytes
  * @nbytes: amount of pseudorandom bytes
  */
-static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars,
+static void batadv_tp_fill_prerandom(struct batadv_tp_sender *tp_vars,
 				     u8 *buf, size_t nbytes)
 {
 	u32 local_offset;
@@ -625,7 +624,7 @@ static void batadv_tp_fill_prerandom(struct batadv_tp_vars *tp_vars,
  * not reachable, BATADV_TP_REASON_MEMORY_ERROR if the packet couldn't be
  * allocated
  */
-static int batadv_tp_send_msg(struct batadv_tp_vars *tp_vars, const u8 *src,
+static int batadv_tp_send_msg(struct batadv_tp_sender *tp_vars, const u8 *src,
 			      struct batadv_orig_node *orig_node,
 			      u32 seqno, size_t len, const u8 *session,
 			      int uid, u32 timestamp)
@@ -681,7 +680,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 	struct batadv_hard_iface *primary_if = NULL;
 	struct batadv_orig_node *orig_node = NULL;
 	const struct batadv_icmp_tp_packet *icmp;
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_sender *tp_vars;
 	const unsigned char *dev_addr;
 	size_t packet_len, mss;
 	u32 rtt, recv_ack, cwnd;
@@ -693,8 +692,8 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 	icmp = (struct batadv_icmp_tp_packet *)skb->data;
 
 	/* find the tp_vars */
-	tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
-					      icmp->session, BATADV_TP_SENDER);
+	tp_vars = batadv_tp_list_find_sender_session(bat_priv, icmp->orig,
+						     icmp->session);
 	if (unlikely(!tp_vars))
 		return;
 
@@ -804,7 +803,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 out:
 	batadv_hardif_put(primary_if);
 	batadv_orig_node_put(orig_node);
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_sender_put(tp_vars);
 }
 
 /**
@@ -814,7 +813,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
  *
  * Return: true when congestion window is not full, false otherwise
  */
-static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
+static bool batadv_tp_avail(struct batadv_tp_sender *tp_vars,
 			    size_t payload_len)
 {
 	u32 last_sent = tp_vars->last_sent;
@@ -841,7 +840,7 @@ static bool batadv_tp_avail(struct batadv_tp_vars *tp_vars,
  *  remaining jiffies (at least 1) if the condition evaluated to true before
  *  the timeout elapsed, or -ERESTARTSYS if it was interrupted by a signal.
  */
-static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen)
+static int batadv_tp_wait_available(struct batadv_tp_sender *tp_vars, size_t plen)
 {
 	int ret;
 
@@ -860,20 +859,14 @@ static int batadv_tp_wait_available(struct batadv_tp_vars *tp_vars, size_t plen)
  */
 static int batadv_tp_send(void *arg)
 {
-	struct batadv_tp_vars *tp_vars = arg;
-	struct batadv_priv *bat_priv = tp_vars->bat_priv;
+	struct batadv_tp_sender *tp_vars = arg;
+	struct batadv_priv *bat_priv = tp_vars->common.bat_priv;
 	struct batadv_hard_iface *primary_if = NULL;
 	struct batadv_orig_node *orig_node = NULL;
 	size_t payload_len, packet_len;
 	int err = 0;
 
-	if (unlikely(tp_vars->role != BATADV_TP_SENDER)) {
-		err = BATADV_TP_REASON_DST_UNREACHABLE;
-		batadv_tp_sender_shutdown(tp_vars, err);
-		goto out;
-	}
-
-	orig_node = batadv_orig_hash_find(bat_priv, tp_vars->other_end);
+	orig_node = batadv_orig_hash_find(bat_priv, tp_vars->common.other_end);
 	if (unlikely(!orig_node)) {
 		err = BATADV_TP_REASON_DST_UNREACHABLE;
 		batadv_tp_sender_shutdown(tp_vars, err);
@@ -916,7 +909,7 @@ static int batadv_tp_send(void *arg)
 		err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
 					 orig_node, tp_vars->last_sent,
 					 packet_len,
-					 tp_vars->session, tp_vars->icmp_uid,
+					 tp_vars->common.session, tp_vars->icmp_uid,
 					 jiffies_to_msecs(jiffies));
 
 		/* something went wrong during the preparation/transmission */
@@ -944,7 +937,7 @@ static int batadv_tp_send(void *arg)
 	batadv_tp_sender_cleanup(tp_vars);
 	complete(&tp_vars->finished);
 
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_sender_put(tp_vars);
 
 	return 0;
 }
@@ -954,24 +947,24 @@ static int batadv_tp_send(void *arg)
  *  sender
  * @tp_vars: the private data of the current TP meter session
  */
-static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
+static void batadv_tp_start_kthread(struct batadv_tp_sender *tp_vars)
 {
 	struct task_struct *kthread;
-	struct batadv_priv *bat_priv = tp_vars->bat_priv;
+	struct batadv_priv *bat_priv = tp_vars->common.bat_priv;
 	u32 session_cookie;
 
-	kref_get(&tp_vars->refcount);
+	kref_get(&tp_vars->common.refcount);
 	kthread = kthread_create(batadv_tp_send, tp_vars, "kbatadv_tp_meter");
 	if (IS_ERR(kthread)) {
-		session_cookie = batadv_tp_session_cookie(tp_vars->session,
+		session_cookie = batadv_tp_session_cookie(tp_vars->common.session,
 							  tp_vars->icmp_uid);
 		pr_err("batadv: cannot create tp meter kthread\n");
 		batadv_tp_batctl_error_notify(BATADV_TP_REASON_MEMORY_ERROR,
-					      tp_vars->other_end,
+					      tp_vars->common.other_end,
 					      bat_priv, session_cookie);
 
 		/* drop reserved reference for kthread */
-		batadv_tp_vars_put(tp_vars);
+		batadv_tp_sender_put(tp_vars);
 
 		/* cleanup of failed tp meter variables */
 		batadv_tp_sender_cleanup(tp_vars);
@@ -992,7 +985,7 @@ static void batadv_tp_start_kthread(struct batadv_tp_vars *tp_vars)
 void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 		     u32 test_length, u32 *cookie)
 {
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_sender *tp_vars;
 	u8 session_id[2];
 	u8 icmp_uid;
 	u32 session_cookie;
@@ -1042,11 +1035,10 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	}
 
 	/* initialize tp_vars */
-	ether_addr_copy(tp_vars->other_end, dst);
-	kref_init(&tp_vars->refcount);
-	tp_vars->role = BATADV_TP_SENDER;
+	ether_addr_copy(tp_vars->common.other_end, dst);
+	kref_init(&tp_vars->common.refcount);
 	atomic_set(&tp_vars->send_result, 0);
-	memcpy(tp_vars->session, session_id, sizeof(session_id));
+	memcpy(tp_vars->common.session, session_id, sizeof(session_id));
 	tp_vars->icmp_uid = icmp_uid;
 
 	tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
@@ -1074,25 +1066,25 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 
 	atomic64_set(&tp_vars->tot_sent, 0);
 
-	kref_get(&tp_vars->refcount);
-	timer_setup(&tp_vars->timer, batadv_tp_sender_timeout, 0);
+	kref_get(&tp_vars->common.refcount);
+	timer_setup(&tp_vars->common.timer, batadv_tp_sender_timeout, 0);
 
-	tp_vars->bat_priv = bat_priv;
+	tp_vars->common.bat_priv = bat_priv;
 	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);
+	spin_lock_init(&tp_vars->common.unacked_lock);
+	INIT_LIST_HEAD(&tp_vars->common.unacked_list);
 
 	spin_lock_init(&tp_vars->cwnd_lock);
 
 	tp_vars->prerandom_offset = 0;
 	spin_lock_init(&tp_vars->prerandom_lock);
 
-	kref_get(&tp_vars->refcount);
-	hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+	kref_get(&tp_vars->common.refcount);
+	hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_sender_list);
 	spin_unlock_bh(&bat_priv->tp_list_lock);
 
 	tp_vars->test_length = test_length;
@@ -1112,7 +1104,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	batadv_tp_start_kthread(tp_vars);
 
 	/* don't return reference to new tp_vars */
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_sender_put(tp_vars);
 }
 
 /**
@@ -1125,7 +1117,7 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
 		    u8 return_value)
 {
 	struct batadv_orig_node *orig_node;
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_sender *tp_vars;
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Meter: stopping test towards %pM\n", dst);
@@ -1134,7 +1126,7 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
 	if (!orig_node)
 		return;
 
-	tp_vars = batadv_tp_list_find(bat_priv, orig_node->orig, BATADV_TP_SENDER);
+	tp_vars = batadv_tp_list_find_sender(bat_priv, orig_node->orig);
 	if (!tp_vars) {
 		batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 			   "Meter: trying to interrupt an already over connection\n");
@@ -1142,20 +1134,75 @@ void batadv_tp_stop(struct batadv_priv *bat_priv, const u8 *dst,
 	}
 
 	batadv_tp_sender_shutdown(tp_vars, return_value);
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_sender_put(tp_vars);
 out_put_orig_node:
 	batadv_orig_node_put(orig_node);
 }
 
+/**
+ * batadv_tp_list_find_receiver_session() - find tp_vars receiver session
+ *  object in the global list
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @dst: the other endpoint MAC address to look for
+ * @session: session identifier
+ *
+ * Look for a tp_vars object matching dst as end_point, session as tp meter
+ * session and return it after having increment the refcounter. Return NULL
+ * is not found
+ *
+ * Return: matching tp_vars or NULL when no tp_vars was found
+ */
+static struct batadv_tp_receiver *
+batadv_tp_list_find_receiver_session(struct batadv_priv *bat_priv, const u8 *dst,
+				     const u8 *session)
+{
+	struct batadv_tp_receiver *pos, *tp_vars = NULL;
+
+	rcu_read_lock();
+	hlist_for_each_entry_rcu(pos, &bat_priv->tp_receiver_list, common.list) {
+		if (!batadv_compare_eth(pos->common.other_end, dst))
+			continue;
+
+		if (memcmp(pos->common.session, session, sizeof(pos->common.session)) != 0)
+			continue;
+
+		/* most of the time this function is invoked during the normal
+		 * process..it makes sense to pay more when the session is
+		 * finished and to speed the process up during the measurement
+		 */
+		if (unlikely(!kref_get_unless_zero(&pos->common.refcount)))
+			continue;
+
+		tp_vars = pos;
+		break;
+	}
+	rcu_read_unlock();
+
+	return tp_vars;
+}
+
+/**
+ * batadv_tp_receiver_put() - decrement the batadv_tp_receiver
+ *  refcounter and possibly release it
+ * @tp_vars: the private data of the current TP meter session to be free'd
+ */
+static void batadv_tp_receiver_put(struct batadv_tp_receiver *tp_vars)
+{
+	if (!tp_vars)
+		return;
+
+	kref_put(&tp_vars->common.refcount, batadv_tp_vars_common_release);
+}
+
 /**
  * batadv_tp_reset_receiver_timer() - reset the receiver shutdown timer
  * @tp_vars: the private data of the current TP meter session
  *
  * start the receiver shutdown timer or reset it if already started
  */
-static void batadv_tp_reset_receiver_timer(struct batadv_tp_vars *tp_vars)
+static void batadv_tp_reset_receiver_timer(struct batadv_tp_receiver *tp_vars)
 {
-	mod_timer(&tp_vars->timer,
+	mod_timer(&tp_vars->common.timer,
 		  jiffies + msecs_to_jiffies(BATADV_TP_RECV_TIMEOUT));
 }
 
@@ -1166,11 +1213,11 @@ static void batadv_tp_reset_receiver_timer(struct batadv_tp_vars *tp_vars)
  */
 static void batadv_tp_receiver_shutdown(struct timer_list *t)
 {
-	struct batadv_tp_vars *tp_vars = timer_container_of(tp_vars, t, timer);
+	struct batadv_tp_receiver *tp_vars = timer_container_of(tp_vars, t, common.timer);
 	struct batadv_tp_unacked *un, *safe;
 	struct batadv_priv *bat_priv;
 
-	bat_priv = tp_vars->bat_priv;
+	bat_priv = tp_vars->common.bat_priv;
 
 	/* if there is recent activity rearm the timer */
 	if (!batadv_has_timed_out(tp_vars->last_recv_time,
@@ -1182,22 +1229,23 @@ static void batadv_tp_receiver_shutdown(struct timer_list *t)
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Shutting down for inactivity (more than %dms) from %pM\n",
-		   BATADV_TP_RECV_TIMEOUT, tp_vars->other_end);
+		   BATADV_TP_RECV_TIMEOUT, tp_vars->common.other_end);
 
-	batadv_tp_list_detach(tp_vars);
+	if (batadv_tp_list_detach(&tp_vars->common))
+		batadv_tp_receiver_put(tp_vars);
 
-	spin_lock_bh(&tp_vars->unacked_lock);
-	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+	spin_lock_bh(&tp_vars->common.unacked_lock);
+	list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
 		list_del(&un->list);
 		kfree(un);
 	}
-	spin_unlock_bh(&tp_vars->unacked_lock);
+	spin_unlock_bh(&tp_vars->common.unacked_lock);
 
 	/* drop reference of timer */
 	if (WARN_ON(atomic_xchg(&tp_vars->receiving, 0) != 1))
 		return;
 
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_receiver_put(tp_vars);
 }
 
 /**
@@ -1281,7 +1329,7 @@ static int batadv_tp_send_ack(struct batadv_priv *bat_priv, const u8 *dst,
  *
  * Return: true if the packed has been successfully processed, false otherwise
  */
-static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
+static bool batadv_tp_handle_out_of_order(struct batadv_tp_receiver *tp_vars,
 					  const struct sk_buff *skb)
 {
 	const struct batadv_icmp_tp_packet *icmp;
@@ -1299,10 +1347,10 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
 	payload_len = skb->len - sizeof(struct batadv_unicast_packet);
 	new->len = payload_len;
 
-	spin_lock_bh(&tp_vars->unacked_lock);
+	spin_lock_bh(&tp_vars->common.unacked_lock);
 	/* if the list is empty immediately attach this new object */
-	if (list_empty(&tp_vars->unacked_list)) {
-		list_add(&new->list, &tp_vars->unacked_list);
+	if (list_empty(&tp_vars->common.unacked_list)) {
+		list_add(&new->list, &tp_vars->common.unacked_list);
 		goto out;
 	}
 
@@ -1313,7 +1361,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
 	 * the last received packet (the one being processed now) has a bigger
 	 * seqno than all the others already stored.
 	 */
-	list_for_each_entry_reverse(un, &tp_vars->unacked_list, list) {
+	list_for_each_entry_reverse(un, &tp_vars->common.unacked_list, list) {
 		/* check for duplicates */
 		if (new->seqno == un->seqno) {
 			if (new->len > un->len)
@@ -1338,10 +1386,10 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
 
 	/* received packet with smallest seqno out of order; add it to front */
 	if (!added)
-		list_add(&new->list, &tp_vars->unacked_list);
+		list_add(&new->list, &tp_vars->common.unacked_list);
 
 out:
-	spin_unlock_bh(&tp_vars->unacked_lock);
+	spin_unlock_bh(&tp_vars->common.unacked_lock);
 
 	return true;
 }
@@ -1351,7 +1399,7 @@ static bool batadv_tp_handle_out_of_order(struct batadv_tp_vars *tp_vars,
  *  without gaps
  * @tp_vars: the private data of the current TP meter session
  */
-static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
+static void batadv_tp_ack_unordered(struct batadv_tp_receiver *tp_vars)
 {
 	struct batadv_tp_unacked *un, *safe;
 	u32 to_ack;
@@ -1359,8 +1407,8 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
 	/* go through the unacked packet list and possibly ACK them as
 	 * well
 	 */
-	spin_lock_bh(&tp_vars->unacked_lock);
-	list_for_each_entry_safe(un, safe, &tp_vars->unacked_list, list) {
+	spin_lock_bh(&tp_vars->common.unacked_lock);
+	list_for_each_entry_safe(un, safe, &tp_vars->common.unacked_list, list) {
 		/* the list is ordered, therefore it is possible to stop as soon
 		 * there is a gap between the last acked seqno and the seqno of
 		 * the packet under inspection
@@ -1376,7 +1424,7 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
 		list_del(&un->list);
 		kfree(un);
 	}
-	spin_unlock_bh(&tp_vars->unacked_lock);
+	spin_unlock_bh(&tp_vars->common.unacked_lock);
 }
 
 /**
@@ -1386,18 +1434,18 @@ static void batadv_tp_ack_unordered(struct batadv_tp_vars *tp_vars)
  *
  * Return: corresponding tp_vars or NULL on errors
  */
-static struct batadv_tp_vars *
+static struct batadv_tp_receiver *
 batadv_tp_init_recv(struct batadv_priv *bat_priv,
 		    const struct batadv_icmp_tp_packet *icmp)
 {
-	struct batadv_tp_vars *tp_vars = NULL;
+	struct batadv_tp_receiver *tp_vars = NULL;
 
 	spin_lock_bh(&bat_priv->tp_list_lock);
 	if (atomic_read(&bat_priv->mesh_state) != BATADV_MESH_ACTIVE)
 		goto out_unlock;
 
-	tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
-					      icmp->session, BATADV_TP_RECEIVER);
+	tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig,
+						       icmp->session);
 	if (tp_vars)
 		goto out_unlock;
 
@@ -1413,22 +1461,21 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv,
 		goto out_unlock;
 	}
 
-	ether_addr_copy(tp_vars->other_end, icmp->orig);
-	tp_vars->role = BATADV_TP_RECEIVER;
+	ether_addr_copy(tp_vars->common.other_end, icmp->orig);
 	atomic_set(&tp_vars->receiving, 1);
-	memcpy(tp_vars->session, icmp->session, sizeof(tp_vars->session));
+	memcpy(tp_vars->common.session, icmp->session, sizeof(tp_vars->common.session));
 	tp_vars->last_recv = BATADV_TP_FIRST_SEQ;
-	tp_vars->bat_priv = bat_priv;
-	kref_init(&tp_vars->refcount);
+	tp_vars->common.bat_priv = bat_priv;
+	kref_init(&tp_vars->common.refcount);
 
-	spin_lock_init(&tp_vars->unacked_lock);
-	INIT_LIST_HEAD(&tp_vars->unacked_list);
+	spin_lock_init(&tp_vars->common.unacked_lock);
+	INIT_LIST_HEAD(&tp_vars->common.unacked_list);
 
-	kref_get(&tp_vars->refcount);
-	hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list);
+	kref_get(&tp_vars->common.refcount);
+	hlist_add_head_rcu(&tp_vars->common.list, &bat_priv->tp_receiver_list);
 
-	kref_get(&tp_vars->refcount);
-	timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0);
+	kref_get(&tp_vars->common.refcount);
+	timer_setup(&tp_vars->common.timer, batadv_tp_receiver_shutdown, 0);
 
 	batadv_tp_reset_receiver_timer(tp_vars);
 
@@ -1449,7 +1496,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 			       const struct sk_buff *skb)
 {
 	const struct batadv_icmp_tp_packet *icmp;
-	struct batadv_tp_vars *tp_vars;
+	struct batadv_tp_receiver *tp_vars;
 	size_t packet_size;
 	u32 seqno;
 
@@ -1467,8 +1514,8 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 			goto out;
 		}
 	} else {
-		tp_vars = batadv_tp_list_find_session(bat_priv, icmp->orig,
-						      icmp->session, BATADV_TP_RECEIVER);
+		tp_vars = batadv_tp_list_find_receiver_session(bat_priv, icmp->orig,
+							       icmp->session);
 		if (!tp_vars) {
 			batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 				   "Unexpected packet from %pM!\n",
@@ -1512,7 +1559,7 @@ static void batadv_tp_recv_msg(struct batadv_priv *bat_priv,
 	batadv_tp_send_ack(bat_priv, icmp->orig, tp_vars->last_recv,
 			   icmp->timestamp, icmp->session, icmp->uid);
 out:
-	batadv_tp_vars_put(tp_vars);
+	batadv_tp_receiver_put(tp_vars);
 }
 
 /**
@@ -1552,45 +1599,58 @@ void batadv_tp_meter_recv(struct batadv_priv *bat_priv, struct sk_buff *skb)
  */
 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;
+	struct batadv_tp_receiver *tp_receivers[BATADV_TP_MAX_NUM];
+	struct batadv_tp_sender *tp_senders[BATADV_TP_MAX_NUM];
+	struct batadv_tp_receiver *tp_receiver;
+	struct batadv_tp_sender *tp_sender;
+	size_t receiver_count = 0;
+	size_t sender_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))
+	hlist_for_each_entry(tp_receiver, &bat_priv->tp_receiver_list, common.list) {
+		if (WARN_ON_ONCE(receiver_count >= BATADV_TP_MAX_NUM))
 			break;
 
-		if (!kref_get_unless_zero(&tp_var->refcount))
+		if (!kref_get_unless_zero(&tp_receiver->common.refcount))
 			continue;
 
-		tp_vars[count++] = tp_var;
+		tp_receivers[receiver_count++] = tp_receiver;
+	}
+
+	hlist_for_each_entry(tp_sender, &bat_priv->tp_sender_list, common.list) {
+		if (WARN_ON_ONCE(sender_count >= BATADV_TP_MAX_NUM))
+			break;
+
+		if (!kref_get_unless_zero(&tp_sender->common.refcount))
+			continue;
+
+		tp_senders[sender_count++] = tp_sender;
 	}
 	spin_unlock_bh(&bat_priv->tp_list_lock);
 
-	for (i = 0; i < count; i++) {
-		tp_var = tp_vars[i];
+	for (i = 0; i < receiver_count; i++) {
+		tp_receiver = tp_receivers[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);
-			timer_shutdown_sync(&tp_var->timer);
+		if (batadv_tp_list_detach(&tp_receiver->common))
+			batadv_tp_receiver_put(tp_receiver);
 
-			if (atomic_xchg(&tp_var->receiving, 0) != 1)
-				break;
+		timer_shutdown_sync(&tp_receiver->common.timer);
 
-			batadv_tp_vars_put(tp_var);
-			break;
-		}
+		if (atomic_xchg(&tp_receiver->receiving, 0) != 0)
+			batadv_tp_receiver_put(tp_receiver);
 
-		batadv_tp_vars_put(tp_var);
+		batadv_tp_receiver_put(tp_receiver);
+	}
+
+	for (i = 0; i < sender_count; i++) {
+		tp_sender = tp_senders[i];
+
+		batadv_tp_sender_shutdown(tp_sender, BATADV_TP_REASON_CANCEL);
+		wake_up(&tp_sender->more_bytes);
+		wait_for_completion(&tp_sender->finished);
+
+		batadv_tp_sender_put(tp_sender);
 	}
 
 	synchronize_net();
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index a01ee46d..4880bf3e 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1297,21 +1297,10 @@ struct batadv_tp_unacked {
 };
 
 /**
- * enum batadv_tp_meter_role - Modus in tp meter session
+ * struct batadv_tp_vars_common - common tp meter private variables per session
  */
-enum batadv_tp_meter_role {
-	/** @BATADV_TP_RECEIVER: Initialized as receiver */
-	BATADV_TP_RECEIVER,
-
-	/** @BATADV_TP_SENDER: Initialized as sender */
-	BATADV_TP_SENDER
-};
-
-/**
- * struct batadv_tp_vars - tp meter private variables per session
- */
-struct batadv_tp_vars {
-	/** @list: list node for &bat_priv.tp_list */
+struct batadv_tp_vars_common {
+	/** @list: list node for &bat_priv.tp_sender_list/&bat_priv.tp_receiver_list */
 	struct hlist_node list;
 
 	/** @timer: timer for ack (receiver) and retry (sender) */
@@ -1320,14 +1309,34 @@ struct batadv_tp_vars {
 	/** @bat_priv: pointer to the mesh object */
 	struct batadv_priv *bat_priv;
 
-	/** @start_time: start time in jiffies */
-	unsigned long start_time;
-
 	/** @other_end: mac address of remote */
 	u8 other_end[ETH_ALEN];
 
-	/** @role: receiver/sender modi */
-	enum batadv_tp_meter_role role;
+	/** @session: TP session identifier */
+	u8 session[2];
+
+	/** @unacked_list: list of unacked packets (meta-info only) */
+	struct list_head unacked_list;
+
+	/** @unacked_lock: protect unacked_list */
+	spinlock_t unacked_lock;
+
+	/** @refcount: number of context where the object is used */
+	struct kref refcount;
+
+	/** @rcu: struct used for freeing in an RCU-safe manner */
+	struct rcu_head rcu;
+};
+
+/**
+ * struct batadv_tp_sender - sender tp meter private variables per session
+ */
+struct batadv_tp_sender {
+	/** @common: common batadv_tp_vars (best be first member) */
+	struct batadv_tp_vars_common common;
+
+	/** @start_time: start time in jiffies */
+	unsigned long start_time;
 
 	/**
 	 * @send_result: 0 when sending is ongoing and otherwise
@@ -1335,8 +1344,14 @@ struct batadv_tp_vars {
 	 */
 	atomic_t send_result;
 
-	/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
-	atomic_t receiving;
+	/** @last_sent: last sent byte, not yet acked */
+	u32 last_sent;
+
+	/** @fast_recovery: true if in Fast Recovery mode */
+	unsigned char fast_recovery:1;
+
+	/** @recover: last sent seqno when entering Fast Recovery */
+	u32 recover;
 
 	/** @finish_work: work item for the finishing procedure */
 	struct delayed_work finish_work;
@@ -1347,14 +1362,9 @@ struct batadv_tp_vars {
 	/** @test_length: test length in milliseconds */
 	u32 test_length;
 
-	/** @session: TP session identifier */
-	u8 session[2];
-
 	/** @icmp_uid: local ICMP "socket" index */
 	u8 icmp_uid;
 
-	/* sender variables */
-
 	/** @dec_cwnd: decimal part of the cwnd used during linear growth */
 	u16 dec_cwnd;
 
@@ -1373,21 +1383,12 @@ struct batadv_tp_vars {
 	/** @last_acked: last acked byte */
 	atomic_t last_acked;
 
-	/** @last_sent: last sent byte, not yet acked */
-	u32 last_sent;
-
 	/** @tot_sent: amount of data sent/ACKed so far */
 	atomic64_t tot_sent;
 
 	/** @dup_acks: duplicate ACKs counter */
 	atomic_t dup_acks;
 
-	/** @fast_recovery: true if in Fast Recovery mode */
-	unsigned char fast_recovery:1;
-
-	/** @recover: last sent seqno when entering Fast Recovery */
-	u32 recover;
-
 	/** @rto: sender timeout */
 	u32 rto;
 
@@ -1408,26 +1409,23 @@ struct batadv_tp_vars {
 
 	/** @prerandom_lock: spinlock protecting access to prerandom_offset */
 	spinlock_t prerandom_lock;
+};
 
-	/* receiver variables */
+/**
+ * struct batadv_tp_receiver - receiver tp meter private variables per session
+ */
+struct batadv_tp_receiver {
+	/** @common: common batadv_tp_vars (best be first member) */
+	struct batadv_tp_vars_common common;
+
+	/** @receiving: receiving binary semaphore: 1 if receiving, 0 is not */
+	atomic_t receiving;
 
 	/** @last_recv: last in-order received packet */
 	u32 last_recv;
 
-	/** @unacked_list: list of unacked packets (meta-info only) */
-	struct list_head unacked_list;
-
-	/** @unacked_lock: protect unacked_list */
-	spinlock_t unacked_lock;
-
 	/** @last_recv_time: time (jiffies) a msg was received */
 	unsigned long last_recv_time;
-
-	/** @refcount: number of context where the object is used */
-	struct kref refcount;
-
-	/** @rcu: struct used for freeing in an RCU-safe manner */
-	struct rcu_head rcu;
 };
 
 /**
@@ -1607,8 +1605,11 @@ struct batadv_priv {
 	 */
 	struct hlist_head forw_bcast_list;
 
-	/** @tp_list: list of tp sessions */
-	struct hlist_head tp_list;
+	/** @tp_sender_list: list of tp sender sessions */
+	struct hlist_head tp_sender_list;
+
+	/** @tp_receiver_list: list of tp receiver sessions */
+	struct hlist_head tp_receiver_list;
 
 	/** @orig_hash: hash table containing mesh participants (orig nodes) */
 	struct batadv_hashtable *orig_hash;

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
                   ` (3 preceding siblings ...)
  2026-05-31  8:28 ` [PATCH batadv v4 4/6] batman-adv: tp_meter: split vars into sender and receiver types Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  2026-05-31  9:55   ` Sven Eckelmann
  2026-05-31  8:28 ` [PATCH batadv v4 6/6] batman-adv: tp_meter: consolidate " Sven Eckelmann
  5 siblings, 1 reply; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

Some variables used atomic_t for concurrent access while others relied on
cwnd_lock, leading to an inconsistent locking model. This can be simplified
by:

* keeping all congestion control decisions inside the cc_lock
* variables which can be accessed without a lock must use
  READ_ONCE/WRITE_ONE

This is only possible, by extracting the congestion control logic from
batadv_tp_recv_ack() into a new helper batadv_tp_handle_ack(). Its
decisions are returned as a batadv_tp_ack_reaction enum value and then
applied by the caller. This separates the algorithm (deciding what to do)
from the mechanism (actually doing it).

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/tp_meter.c | 330 +++++++++++++++++++++++++++-------------------
 net/batman-adv/types.h    |   8 +-
 2 files changed, 199 insertions(+), 139 deletions(-)

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index c8afd1e0..e7a2dde1 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -143,29 +143,23 @@ static u32 batadv_tp_cwnd(u32 base, u32 increment, u32 min)
  * increased by MSS * MSS / CWND for every unique received ACK
  */
 static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss)
+	__must_hold(&tp_vars->cwnd_lock)
 {
-	spin_lock_bh(&tp_vars->cwnd_lock);
-
 	/* slow start... */
 	if (tp_vars->cwnd <= tp_vars->ss_threshold) {
 		tp_vars->dec_cwnd = 0;
 		tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss);
-		spin_unlock_bh(&tp_vars->cwnd_lock);
 		return;
 	}
 
 	/* increment CWND at least of 1 (section 3.1 of RFC5681) */
 	tp_vars->dec_cwnd += max_t(u32, 1U << 3,
 				   ((mss * mss) << 6) / (tp_vars->cwnd << 3));
-	if (tp_vars->dec_cwnd < (mss << 3)) {
-		spin_unlock_bh(&tp_vars->cwnd_lock);
+	if (tp_vars->dec_cwnd < (mss << 3))
 		return;
-	}
 
 	tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss);
 	tp_vars->dec_cwnd = 0;
-
-	spin_unlock_bh(&tp_vars->cwnd_lock);
 }
 
 /**
@@ -175,6 +169,7 @@ static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss)
  */
 static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars,
 				 u32 new_rtt)
+	__must_hold(&tp_vars->cwnd_lock)
 {
 	long m = new_rtt;
 
@@ -194,14 +189,14 @@ static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars,
 		tp_vars->rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */
 	} else {
 		/* first measure getting in */
-		tp_vars->srtt = m << 3;	/* take the measured time to be srtt */
+		tp_vars->srtt = m << 3; /* take the measured time to be srtt */
 		tp_vars->rttvar = m << 1; /* new_rtt / 2 */
 	}
 
 	/* rto = srtt + 4 * rttvar.
 	 * rttvar is scaled by 4, therefore doesn't need to be multiplied
 	 */
-	tp_vars->rto = (tp_vars->srtt >> 3) + tp_vars->rttvar;
+	WRITE_ONCE(tp_vars->rto, (tp_vars->srtt >> 3) + tp_vars->rttvar);
 }
 
 /**
@@ -522,7 +517,8 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_sender *tp_vars)
 		/* timer ref will be dropped in batadv_tp_sender_cleanup */
 		return;
 
-	mod_timer(&tp_vars->common.timer, jiffies + msecs_to_jiffies(tp_vars->rto));
+	mod_timer(&tp_vars->common.timer,
+		  jiffies + msecs_to_jiffies(READ_ONCE(tp_vars->rto)));
 }
 
 /**
@@ -541,8 +537,11 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 	if (batadv_tp_sender_stopped(tp_vars))
 		return;
 
+	spin_lock_bh(&tp_vars->cwnd_lock);
+
 	/* if the user waited long enough...shutdown the test */
 	if (unlikely(tp_vars->rto >= BATADV_TP_MAX_RTO)) {
+		spin_unlock_bh(&tp_vars->cwnd_lock);
 		batadv_tp_sender_shutdown(tp_vars,
 					  BATADV_TP_REASON_DST_UNREACHABLE);
 		return;
@@ -551,9 +550,7 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 	/* RTO exponential backoff
 	 * Details in Section 5.5 of RFC6298
 	 */
-	tp_vars->rto <<= 1;
-
-	spin_lock_bh(&tp_vars->cwnd_lock);
+	WRITE_ONCE(tp_vars->rto, tp_vars->rto * 2);
 
 	tp_vars->ss_threshold = tp_vars->cwnd >> 1;
 	if (tp_vars->ss_threshold < BATADV_TP_PLEN * 2)
@@ -562,14 +559,15 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Meter: RTO fired during test towards %pM! cwnd=%u new ss_thr=%u, resetting last_sent to %u\n",
 		   tp_vars->common.other_end, tp_vars->cwnd, tp_vars->ss_threshold,
-		   atomic_read(&tp_vars->last_acked));
+		   tp_vars->last_acked);
 
 	tp_vars->cwnd = BATADV_TP_PLEN * 3;
 
+	WRITE_ONCE(tp_vars->last_sent, tp_vars->last_acked);
+
 	spin_unlock_bh(&tp_vars->cwnd_lock);
 
 	/* resend the non-ACKed packets.. */
-	tp_vars->last_sent = atomic_read(&tp_vars->last_acked);
 	wake_up(&tp_vars->more_bytes);
 
 	batadv_tp_reset_sender_timer(tp_vars);
@@ -668,80 +666,58 @@ static int batadv_tp_send_msg(struct batadv_tp_sender *tp_vars, const u8 *src,
 }
 
 /**
- * batadv_tp_recv_ack() - ACK receiving function
- * @bat_priv: the bat priv with all the mesh interface information
- * @skb: the buffer containing the received packet
- *
- * Process a received TP ACK packet
+ * enum batadv_tp_ack_reaction - expected reaction to ack packet
  */
-static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
-			       const struct sk_buff *skb)
+enum batadv_tp_ack_reaction {
+	/** @BATADV_TP_ACK_REACTION_OLD_ACK: ignore old ack packet */
+	BATADV_TP_ACK_REACTION_OLD_ACK,
+
+	/** @BATADV_TP_ACK_REACTION_IGNORE: ignore duplicated ack but reset timer */
+	BATADV_TP_ACK_REACTION_IGNORE,
+
+	/** @BATADV_TP_ACK_REACTION_RESEND_WAKEUP: resend data and wakeup "more_bytes" */
+	BATADV_TP_ACK_REACTION_RESEND_WAKEUP,
+
+	/** @BATADV_TP_ACK_REACTION_WAKEUP: wakeup "more_bytes" */
+	BATADV_TP_ACK_REACTION_WAKEUP,
+};
+
+/**
+ * batadv_tp_handle_ack() - Calculate reaction to ACK and update congestion control
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @tp_vars: the private data of the current TP meter session
+ * @recv_ack: received ACK seqno
+ * @mss: maximum segment size for transmission
+ *
+ * Return: expected reaction to this ack
+ */
+static enum batadv_tp_ack_reaction
+batadv_tp_handle_ack(struct batadv_priv *bat_priv,
+		     struct batadv_tp_sender *tp_vars,
+		     u32 recv_ack, size_t mss)
+	__must_hold(&tp_vars->cwnd_lock)
 {
-	struct batadv_hard_iface *primary_if = NULL;
-	struct batadv_orig_node *orig_node = NULL;
-	const struct batadv_icmp_tp_packet *icmp;
-	struct batadv_tp_sender *tp_vars;
-	const unsigned char *dev_addr;
-	size_t packet_len, mss;
-	u32 rtt, recv_ack, cwnd;
+	static enum batadv_tp_ack_reaction reaction;
 
-	packet_len = BATADV_TP_PLEN;
-	mss = BATADV_TP_PLEN;
-	packet_len += sizeof(struct batadv_unicast_packet);
-
-	icmp = (struct batadv_icmp_tp_packet *)skb->data;
-
-	/* find the tp_vars */
-	tp_vars = batadv_tp_list_find_sender_session(bat_priv, icmp->orig,
-						     icmp->session);
-	if (unlikely(!tp_vars))
-		return;
-
-	if (unlikely(batadv_tp_sender_stopped(tp_vars)))
-		goto out;
-
-	/* old ACK? silently drop it.. */
-	if (batadv_seq_before(ntohl(icmp->seqno),
-			      (u32)atomic_read(&tp_vars->last_acked)))
-		goto out;
-
-	primary_if = batadv_primary_if_get_selected(bat_priv);
-	if (unlikely(!primary_if))
-		goto out;
-
-	orig_node = batadv_orig_hash_find(bat_priv, icmp->orig);
-	if (unlikely(!orig_node))
-		goto out;
-
-	/* update RTO with the new sampled RTT, if any */
-	rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp);
-	if (icmp->timestamp && rtt)
-		batadv_tp_update_rto(tp_vars, rtt);
-
-	/* ACK for new data... reset the timer */
-	batadv_tp_reset_sender_timer(tp_vars);
-
-	recv_ack = ntohl(icmp->seqno);
+	if (batadv_seq_before(recv_ack, tp_vars->last_acked))
+		return BATADV_TP_ACK_REACTION_OLD_ACK;
 
 	/* check if this ACK is a duplicate */
-	if (atomic_read(&tp_vars->last_acked) == recv_ack) {
-		atomic_inc(&tp_vars->dup_acks);
-		if (atomic_read(&tp_vars->dup_acks) != 3)
-			goto out;
-
-		if (recv_ack >= tp_vars->recover)
-			goto out;
-
+	if (tp_vars->last_acked == recv_ack) {
 		/* if this is the third duplicate ACK do Fast Retransmit */
-		batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
-				   orig_node, recv_ack, packet_len,
-				   icmp->session, icmp->uid,
-				   jiffies_to_msecs(jiffies));
+		if (tp_vars->dup_acks > 3)
+			return BATADV_TP_ACK_REACTION_IGNORE;
 
-		spin_lock_bh(&tp_vars->cwnd_lock);
+		tp_vars->dup_acks++;
+		if (tp_vars->dup_acks != 3)
+			return BATADV_TP_ACK_REACTION_IGNORE;
+
+		if (!batadv_seq_before(recv_ack, tp_vars->recover))
+			return BATADV_TP_ACK_REACTION_IGNORE;
 
 		/* Fast Recovery */
 		tp_vars->fast_recovery = true;
+
 		/* Set recover to the last outstanding seqno when Fast Recovery
 		 * is entered. RFC6582, Section 3.2, step 1
 		 */
@@ -754,52 +730,128 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 		tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 3 * mss,
 					       mss);
 		tp_vars->dec_cwnd = 0;
-		tp_vars->last_sent = recv_ack;
+		WRITE_ONCE(tp_vars->last_sent, recv_ack);
 
-		spin_unlock_bh(&tp_vars->cwnd_lock);
-	} else {
-		/* count the acked data */
-		atomic64_add(recv_ack - atomic_read(&tp_vars->last_acked),
-			     &tp_vars->tot_sent);
-		/* reset the duplicate ACKs counter */
-		atomic_set(&tp_vars->dup_acks, 0);
-
-		if (tp_vars->fast_recovery) {
-			/* partial ACK */
-			if (batadv_seq_before(recv_ack, tp_vars->recover)) {
-				/* this is another hole in the window. React
-				 * immediately as specified by NewReno (see
-				 * Section 3.2 of RFC6582 for details)
-				 */
-				dev_addr = primary_if->net_dev->dev_addr;
-				batadv_tp_send_msg(tp_vars, dev_addr,
-						   orig_node, recv_ack,
-						   packet_len, icmp->session,
-						   icmp->uid,
-						   jiffies_to_msecs(jiffies));
-				tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd,
-							       mss, mss);
-			} else {
-				tp_vars->fast_recovery = false;
-				/* set cwnd to the value of ss_threshold at the
-				 * moment that Fast Recovery was entered.
-				 * RFC6582, Section 3.2, step 3
-				 */
-				cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 0,
-						      mss);
-				tp_vars->cwnd = cwnd;
-			}
-			goto move_twnd;
-		}
-
-		if (recv_ack - atomic_read(&tp_vars->last_acked) >= mss)
-			batadv_tp_update_cwnd(tp_vars, mss);
-move_twnd:
-		/* move the Transmit Window */
-		atomic_set(&tp_vars->last_acked, recv_ack);
+		return BATADV_TP_ACK_REACTION_RESEND_WAKEUP;
+	}
+
+	/* count the acked data */
+	atomic64_add(recv_ack - tp_vars->last_acked, &tp_vars->tot_sent);
+
+	/* reset the duplicate ACKs counter */
+	tp_vars->dup_acks = 0;
+
+	if (tp_vars->fast_recovery) {
+		/* partial ACK */
+		if (batadv_seq_before(recv_ack, tp_vars->recover)) {
+			/* this is another hole in the window. React
+			 * immediately as specified by NewReno (see
+			 * Section 3.2 of RFC6582 for details)
+			 */
+			reaction = BATADV_TP_ACK_REACTION_RESEND_WAKEUP;
+			tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd,
+						       mss, mss);
+		} else {
+			tp_vars->fast_recovery = false;
+			/* set cwnd to the value of ss_threshold at the
+			 * moment that Fast Recovery was entered.
+			 * RFC6582, Section 3.2, step 3
+			 */
+			tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold,
+						       0, mss);
+			reaction = BATADV_TP_ACK_REACTION_WAKEUP;
+		}
+	} else {
+		if (recv_ack - tp_vars->last_acked >= mss)
+			batadv_tp_update_cwnd(tp_vars, mss);
+
+		reaction = BATADV_TP_ACK_REACTION_WAKEUP;
+	}
+
+	/* move the Transmit Window */
+	WRITE_ONCE(tp_vars->last_acked, recv_ack);
+
+	return reaction;
+}
+
+/**
+ * batadv_tp_recv_ack() - ACK receiving function
+ * @bat_priv: the bat priv with all the mesh interface information
+ * @skb: the buffer containing the received packet
+ *
+ * Process a received TP ACK packet
+ */
+static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
+			       const struct sk_buff *skb)
+{
+	struct batadv_hard_iface *primary_if = NULL;
+	struct batadv_orig_node *orig_node = NULL;
+	const struct batadv_icmp_tp_packet *icmp;
+	enum batadv_tp_ack_reaction reaction;
+	struct batadv_tp_sender *tp_vars;
+	size_t packet_len;
+	u32 recv_ack;
+	size_t mss;
+	u32 rtt;
+
+	packet_len = BATADV_TP_PLEN;
+	mss = BATADV_TP_PLEN;
+	packet_len += sizeof(struct batadv_unicast_packet);
+
+	icmp = (struct batadv_icmp_tp_packet *)skb->data;
+	recv_ack = ntohl(icmp->seqno);
+
+	/* find the tp_vars */
+	tp_vars = batadv_tp_list_find_sender_session(bat_priv, icmp->orig,
+						     icmp->session);
+	if (unlikely(!tp_vars))
+		return;
+
+	if (unlikely(batadv_tp_sender_stopped(tp_vars)))
+		goto out;
+
+	/* old ACK? silently drop it.. */
+	if (batadv_seq_before(recv_ack, READ_ONCE(tp_vars->last_acked)))
+		goto out;
+
+	primary_if = batadv_primary_if_get_selected(bat_priv);
+	if (unlikely(!primary_if))
+		goto out;
+
+	orig_node = batadv_orig_hash_find(bat_priv, icmp->orig);
+	if (unlikely(!orig_node))
+		goto out;
+
+	spin_lock_bh(&tp_vars->cwnd_lock);
+	/* update RTO with the new sampled RTT, if any */
+	rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp);
+	if (icmp->timestamp && rtt)
+		batadv_tp_update_rto(tp_vars, rtt);
+
+	reaction = batadv_tp_handle_ack(bat_priv, tp_vars, recv_ack, mss);
+	spin_unlock_bh(&tp_vars->cwnd_lock);
+
+	if (reaction == BATADV_TP_ACK_REACTION_OLD_ACK)
+		goto out;
+
+	/* ACK for new data... reset the timer */
+	batadv_tp_reset_sender_timer(tp_vars);
+
+	switch (reaction) {
+	default:
+	case BATADV_TP_ACK_REACTION_IGNORE:
+		goto out;
+	case BATADV_TP_ACK_REACTION_RESEND_WAKEUP:
+		batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
+				   orig_node, recv_ack, packet_len,
+				   icmp->session, icmp->uid,
+				   jiffies_to_msecs(jiffies));
+		fallthrough;
+	case BATADV_TP_ACK_REACTION_WAKEUP:
+		wake_up(&tp_vars->more_bytes);
+		break;
 	}
 
-	wake_up(&tp_vars->more_bytes);
 out:
 	batadv_hardif_put(primary_if);
 	batadv_orig_node_put(orig_node);
@@ -816,16 +868,19 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 static bool batadv_tp_avail(struct batadv_tp_sender *tp_vars,
 			    size_t payload_len)
 {
-	u32 last_sent = tp_vars->last_sent;
 	u32 win_left, win_limit;
 
-	win_limit = atomic_read(&tp_vars->last_acked) + tp_vars->cwnd;
+	spin_lock_bh(&tp_vars->cwnd_lock);
 
-	if (batadv_seq_before(last_sent, win_limit))
-		win_left = win_limit - last_sent;
+	win_limit = tp_vars->last_acked + tp_vars->cwnd;
+
+	if (batadv_seq_before(tp_vars->last_sent, win_limit))
+		win_left = win_limit - tp_vars->last_sent;
 	else
 		win_left = 0;
 
+	spin_unlock_bh(&tp_vars->cwnd_lock);
+
 	return win_left >= payload_len;
 }
 
@@ -864,6 +919,7 @@ static int batadv_tp_send(void *arg)
 	struct batadv_hard_iface *primary_if = NULL;
 	struct batadv_orig_node *orig_node = NULL;
 	size_t payload_len, packet_len;
+	u32 last_sent;
 	int err = 0;
 
 	orig_node = batadv_orig_hash_find(bat_priv, tp_vars->common.other_end);
@@ -905,10 +961,10 @@ static int batadv_tp_send(void *arg)
 		 * the size of the unicast header
 		 */
 		packet_len = payload_len + sizeof(struct batadv_unicast_packet);
+		last_sent = READ_ONCE(tp_vars->last_sent);
 
 		err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
-					 orig_node, tp_vars->last_sent,
-					 packet_len,
+					 orig_node, last_sent, packet_len,
 					 tp_vars->common.session, tp_vars->icmp_uid,
 					 jiffies_to_msecs(jiffies));
 
@@ -923,8 +979,12 @@ static int batadv_tp_send(void *arg)
 		}
 
 		/* right-shift the TWND */
-		if (!err)
-			tp_vars->last_sent += payload_len;
+		if (!err) {
+			spin_lock_bh(&tp_vars->cwnd_lock);
+			if (tp_vars->last_sent == last_sent)
+				WRITE_ONCE(tp_vars->last_sent, last_sent + payload_len);
+			spin_unlock_bh(&tp_vars->cwnd_lock);
+		}
 
 		cond_resched();
 	}
@@ -1041,9 +1101,9 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	memcpy(tp_vars->common.session, session_id, sizeof(session_id));
 	tp_vars->icmp_uid = icmp_uid;
 
-	tp_vars->last_sent = BATADV_TP_FIRST_SEQ;
-	atomic_set(&tp_vars->dup_acks, 0);
-	atomic_set(&tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
+	WRITE_ONCE(tp_vars->last_sent, BATADV_TP_FIRST_SEQ);
+	WRITE_ONCE(tp_vars->dup_acks, 0);
+	WRITE_ONCE(tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
 	tp_vars->fast_recovery = false;
 	tp_vars->recover = BATADV_TP_FIRST_SEQ;
 
@@ -1060,7 +1120,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	/* RTO initial value is 3 seconds.
 	 * Details in Section 2.1 of RFC6298
 	 */
-	tp_vars->rto = 1000;
+	WRITE_ONCE(tp_vars->rto, 1000);
 	tp_vars->srtt = 0;
 	tp_vars->rttvar = 0;
 
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 4880bf3e..8064f696 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1348,7 +1348,7 @@ struct batadv_tp_sender {
 	u32 last_sent;
 
 	/** @fast_recovery: true if in Fast Recovery mode */
-	unsigned char fast_recovery:1;
+	bool fast_recovery:1;
 
 	/** @recover: last sent seqno when entering Fast Recovery */
 	u32 recover;
@@ -1371,7 +1371,7 @@ struct batadv_tp_sender {
 	/** @cwnd: current size of the congestion window */
 	u32 cwnd;
 
-	/** @cwnd_lock: lock do protect @cwnd & @dec_cwnd */
+	/** @cwnd_lock: lock do protect congestion control variables */
 	spinlock_t cwnd_lock;
 
 	/**
@@ -1381,13 +1381,13 @@ struct batadv_tp_sender {
 	u32 ss_threshold;
 
 	/** @last_acked: last acked byte */
-	atomic_t last_acked;
+	u32 last_acked;
 
 	/** @tot_sent: amount of data sent/ACKed so far */
 	atomic64_t tot_sent;
 
 	/** @dup_acks: duplicate ACKs counter */
-	atomic_t dup_acks;
+	u8 dup_acks;
 
 	/** @rto: sender timeout */
 	u32 rto;

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH batadv v4 6/6] batman-adv: tp_meter: consolidate congestion control variables
  2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
                   ` (4 preceding siblings ...)
  2026-05-31  8:28 ` [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables Sven Eckelmann
@ 2026-05-31  8:28 ` Sven Eckelmann
  5 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  8:28 UTC (permalink / raw)
  To: b.a.t.m.a.n; +Cc: Sven Eckelmann

Congestion control variables in batadv_tp_sender were scattered across the
struct without clear grouping, making it difficult to reason about which
fields require cwnd_lock (now "cc_lock") protection. These should be
combined in a structure to make it more easily visible which variable
should be read/modified with the cc_lock held.

Signed-off-by: Sven Eckelmann <sven@narfation.org>
---
 net/batman-adv/tp_meter.c | 168 +++++++++++++++++++++++-----------------------
 net/batman-adv/types.h    |  80 ++++++++++++----------
 2 files changed, 127 insertions(+), 121 deletions(-)

diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c
index e7a2dde1..c621683b 100644
--- a/net/batman-adv/tp_meter.c
+++ b/net/batman-adv/tp_meter.c
@@ -143,23 +143,23 @@ static u32 batadv_tp_cwnd(u32 base, u32 increment, u32 min)
  * increased by MSS * MSS / CWND for every unique received ACK
  */
 static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss)
-	__must_hold(&tp_vars->cwnd_lock)
+	__must_hold(&tp_vars->cc_lock)
 {
 	/* slow start... */
-	if (tp_vars->cwnd <= tp_vars->ss_threshold) {
-		tp_vars->dec_cwnd = 0;
-		tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss);
+	if (tp_vars->cc.cwnd <= tp_vars->cc.ss_threshold) {
+		tp_vars->cc.dec_cwnd = 0;
+		tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd, mss, mss);
 		return;
 	}
 
 	/* increment CWND at least of 1 (section 3.1 of RFC5681) */
-	tp_vars->dec_cwnd += max_t(u32, 1U << 3,
-				   ((mss * mss) << 6) / (tp_vars->cwnd << 3));
-	if (tp_vars->dec_cwnd < (mss << 3))
+	tp_vars->cc.dec_cwnd += max_t(u32, 1U << 3,
+				      ((mss * mss) << 6) / (tp_vars->cc.cwnd << 3));
+	if (tp_vars->cc.dec_cwnd < (mss << 3))
 		return;
 
-	tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd, mss, mss);
-	tp_vars->dec_cwnd = 0;
+	tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd, mss, mss);
+	tp_vars->cc.dec_cwnd = 0;
 }
 
 /**
@@ -169,7 +169,7 @@ static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss)
  */
 static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars,
 				 u32 new_rtt)
-	__must_hold(&tp_vars->cwnd_lock)
+	__must_hold(&tp_vars->cc_lock)
 {
 	long m = new_rtt;
 
@@ -179,24 +179,24 @@ static void batadv_tp_update_rto(struct batadv_tp_sender *tp_vars,
 	 * It's tricky to understand. Don't lose hair please.
 	 * Inspired by tcp_rtt_estimator() tcp_input.c
 	 */
-	if (tp_vars->srtt != 0) {
-		m -= (tp_vars->srtt >> 3); /* m is now error in rtt est */
-		tp_vars->srtt += m; /* rtt = 7/8 srtt + 1/8 new */
+	if (tp_vars->cc.srtt != 0) {
+		m -= (tp_vars->cc.srtt >> 3); /* m is now error in rtt est */
+		tp_vars->cc.srtt += m; /* rtt = 7/8 srtt + 1/8 new */
 		if (m < 0)
 			m = -m;
 
-		m -= (tp_vars->rttvar >> 2);
-		tp_vars->rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */
+		m -= (tp_vars->cc.rttvar >> 2);
+		tp_vars->cc.rttvar += m; /* mdev ~= 3/4 rttvar + 1/4 new */
 	} else {
 		/* first measure getting in */
-		tp_vars->srtt = m << 3; /* take the measured time to be srtt */
-		tp_vars->rttvar = m << 1; /* new_rtt / 2 */
+		tp_vars->cc.srtt = m << 3; /* take the measured time to be srtt */
+		tp_vars->cc.rttvar = m << 1; /* new_rtt / 2 */
 	}
 
 	/* rto = srtt + 4 * rttvar.
 	 * rttvar is scaled by 4, therefore doesn't need to be multiplied
 	 */
-	WRITE_ONCE(tp_vars->rto, (tp_vars->srtt >> 3) + tp_vars->rttvar);
+	WRITE_ONCE(tp_vars->cc.rto, (tp_vars->cc.srtt >> 3) + tp_vars->cc.rttvar);
 }
 
 /**
@@ -447,11 +447,11 @@ static void batadv_tp_sender_end(struct batadv_priv *bat_priv,
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Last timing stats: SRTT=%ums RTTVAR=%ums RTO=%ums\n",
-		   tp_vars->srtt >> 3, tp_vars->rttvar >> 2, tp_vars->rto);
+		   tp_vars->cc.srtt >> 3, tp_vars->cc.rttvar >> 2, tp_vars->cc.rto);
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Final values: cwnd=%u ss_threshold=%u\n",
-		   tp_vars->cwnd, tp_vars->ss_threshold);
+		   tp_vars->cc.cwnd, tp_vars->cc.ss_threshold);
 
 	session_cookie = batadv_tp_session_cookie(tp_vars->common.session,
 						  tp_vars->icmp_uid);
@@ -518,7 +518,7 @@ static void batadv_tp_reset_sender_timer(struct batadv_tp_sender *tp_vars)
 		return;
 
 	mod_timer(&tp_vars->common.timer,
-		  jiffies + msecs_to_jiffies(READ_ONCE(tp_vars->rto)));
+		  jiffies + msecs_to_jiffies(READ_ONCE(tp_vars->cc.rto)));
 }
 
 /**
@@ -537,11 +537,11 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 	if (batadv_tp_sender_stopped(tp_vars))
 		return;
 
-	spin_lock_bh(&tp_vars->cwnd_lock);
+	spin_lock_bh(&tp_vars->cc_lock);
 
 	/* if the user waited long enough...shutdown the test */
-	if (unlikely(tp_vars->rto >= BATADV_TP_MAX_RTO)) {
-		spin_unlock_bh(&tp_vars->cwnd_lock);
+	if (unlikely(tp_vars->cc.rto >= BATADV_TP_MAX_RTO)) {
+		spin_unlock_bh(&tp_vars->cc_lock);
 		batadv_tp_sender_shutdown(tp_vars,
 					  BATADV_TP_REASON_DST_UNREACHABLE);
 		return;
@@ -550,22 +550,22 @@ static void batadv_tp_sender_timeout(struct timer_list *t)
 	/* RTO exponential backoff
 	 * Details in Section 5.5 of RFC6298
 	 */
-	WRITE_ONCE(tp_vars->rto, tp_vars->rto * 2);
+	WRITE_ONCE(tp_vars->cc.rto, tp_vars->cc.rto * 2);
 
-	tp_vars->ss_threshold = tp_vars->cwnd >> 1;
-	if (tp_vars->ss_threshold < BATADV_TP_PLEN * 2)
-		tp_vars->ss_threshold = BATADV_TP_PLEN * 2;
+	tp_vars->cc.ss_threshold = tp_vars->cc.cwnd >> 1;
+	if (tp_vars->cc.ss_threshold < BATADV_TP_PLEN * 2)
+		tp_vars->cc.ss_threshold = BATADV_TP_PLEN * 2;
 
 	batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 		   "Meter: RTO fired during test towards %pM! cwnd=%u new ss_thr=%u, resetting last_sent to %u\n",
-		   tp_vars->common.other_end, tp_vars->cwnd, tp_vars->ss_threshold,
-		   tp_vars->last_acked);
+		   tp_vars->common.other_end, tp_vars->cc.cwnd, tp_vars->cc.ss_threshold,
+		   tp_vars->cc.last_acked);
 
-	tp_vars->cwnd = BATADV_TP_PLEN * 3;
+	tp_vars->cc.cwnd = BATADV_TP_PLEN * 3;
 
-	WRITE_ONCE(tp_vars->last_sent, tp_vars->last_acked);
+	WRITE_ONCE(tp_vars->cc.last_sent, tp_vars->cc.last_acked);
 
-	spin_unlock_bh(&tp_vars->cwnd_lock);
+	spin_unlock_bh(&tp_vars->cc_lock);
 
 	/* resend the non-ACKed packets.. */
 	wake_up(&tp_vars->more_bytes);
@@ -695,81 +695,81 @@ static enum batadv_tp_ack_reaction
 batadv_tp_handle_ack(struct batadv_priv *bat_priv,
 		     struct batadv_tp_sender *tp_vars,
 		     u32 recv_ack, size_t mss)
-	__must_hold(&tp_vars->cwnd_lock)
+	__must_hold(&tp_vars->cc_lock)
 {
 	static enum batadv_tp_ack_reaction reaction;
 
-	if (batadv_seq_before(recv_ack, tp_vars->last_acked))
+	if (batadv_seq_before(recv_ack, tp_vars->cc.last_acked))
 		return BATADV_TP_ACK_REACTION_OLD_ACK;
 
 	/* check if this ACK is a duplicate */
-	if (tp_vars->last_acked == recv_ack) {
+	if (tp_vars->cc.last_acked == recv_ack) {
 		/* if this is the third duplicate ACK do Fast Retransmit */
-		if (tp_vars->dup_acks > 3)
+		if (tp_vars->cc.dup_acks > 3)
 			return BATADV_TP_ACK_REACTION_IGNORE;
 
-		tp_vars->dup_acks++;
-		if (tp_vars->dup_acks != 3)
+		tp_vars->cc.dup_acks++;
+		if (tp_vars->cc.dup_acks != 3)
 			return BATADV_TP_ACK_REACTION_IGNORE;
 
-		if (!batadv_seq_before(recv_ack, tp_vars->recover))
+		if (!batadv_seq_before(recv_ack, tp_vars->cc.recover))
 			return BATADV_TP_ACK_REACTION_IGNORE;
 
 		/* Fast Recovery */
-		tp_vars->fast_recovery = true;
+		tp_vars->cc.fast_recovery = true;
 
 		/* Set recover to the last outstanding seqno when Fast Recovery
 		 * is entered. RFC6582, Section 3.2, step 1
 		 */
-		tp_vars->recover = tp_vars->last_sent;
-		tp_vars->ss_threshold = tp_vars->cwnd >> 1;
+		tp_vars->cc.recover = tp_vars->cc.last_sent;
+		tp_vars->cc.ss_threshold = tp_vars->cc.cwnd >> 1;
 		batadv_dbg(BATADV_DBG_TP_METER, bat_priv,
 			   "Meter: Fast Recovery, (cur cwnd=%u) ss_thr=%u last_sent=%u recv_ack=%u\n",
-			   tp_vars->cwnd, tp_vars->ss_threshold,
-			   tp_vars->last_sent, recv_ack);
-		tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold, 3 * mss,
-					       mss);
-		tp_vars->dec_cwnd = 0;
-		WRITE_ONCE(tp_vars->last_sent, recv_ack);
+			   tp_vars->cc.cwnd, tp_vars->cc.ss_threshold,
+			   tp_vars->cc.last_sent, recv_ack);
+		tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.ss_threshold, 3 * mss,
+						  mss);
+		tp_vars->cc.dec_cwnd = 0;
+		WRITE_ONCE(tp_vars->cc.last_sent, recv_ack);
 
 		return BATADV_TP_ACK_REACTION_RESEND_WAKEUP;
 	}
 
 	/* count the acked data */
-	atomic64_add(recv_ack - tp_vars->last_acked, &tp_vars->tot_sent);
+	atomic64_add(recv_ack - tp_vars->cc.last_acked, &tp_vars->tot_sent);
 
 	/* reset the duplicate ACKs counter */
-	tp_vars->dup_acks = 0;
+	tp_vars->cc.dup_acks = 0;
 
-	if (tp_vars->fast_recovery) {
+	if (tp_vars->cc.fast_recovery) {
 		/* partial ACK */
-		if (batadv_seq_before(recv_ack, tp_vars->recover)) {
+		if (batadv_seq_before(recv_ack, tp_vars->cc.recover)) {
 			/* this is another hole in the window. React
 			 * immediately as specified by NewReno (see
 			 * Section 3.2 of RFC6582 for details)
 			 */
 			reaction = BATADV_TP_ACK_REACTION_RESEND_WAKEUP;
-			tp_vars->cwnd = batadv_tp_cwnd(tp_vars->cwnd,
-						       mss, mss);
+			tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.cwnd,
+							  mss, mss);
 		} else {
-			tp_vars->fast_recovery = false;
+			tp_vars->cc.fast_recovery = false;
 			/* set cwnd to the value of ss_threshold at the
 			 * moment that Fast Recovery was entered.
 			 * RFC6582, Section 3.2, step 3
 			 */
-			tp_vars->cwnd = batadv_tp_cwnd(tp_vars->ss_threshold,
-						       0, mss);
+			tp_vars->cc.cwnd = batadv_tp_cwnd(tp_vars->cc.ss_threshold,
+							  0, mss);
 			reaction = BATADV_TP_ACK_REACTION_WAKEUP;
 		}
 	} else {
-		if (recv_ack - tp_vars->last_acked >= mss)
+		if (recv_ack - tp_vars->cc.last_acked >= mss)
 			batadv_tp_update_cwnd(tp_vars, mss);
 
 		reaction = BATADV_TP_ACK_REACTION_WAKEUP;
 	}
 
 	/* move the Transmit Window */
-	WRITE_ONCE(tp_vars->last_acked, recv_ack);
+	WRITE_ONCE(tp_vars->cc.last_acked, recv_ack);
 
 	return reaction;
 }
@@ -811,7 +811,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 		goto out;
 
 	/* old ACK? silently drop it.. */
-	if (batadv_seq_before(recv_ack, READ_ONCE(tp_vars->last_acked)))
+	if (batadv_seq_before(recv_ack, READ_ONCE(tp_vars->cc.last_acked)))
 		goto out;
 
 	primary_if = batadv_primary_if_get_selected(bat_priv);
@@ -822,14 +822,14 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv,
 	if (unlikely(!orig_node))
 		goto out;
 
-	spin_lock_bh(&tp_vars->cwnd_lock);
+	spin_lock_bh(&tp_vars->cc_lock);
 	/* update RTO with the new sampled RTT, if any */
 	rtt = jiffies_to_msecs(jiffies) - ntohl(icmp->timestamp);
 	if (icmp->timestamp && rtt)
 		batadv_tp_update_rto(tp_vars, rtt);
 
 	reaction = batadv_tp_handle_ack(bat_priv, tp_vars, recv_ack, mss);
-	spin_unlock_bh(&tp_vars->cwnd_lock);
+	spin_unlock_bh(&tp_vars->cc_lock);
 
 	if (reaction == BATADV_TP_ACK_REACTION_OLD_ACK)
 		goto out;
@@ -870,16 +870,16 @@ static bool batadv_tp_avail(struct batadv_tp_sender *tp_vars,
 {
 	u32 win_left, win_limit;
 
-	spin_lock_bh(&tp_vars->cwnd_lock);
+	spin_lock_bh(&tp_vars->cc_lock);
 
-	win_limit = tp_vars->last_acked + tp_vars->cwnd;
+	win_limit = tp_vars->cc.last_acked + tp_vars->cc.cwnd;
 
-	if (batadv_seq_before(tp_vars->last_sent, win_limit))
-		win_left = win_limit - tp_vars->last_sent;
+	if (batadv_seq_before(tp_vars->cc.last_sent, win_limit))
+		win_left = win_limit - tp_vars->cc.last_sent;
 	else
 		win_left = 0;
 
-	spin_unlock_bh(&tp_vars->cwnd_lock);
+	spin_unlock_bh(&tp_vars->cc_lock);
 
 	return win_left >= payload_len;
 }
@@ -961,7 +961,7 @@ static int batadv_tp_send(void *arg)
 		 * the size of the unicast header
 		 */
 		packet_len = payload_len + sizeof(struct batadv_unicast_packet);
-		last_sent = READ_ONCE(tp_vars->last_sent);
+		last_sent = READ_ONCE(tp_vars->cc.last_sent);
 
 		err = batadv_tp_send_msg(tp_vars, primary_if->net_dev->dev_addr,
 					 orig_node, last_sent, packet_len,
@@ -980,10 +980,10 @@ static int batadv_tp_send(void *arg)
 
 		/* right-shift the TWND */
 		if (!err) {
-			spin_lock_bh(&tp_vars->cwnd_lock);
-			if (tp_vars->last_sent == last_sent)
-				WRITE_ONCE(tp_vars->last_sent, last_sent + payload_len);
-			spin_unlock_bh(&tp_vars->cwnd_lock);
+			spin_lock_bh(&tp_vars->cc_lock);
+			if (tp_vars->cc.last_sent == last_sent)
+				WRITE_ONCE(tp_vars->cc.last_sent, last_sent + payload_len);
+			spin_unlock_bh(&tp_vars->cc_lock);
 		}
 
 		cond_resched();
@@ -1101,28 +1101,28 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	memcpy(tp_vars->common.session, session_id, sizeof(session_id));
 	tp_vars->icmp_uid = icmp_uid;
 
-	WRITE_ONCE(tp_vars->last_sent, BATADV_TP_FIRST_SEQ);
-	WRITE_ONCE(tp_vars->dup_acks, 0);
-	WRITE_ONCE(tp_vars->last_acked, BATADV_TP_FIRST_SEQ);
-	tp_vars->fast_recovery = false;
-	tp_vars->recover = BATADV_TP_FIRST_SEQ;
+	WRITE_ONCE(tp_vars->cc.last_sent, BATADV_TP_FIRST_SEQ);
+	WRITE_ONCE(tp_vars->cc.dup_acks, 0);
+	WRITE_ONCE(tp_vars->cc.last_acked, BATADV_TP_FIRST_SEQ);
+	tp_vars->cc.fast_recovery = false;
+	tp_vars->cc.recover = BATADV_TP_FIRST_SEQ;
 
 	/* initialise the CWND to 3*MSS (Section 3.1 in RFC5681).
 	 * For batman-adv the MSS is the size of the payload received by the
 	 * mesh_interface, hence its MTU
 	 */
-	tp_vars->cwnd = BATADV_TP_PLEN * 3;
+	tp_vars->cc.cwnd = BATADV_TP_PLEN * 3;
 	/* at the beginning initialise the SS threshold to the biggest possible
 	 * window size, hence the AWND size
 	 */
-	tp_vars->ss_threshold = BATADV_TP_AWND;
+	tp_vars->cc.ss_threshold = BATADV_TP_AWND;
 
 	/* RTO initial value is 3 seconds.
 	 * Details in Section 2.1 of RFC6298
 	 */
-	WRITE_ONCE(tp_vars->rto, 1000);
-	tp_vars->srtt = 0;
-	tp_vars->rttvar = 0;
+	WRITE_ONCE(tp_vars->cc.rto, 1000);
+	tp_vars->cc.srtt = 0;
+	tp_vars->cc.rttvar = 0;
 
 	atomic64_set(&tp_vars->tot_sent, 0);
 
@@ -1138,7 +1138,7 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst,
 	spin_lock_init(&tp_vars->common.unacked_lock);
 	INIT_LIST_HEAD(&tp_vars->common.unacked_list);
 
-	spin_lock_init(&tp_vars->cwnd_lock);
+	spin_lock_init(&tp_vars->cc_lock);
 
 	tp_vars->prerandom_offset = 0;
 	spin_lock_init(&tp_vars->prerandom_lock);
diff --git a/net/batman-adv/types.h b/net/batman-adv/types.h
index 8064f696..53dc42e7 100644
--- a/net/batman-adv/types.h
+++ b/net/batman-adv/types.h
@@ -1328,6 +1328,45 @@ struct batadv_tp_vars_common {
 	struct rcu_head rcu;
 };
 
+/** struct batadv_tp_sender_cc - congestion control variables */
+struct batadv_tp_sender_cc {
+	/** @fast_recovery: true if in Fast Recovery mode */
+	bool fast_recovery:1;
+
+	/** @dup_acks: duplicate ACKs counter */
+	u8 dup_acks;
+
+	/** @dec_cwnd: decimal part of the cwnd used during linear growth */
+	u16 dec_cwnd;
+
+	/** @cwnd: current size of the congestion window */
+	u32 cwnd;
+
+	/**
+	 * @ss_threshold: Slow Start threshold. Once cwnd exceeds this value the
+	 *  connection switches to the Congestion Avoidance state
+	 */
+	u32 ss_threshold;
+
+	/** @last_acked: last acked byte */
+	u32 last_acked;
+
+	/** @last_sent: last sent byte, not yet acked */
+	u32 last_sent;
+
+	/** @recover: last sent seqno when entering Fast Recovery */
+	u32 recover;
+
+	/** @rto: sender timeout */
+	u32 rto;
+
+	/** @srtt: smoothed RTT scaled by 2^3 */
+	u32 srtt;
+
+	/** @rttvar: RTT variation scaled by 2^2 */
+	u32 rttvar;
+};
+
 /**
  * struct batadv_tp_sender - sender tp meter private variables per session
  */
@@ -1344,15 +1383,6 @@ struct batadv_tp_sender {
 	 */
 	atomic_t send_result;
 
-	/** @last_sent: last sent byte, not yet acked */
-	u32 last_sent;
-
-	/** @fast_recovery: true if in Fast Recovery mode */
-	bool fast_recovery:1;
-
-	/** @recover: last sent seqno when entering Fast Recovery */
-	u32 recover;
-
 	/** @finish_work: work item for the finishing procedure */
 	struct delayed_work finish_work;
 
@@ -1365,39 +1395,15 @@ struct batadv_tp_sender {
 	/** @icmp_uid: local ICMP "socket" index */
 	u8 icmp_uid;
 
-	/** @dec_cwnd: decimal part of the cwnd used during linear growth */
-	u16 dec_cwnd;
+	/** @cc: congestion control variables */
+	struct batadv_tp_sender_cc cc;
 
-	/** @cwnd: current size of the congestion window */
-	u32 cwnd;
-
-	/** @cwnd_lock: lock do protect congestion control variables */
-	spinlock_t cwnd_lock;
-
-	/**
-	 * @ss_threshold: Slow Start threshold. Once cwnd exceeds this value the
-	 *  connection switches to the Congestion Avoidance state
-	 */
-	u32 ss_threshold;
-
-	/** @last_acked: last acked byte */
-	u32 last_acked;
+	/** @cc_lock: lock do protect @cc */
+	spinlock_t cc_lock;
 
 	/** @tot_sent: amount of data sent/ACKed so far */
 	atomic64_t tot_sent;
 
-	/** @dup_acks: duplicate ACKs counter */
-	u8 dup_acks;
-
-	/** @rto: sender timeout */
-	u32 rto;
-
-	/** @srtt: smoothed RTT scaled by 2^3 */
-	u32 srtt;
-
-	/** @rttvar: RTT variation scaled by 2^2 */
-	u32 rttvar;
-
 	/**
 	 * @more_bytes: waiting queue anchor when waiting for more ack/retry
 	 *  timeout

-- 
2.47.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables
  2026-05-31  8:28 ` [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables Sven Eckelmann
@ 2026-05-31  9:55   ` Sven Eckelmann
  0 siblings, 0 replies; 8+ messages in thread
From: Sven Eckelmann @ 2026-05-31  9:55 UTC (permalink / raw)
  To: b.a.t.m.a.n

[-- Attachment #1: Type: text/plain, Size: 319 bytes --]

On Sunday, 31 May 2026 10:28:55 CEST Sven Eckelmann wrote:
> +       static enum batadv_tp_ack_reaction reaction;

This should have been of course be non-static variable. v5 will come 
tomorrow....

Will not be applied anyway before v2 of the net-next.git PR was merged (and of 
course submitted first).

Regards,
	Sven

[-- Attachment #2: This is a digitally signed message part. --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-05-31  9:56 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-31  8:28 [PATCH batadv v4 0/6] batman-adv: tp_meter: post fixes cleanups Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 1/6] batman-adv: tp_meter: keep unacked list in ascending ordered Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 2/6] batman-adv: tp_meter: initialize dup_acks explicitly Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 3/6] batman-adv: tp_meter: avoid window underflow Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 4/6] batman-adv: tp_meter: split vars into sender and receiver types Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 5/6] batman-adv: tp_meter: use locking for all congestion control variables Sven Eckelmann
2026-05-31  9:55   ` Sven Eckelmann
2026-05-31  8:28 ` [PATCH batadv v4 6/6] batman-adv: tp_meter: consolidate " Sven Eckelmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox