From 5aa8651527ea0b610e7a09fb3b8204c1398b9525 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 28 May 2026 21:14:39 +0200 Subject: [PATCH 01/15] batman-adv: tp_meter: keep unacked list in ascending ordered 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. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- 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 aefe757277b2..0e39ea33e5f2 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; } From b2b68b32a715e0328662801576974aa37b942b00 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 28 May 2026 21:14:39 +0200 Subject: [PATCH 02/15] batman-adv: tp_meter: initialize dup_acks explicitly 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. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- 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 0e39ea33e5f2..8d7308327a9b 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; From febfb1b86224489535312296ecfa3d4bf467f339 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 28 May 2026 21:14:39 +0200 Subject: [PATCH 03/15] batman-adv: tp_meter: initialize dec_cwnd explicitly When batadv_tp_update_cwnd() is called, dec_cwnd is increased. But dec_cwnd is only initialixed (to 0) when a duplicate Ack was received or when cwnd is below the ss_threshold. Just initialize the cwnd during the initialization to avoid any potential access of uninitialized data. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 8d7308327a9b..beabc264a4f1 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1055,6 +1055,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, * mesh_interface, hence its MTU */ tp_vars->cwnd = BATADV_TP_PLEN * 3; + tp_vars->dec_cwnd = 0; + /* at the beginning initialise the SS threshold to the biggest possible * window size, hence the AWND size */ From 765947b81fb54b6ebb0bc1cfe55c0fa399e002b8 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Thu, 28 May 2026 21:14:39 +0200 Subject: [PATCH 04/15] batman-adv: tp_meter: avoid window underflow 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. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- 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 beabc264a4f1..9ecbc6023cfc 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 = READ_ONCE(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; } From 33ccd52f3cc9ed46ce395199f89aa3234dc83314 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 31 May 2026 12:38:05 +0200 Subject: [PATCH 05/15] batman-adv: tp_meter: avoid divide-by-zero for dec_cwnd The cwnd is always MSS <= cwnd <= 0x20000000. But the calculation in batadv_tp_update_cwnd() assumes unsigned 32 bit arithmetics. ((mss * 8) ** 2) / (cwnd * 8) In case cwnd is actually 0x20000000, it will be shifted by 3 bit to the left end up at 0x100000000 or U32_MAX + 1. It will therefore wrap around and be 0 - resulting in: ((mss * 8) ** 2) / 0 This is of course invalid and cannot be calculated. The calculation should must be simplified to avoid this overflow: (mss ** 2) * 8 / cwnd It will keep the precision enhancement from the scaling (by 8) but avoid the overflow in the divisor. In theory, there could still be an overflow in the dividend. It is at the moment fixed to BATADV_TP_PLEN in batadv_tp_recv_ack() - so it is not an imminent problem. But allowing it to use the whole u32 bit range, would mean that it can still use up to 67 bits. To keep this calculation safe for 32 bit arithmetic, mss must never use more than floor((32 - 3) / 2) bits - or in other words: must never be larger than 16383. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 9ecbc6023cfc..1655f181c929 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -154,9 +154,12 @@ static void batadv_tp_update_cwnd(struct batadv_tp_vars *tp_vars, u32 mss) return; } + /* prevent overflow in (mss * mss) << 3 */ + mss = min_t(u32, mss, (1U << 14) - 1); + /* 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)); + ((mss * mss) << 3) / tp_vars->cwnd); if (tp_vars->dec_cwnd < (mss << 3)) { spin_unlock_bh(&tp_vars->cwnd_lock); return; From 2b0d08f08ed3b2174f05c43089ec65f3543a025b Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 1 Jun 2026 11:00:23 +0200 Subject: [PATCH 06/15] batman-adv: tp_meter: fix fast recovery precondition The fast recovery precondition checks if the recover (initialized to BATADV_TP_FIRST_SEQ) is bigger than the received ack. But since recover is only updated when this check is successful, it will never enter the fast recovery mode. According to RFC6582 Section 3.2 step 2, the check should actually be different: > When the third duplicate ACK is received, the TCP sender first > checks the value of recover to see if the Cumulative > Acknowledgment field covers more than recover The precondition must therefore check if recover is smaller than the received ack - basically swapping the operands of the current check. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- 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 1655f181c929..ae6acbc60c8e 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (recv_ack >= tp_vars->recover) + if (tp_vars->recover >= recv_ack) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From f54c85ed42a1b27a516cf2a4728f5a612b799e07 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Mon, 1 Jun 2026 11:47:29 +0200 Subject: [PATCH 07/15] batman-adv: tp_meter: handle seqno wrap-around for fast recovery detection The recover variable and the last_sent sequence number are initialized on purpose as a really high value which will wrap-around after the first 2000 bytes. The fast recovery precondition must therefore not use simple integer comparisons but use helpers which are aware of the sequence number wrap-arounds. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- 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 ae6acbc60c8e..3d3d06c88e1c 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -733,7 +733,7 @@ static void batadv_tp_recv_ack(struct batadv_priv *bat_priv, if (atomic_read(&tp_vars->dup_acks) != 3) goto out; - if (tp_vars->recover >= recv_ack) + if (!batadv_seq_before(tp_vars->recover, recv_ack)) goto out; /* if this is the third duplicate ACK do Fast Retransmit */ From 15ccbf685222274f5add1387af58c2a41a95f81e Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 2 Jun 2026 16:51:42 +0200 Subject: [PATCH 08/15] batman-adv: tp_meter: add only finished tp_vars to lists When the receiver variables (aka "session") are initialized, then they are added to the list of sessions before the timer is set up. A RCU protected reader could therefore find the entry and run mod_setup before batadv_tp_init_recv() finished the timer initialization. The same is true for batadv_tp_start(), which must first initialize the finish_work and the test_length to avoid a similar problem. Cc: stable@kernel.org Fixes: 33a3bb4a3345 ("batman-adv: throughput meter implementation") Signed-off-by: Sven Eckelmann --- net/batman-adv/tp_meter.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index 3d3d06c88e1c..5498cdc972e9 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -1096,21 +1096,21 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, 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); - spin_unlock_bh(&bat_priv->tp_list_lock); - tp_vars->test_length = test_length; if (!tp_vars->test_length) tp_vars->test_length = BATADV_TP_DEF_TEST_LENGTH; + /* init work item for finished tp tests */ + INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); + + kref_get(&tp_vars->refcount); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + spin_unlock_bh(&bat_priv->tp_list_lock); + batadv_dbg(BATADV_DBG_TP_METER, bat_priv, "Meter: starting throughput meter towards %pM (length=%ums)\n", dst, test_length); - /* init work item for finished tp tests */ - INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); - /* start tp kthread. This way the write() call issued from userspace can * happily return and avoid to block */ @@ -1430,10 +1430,10 @@ batadv_tp_init_recv(struct batadv_priv *bat_priv, INIT_LIST_HEAD(&tp_vars->unacked_list); kref_get(&tp_vars->refcount); - hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); + timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); kref_get(&tp_vars->refcount); - timer_setup(&tp_vars->timer, batadv_tp_receiver_shutdown, 0); + hlist_add_head_rcu(&tp_vars->list, &bat_priv->tp_list); batadv_tp_reset_receiver_timer(tp_vars); From feb4a390a540eec60a2c39f7b893f19fdf57b3a5 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sat, 9 May 2026 18:33:16 +0200 Subject: [PATCH 09/15] batman-adv: tp_meter: split vars into sender and receiver types 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 --- 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 60d4f60066c8..3c4572284b53 100644 --- a/net/batman-adv/main.c +++ b/net/batman-adv/main.c @@ -194,7 +194,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 5498cdc972e9..c088d8880453 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); @@ -176,7 +176,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; @@ -255,35 +255,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; @@ -304,10 +299,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; } @@ -315,12 +316,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 @@ -328,28 +328,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; @@ -361,16 +358,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! @@ -386,23 +383,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; @@ -414,27 +413,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); } /** @@ -443,7 +442,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; @@ -452,7 +451,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", @@ -462,11 +461,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), @@ -478,7 +477,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); @@ -490,7 +489,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; } @@ -502,10 +501,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); @@ -517,7 +516,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... @@ -526,7 +525,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)); } /** @@ -539,8 +538,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; @@ -565,7 +564,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; @@ -585,7 +584,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; @@ -628,7 +627,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) @@ -684,7 +683,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; @@ -696,8 +695,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; @@ -807,7 +806,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); } /** @@ -817,7 +816,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 = READ_ONCE(tp_vars->last_sent); @@ -844,7 +843,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; @@ -863,20 +862,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); @@ -919,7 +912,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 */ @@ -947,7 +940,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; } @@ -957,24 +950,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); @@ -995,7 +988,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; @@ -1045,11 +1038,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; @@ -1079,17 +1071,17 @@ 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); @@ -1103,8 +1095,8 @@ void batadv_tp_start(struct batadv_priv *bat_priv, const u8 *dst, /* init work item for finished tp tests */ INIT_DELAYED_WORK(&tp_vars->finish_work, batadv_tp_sender_finish); - 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); batadv_dbg(BATADV_DBG_TP_METER, bat_priv, @@ -1117,7 +1109,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); } /** @@ -1130,7 +1122,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); @@ -1139,7 +1131,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"); @@ -1147,20 +1139,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)); } @@ -1171,11 +1218,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, @@ -1187,22 +1234,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); } /** @@ -1286,7 +1334,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; @@ -1304,10 +1352,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; } @@ -1318,7 +1366,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) @@ -1343,10 +1391,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; } @@ -1356,7 +1404,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; @@ -1364,8 +1412,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 @@ -1381,7 +1429,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); } /** @@ -1391,18 +1439,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 (READ_ONCE(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; @@ -1418,22 +1466,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); - 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); - 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); batadv_tp_reset_receiver_timer(tp_vars); @@ -1454,7 +1501,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; @@ -1472,8 +1519,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", @@ -1517,7 +1564,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); } /** @@ -1557,45 +1604,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 19c7316889b6..f7817a68a29c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1336,21 +1336,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) */ @@ -1359,14 +1348,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 @@ -1374,8 +1383,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; @@ -1386,14 +1401,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; @@ -1412,21 +1422,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; @@ -1447,26 +1448,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; }; /** @@ -1643,8 +1641,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; From a7eff940247168919bb670f04f3d2e8e8101c1b0 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 12 May 2026 19:37:05 +0200 Subject: [PATCH 10/15] batman-adv: tp_meter: use locking for all congestion control variables 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 --- 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 c088d8880453..e9ba1c45a285 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -143,14 +143,12 @@ 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; } @@ -160,15 +158,11 @@ static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss) /* increment CWND at least of 1 (section 3.1 of RFC5681) */ tp_vars->dec_cwnd += max_t(u32, 1U << 3, ((mss * mss) << 3) / tp_vars->cwnd); - 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); } /** @@ -178,6 +172,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; @@ -197,14 +192,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); } /** @@ -525,7 +520,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))); } /** @@ -544,8 +540,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; @@ -554,9 +553,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) @@ -565,14 +562,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); @@ -671,80 +669,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; + 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 (tp_vars->last_acked == recv_ack) { + /* if this is the third duplicate ACK do Fast Retransmit */ + if (tp_vars->dup_acks > 3) + return BATADV_TP_ACK_REACTION_IGNORE; + + tp_vars->dup_acks++; + if (tp_vars->dup_acks != 3) + return BATADV_TP_ACK_REACTION_IGNORE; if (!batadv_seq_before(tp_vars->recover, recv_ack)) - goto out; - - /* 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)); - - spin_lock_bh(&tp_vars->cwnd_lock); + 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 */ @@ -757,52 +733,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); @@ -819,16 +871,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 = READ_ONCE(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; } @@ -867,6 +922,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); @@ -908,10 +964,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)); @@ -926,8 +982,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(); } @@ -1044,9 +1104,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; @@ -1065,7 +1125,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 f7817a68a29c..c782f04aea9c 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1387,7 +1387,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; @@ -1410,7 +1410,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; /** @@ -1420,13 +1420,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; From 4f117f5626287c0d0efa8fa99b82f264a26bc8f7 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 12 May 2026 19:37:05 +0200 Subject: [PATCH 11/15] batman-adv: tp_meter: consolidate congestion control variables 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 --- net/batman-adv/tp_meter.c | 170 +++++++++++++++++++------------------- net/batman-adv/types.h | 80 +++++++++--------- 2 files changed, 128 insertions(+), 122 deletions(-) diff --git a/net/batman-adv/tp_meter.c b/net/batman-adv/tp_meter.c index e9ba1c45a285..978e32d94e6c 100644 --- a/net/batman-adv/tp_meter.c +++ b/net/batman-adv/tp_meter.c @@ -143,12 +143,12 @@ 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; } @@ -156,13 +156,13 @@ static void batadv_tp_update_cwnd(struct batadv_tp_sender *tp_vars, u32 mss) mss = min_t(u32, mss, (1U << 14) - 1); /* increment CWND at least of 1 (section 3.1 of RFC5681) */ - tp_vars->dec_cwnd += max_t(u32, 1U << 3, - ((mss * mss) << 3) / tp_vars->cwnd); - if (tp_vars->dec_cwnd < (mss << 3)) + tp_vars->cc.dec_cwnd += max_t(u32, 1U << 3, + ((mss * mss) << 3) / tp_vars->cc.cwnd); + 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; } /** @@ -172,7 +172,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; @@ -182,24 +182,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); } /** @@ -450,11 +450,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); @@ -521,7 +521,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))); } /** @@ -540,11 +540,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; @@ -553,22 +553,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); @@ -698,81 +698,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) { 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(tp_vars->recover, recv_ack)) + if (!batadv_seq_before(tp_vars->cc.recover, recv_ack)) 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; } @@ -814,7 +814,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); @@ -825,14 +825,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; @@ -873,16 +873,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; } @@ -964,7 +964,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, @@ -983,10 +983,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(); @@ -1104,30 +1104,30 @@ 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->dec_cwnd = 0; + tp_vars->cc.cwnd = BATADV_TP_PLEN * 3; + tp_vars->cc.dec_cwnd = 0; /* 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); @@ -1143,7 +1143,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 c782f04aea9c..5eb0371def83 100644 --- a/net/batman-adv/types.h +++ b/net/batman-adv/types.h @@ -1367,6 +1367,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 */ @@ -1383,15 +1422,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; @@ -1404,39 +1434,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 From 98b0fb191c878a64cbaebfe231d96d57576acf8c Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 26 May 2026 21:50:51 +0200 Subject: [PATCH 12/15] batman-adv: bla: annotate lasttime access with READ/WRITE_ONCE The lasttime field for claim, backbone_gw, and loopdetect tracks the jiffies value of the most recent activity and is used to detect timeouts. These accesses are not consistently protected by a lock, so READ_ONCE/WRITE_ONCE must be used to prevent data races caused by compiler optimizations. Cc: stable@kernel.org Fixes: 23721387c409 ("batman-adv: add basic bridge loop avoidance code") Signed-off-by: Sven Eckelmann --- net/batman-adv/bridge_loop_avoidance.c | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/net/batman-adv/bridge_loop_avoidance.c b/net/batman-adv/bridge_loop_avoidance.c index 87d6b11161e4..0461f11227d0 100644 --- a/net/batman-adv/bridge_loop_avoidance.c +++ b/net/batman-adv/bridge_loop_avoidance.c @@ -513,7 +513,7 @@ batadv_bla_get_backbone_gw(struct batadv_priv *bat_priv, const u8 *orig, return NULL; entry->vid = vid; - entry->lasttime = jiffies; + WRITE_ONCE(entry->lasttime, jiffies); entry->crc = BATADV_BLA_CRC_INIT; entry->bat_priv = bat_priv; spin_lock_init(&entry->crc_lock); @@ -581,7 +581,7 @@ batadv_bla_update_own_backbone_gw(struct batadv_priv *bat_priv, if (unlikely(!backbone_gw)) return; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_backbone_gw_put(backbone_gw); } @@ -715,7 +715,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, ether_addr_copy(claim->addr, mac); spin_lock_init(&claim->backbone_lock); claim->vid = vid; - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); kref_get(&backbone_gw->refcount); claim->backbone_gw = backbone_gw; kref_init(&claim->refcount); @@ -737,7 +737,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, return; } } else { - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); if (claim->backbone_gw == backbone_gw) /* no need to register a new backbone */ goto claim_free_ref; @@ -770,7 +770,7 @@ static void batadv_bla_add_claim(struct batadv_priv *bat_priv, spin_lock_bh(&backbone_gw->crc_lock); backbone_gw->crc ^= crc16(0, claim->addr, ETH_ALEN); spin_unlock_bh(&backbone_gw->crc_lock); - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); claim_free_ref: batadv_claim_put(claim); @@ -859,7 +859,7 @@ static bool batadv_handle_announce(struct batadv_priv *bat_priv, u8 *an_addr, return true; /* handle as ANNOUNCE frame */ - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); crc = ntohs(*((__force __be16 *)(&an_addr[4]))); batadv_dbg(BATADV_DBG_BLA, bat_priv, @@ -1254,7 +1254,7 @@ static void batadv_bla_purge_backbone_gw(struct batadv_priv *bat_priv, int now) head, hash_entry) { if (now) goto purge_now; - if (!batadv_has_timed_out(backbone_gw->lasttime, + if (!batadv_has_timed_out(READ_ONCE(backbone_gw->lasttime), BATADV_BLA_BACKBONE_TIMEOUT)) continue; @@ -1335,7 +1335,7 @@ static void batadv_bla_purge_claims(struct batadv_priv *bat_priv, primary_if->net_dev->dev_addr)) goto skip; - if (!batadv_has_timed_out(claim->lasttime, + if (!batadv_has_timed_out(READ_ONCE(claim->lasttime), BATADV_BLA_CLAIM_TIMEOUT)) goto skip; @@ -1495,7 +1495,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) eth_random_addr(bat_priv->bla.loopdetect_addr); bat_priv->bla.loopdetect_addr[0] = 0xba; bat_priv->bla.loopdetect_addr[1] = 0xbe; - bat_priv->bla.loopdetect_lasttime = jiffies; + WRITE_ONCE(bat_priv->bla.loopdetect_lasttime, jiffies); atomic_set(&bat_priv->bla.loopdetect_next, BATADV_BLA_LOOPDETECT_PERIODS); @@ -1516,7 +1516,7 @@ static void batadv_bla_periodic_work(struct work_struct *work) primary_if->net_dev->dev_addr)) continue; - backbone_gw->lasttime = jiffies; + WRITE_ONCE(backbone_gw->lasttime, jiffies); batadv_bla_send_announce(bat_priv, backbone_gw); if (send_loopdetect) @@ -1900,7 +1900,7 @@ batadv_bla_loopdetect_check(struct batadv_priv *bat_priv, struct sk_buff *skb, /* If the packet came too late, don't forward it on the mesh * but don't consider that as loop. It might be a coincidence. */ - if (batadv_has_timed_out(bat_priv->bla.loopdetect_lasttime, + if (batadv_has_timed_out(READ_ONCE(bat_priv->bla.loopdetect_lasttime), BATADV_BLA_LOOPDETECT_TIMEOUT)) return true; @@ -2015,7 +2015,7 @@ bool batadv_bla_rx(struct batadv_priv *bat_priv, struct sk_buff *skb, if (own_claim) { /* ... allow it in any case */ - claim->lasttime = jiffies; + WRITE_ONCE(claim->lasttime, jiffies); goto allow; } @@ -2117,7 +2117,7 @@ bool batadv_bla_tx(struct batadv_priv *bat_priv, struct sk_buff *skb, /* if yes, the client has roamed and we have * to unclaim it. */ - if (batadv_has_timed_out(claim->lasttime, 100)) { + if (batadv_has_timed_out(READ_ONCE(claim->lasttime), 100)) { /* only unclaim if the last claim entry is * older than 100 ms to make sure we really * have a roaming client here. @@ -2362,7 +2362,7 @@ batadv_bla_backbone_dump_entry(struct sk_buff *msg, u32 portid, backbone_crc = backbone_gw->crc; spin_unlock_bh(&backbone_gw->crc_lock); - msecs = jiffies_to_msecs(jiffies - backbone_gw->lasttime); + msecs = jiffies_to_msecs(jiffies - READ_ONCE(backbone_gw->lasttime)); if (is_own) if (nla_put_flag(msg, BATADV_ATTR_BLA_OWN)) { From 5e50d4b8ae3ea622122d3c6a38d7f6fe68dfddca Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Fri, 29 May 2026 23:36:43 +0200 Subject: [PATCH 13/15] batman-adv: prevent ELP transmission interval underflow batadv_v_elp_start_timer() enqeues a delayed work. The time when it starts is randomly chosen between (elp_interval - BATADV_JITTER) and (elp_interval + BATADV_JITTER). The configured elp_interval must therefore be larger or equal to BATADV_JITTER to avoid that it causes an underflow of the unsigned integer. If this would happen, then a "fast" ELP interval would turn into a "day long" delay. At the same time, it must not be larger than the maximum value the variable can store. Cc: stable@kernel.org Fixes: a10800829040 ("batman-adv: Add elp_interval hardif genl configuration") Signed-off-by: Sven Eckelmann --- net/batman-adv/netlink.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/net/batman-adv/netlink.c b/net/batman-adv/netlink.c index e5463003a3bd..1ba206e40273 100644 --- a/net/batman-adv/netlink.c +++ b/net/batman-adv/netlink.c @@ -919,9 +919,15 @@ static int batadv_netlink_set_hardif(struct sk_buff *skb, #ifdef CONFIG_BATMAN_ADV_BATMAN_V if (info->attrs[BATADV_ATTR_ELP_INTERVAL]) { - attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + u32 elp_interval; - WRITE_ONCE(hard_iface->bat_v.elp_interval, nla_get_u32(attr)); + attr = info->attrs[BATADV_ATTR_ELP_INTERVAL]; + elp_interval = nla_get_u32(attr); + + elp_interval = min_t(u32, elp_interval, INT_MAX); + elp_interval = max_t(u32, elp_interval, BATADV_JITTER); + + WRITE_ONCE(hard_iface->bat_v.elp_interval, elp_interval); } if (info->attrs[BATADV_ATTR_THROUGHPUT_OVERRIDE]) { From 5f80c363677c6aea0f2fc9a4fe0b5622abd97674 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Tue, 2 Jun 2026 17:20:57 +0200 Subject: [PATCH 14/15] batman-adv: tt: sync local and global tvlv preparation return values The batadv_tt_prepare_tvlv_local_data() and batadv_tt_prepare_tvlv_global_data() functions are supposed to work the same - just with different sources for the TT entries. But both handled the return values completely different. The global variant only made sure that the *tt_len parameter was set to 0 and didn't care about the actual return value of the function. The local function never made sure that the *tt_len value was set to some value when the operation failed. The callers were handling these differences and made sure that they didn't access the incorrectly initialized variable. Sync both function as good as possible to avoid problems with new code which might not be aware of these differences in the behavior. Signed-off-by: Sven Eckelmann --- net/batman-adv/translation-table.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 5e134d08a80f..44bbaa3bb37d 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -797,21 +797,22 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, struct batadv_tvlv_tt_change **tt_change, s32 *tt_len) { - u16 num_vlan = 0; - u16 tvlv_len = 0; struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_orig_node_vlan *vlan; u16 total_entries = 0; size_t change_offset; u8 *tt_change_ptr; + u16 num_vlan = 0; int vlan_entries; u16 sum_entries; + u16 tvlv_len; spin_lock_bh(&orig_node->vlan_list_lock); hlist_for_each_entry(vlan, &orig_node->vlan_list, list) { vlan_entries = atomic_read(&vlan->tt.num_entries); if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { + tvlv_len = 0; *tt_len = 0; goto out; } @@ -827,12 +828,14 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, *tt_len = batadv_tt_len(total_entries); if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { + tvlv_len = 0; *tt_len = 0; goto out; } *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); if (!*tt_data) { + tvlv_len = 0; *tt_len = 0; goto out; } @@ -867,6 +870,7 @@ batadv_tt_prepare_tvlv_global_data(struct batadv_orig_node *orig_node, out: spin_unlock_bh(&orig_node->vlan_list_lock); + return tvlv_len; } @@ -896,13 +900,13 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, { struct batadv_tvlv_tt_vlan_data *tt_vlan; struct batadv_meshif_vlan *vlan; - size_t change_offset; - u16 num_vlan = 0; u16 total_entries = 0; - u16 tvlv_len; + size_t change_offset; u8 *tt_change_ptr; + u16 num_vlan = 0; int vlan_entries; u16 sum_entries; + u16 tvlv_len; spin_lock_bh(&bat_priv->meshif_vlan_list_lock); hlist_for_each_entry(vlan, &bat_priv->meshif_vlan_list, list) { @@ -910,6 +914,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, if (check_add_overflow(vlan_entries, total_entries, &sum_entries)) { tvlv_len = 0; + *tt_len = 0; goto out; } @@ -925,12 +930,14 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, if (check_add_overflow(*tt_len, change_offset, &tvlv_len)) { tvlv_len = 0; + *tt_len = 0; goto out; } *tt_data = kmalloc(tvlv_len, GFP_ATOMIC); if (!*tt_data) { tvlv_len = 0; + *tt_len = 0; goto out; } @@ -964,6 +971,7 @@ batadv_tt_prepare_tvlv_local_data(struct batadv_priv *bat_priv, out: spin_unlock_bh(&bat_priv->meshif_vlan_list_lock); + return tvlv_len; } From 626fd14371614b7a8177f79b357fd323a7b91032 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 31 May 2026 15:03:25 +0200 Subject: [PATCH 15/15] batman-adv: tt: directly retrieve wifi flags of net_device batadv_tt_local_add() tries to retrieve the wifi flags of an interface to mark the TT entry as wifi client for the AP isolation feature. In the past, it was necessary to look up the batadv_hard_iface because the wifi_flags were stored inside this struct. But with the batadv_wifi_net_devices rhashtable, it is preferred to directly retrieve the wifi_flags instead of the indirect route via batadv_hard_iface - which at the end only provides the net_device (which we used to find the batadv_hard_iface). This will also be essential when the global batadv_hardif_list is removed and each lookup via batadv_hardif_get_by_netdev() will require the RTNL lock. Signed-off-by: Sven Eckelmann --- net/batman-adv/hard-interface.c | 35 ++++++++++++++++++++---------- net/batman-adv/hard-interface.h | 1 + net/batman-adv/translation-table.c | 14 +++++++----- 3 files changed, 32 insertions(+), 18 deletions(-) diff --git a/net/batman-adv/hard-interface.c b/net/batman-adv/hard-interface.c index bb3c31b5f259..43ebf86e7b36 100644 --- a/net/batman-adv/hard-interface.c +++ b/net/batman-adv/hard-interface.c @@ -365,6 +365,28 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) return wifi_flags; } +/** + * batadv_netdev_get_wifi_flags() - retrieve wifi flags for net_device + * @net_dev: the device to check + * + * Return: batadv_hard_iface_wifi_flags flags of the device + */ +u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev) +{ + struct batadv_wifi_net_device_state *device_state; + u32 wifi_flags = 0; + + rcu_read_lock(); + device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices, + &net_dev, + batadv_wifi_net_devices_params); + if (device_state) + wifi_flags = READ_ONCE(device_state->wifi_flags); + rcu_read_unlock(); + + return wifi_flags; +} + /** * batadv_hardif_get_wifi_flags() - retrieve wifi flags for hard_iface * @hard_iface: the device to check @@ -373,21 +395,10 @@ static u32 batadv_wifi_flags_evaluate(struct net_device *net_device) */ u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface) { - struct batadv_wifi_net_device_state *device_state; - u32 wifi_flags = 0; - if (!hard_iface) return 0; - rcu_read_lock(); - device_state = rhashtable_lookup_fast(&batadv_wifi_net_devices, - &hard_iface->net_dev, - batadv_wifi_net_devices_params); - if (device_state) - wifi_flags = READ_ONCE(device_state->wifi_flags); - rcu_read_unlock(); - - return wifi_flags; + return batadv_netdev_get_wifi_flags(hard_iface->net_dev); } /** diff --git a/net/batman-adv/hard-interface.h b/net/batman-adv/hard-interface.h index 089e65c8a481..822e7e378c4d 100644 --- a/net/batman-adv/hard-interface.h +++ b/net/batman-adv/hard-interface.h @@ -70,6 +70,7 @@ extern struct notifier_block batadv_hard_if_notifier; struct net_device *__batadv_get_real_netdev(struct net_device *net_device); struct net_device *batadv_get_real_netdev(struct net_device *net_device); +u32 batadv_netdev_get_wifi_flags(struct net_device *net_dev); u32 batadv_hardif_get_wifi_flags(struct batadv_hard_iface *hard_iface); bool batadv_is_wifi_hardif(struct batadv_hard_iface *hard_iface); struct batadv_hard_iface* diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 44bbaa3bb37d..c346e43d47b9 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -596,20 +596,23 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, struct net *net = dev_net(mesh_iface); struct batadv_meshif_vlan *vlan; struct net_device *in_dev = NULL; - struct batadv_hard_iface *in_hardif = NULL; struct hlist_head *head; struct batadv_tt_orig_list_entry *orig_entry; int hash_added, table_size, packet_size_max; bool ret = false; bool roamed_back = false; + bool iif_is_wifi = false; u8 remote_flags; u32 match_mark; if (ifindex != BATADV_NULL_IFINDEX) in_dev = dev_get_by_index(net, ifindex); - if (in_dev) - in_hardif = batadv_hardif_get_by_netdev(in_dev); + if (in_dev) { + u32 wifi_flags = batadv_netdev_get_wifi_flags(in_dev); + + iif_is_wifi = batadv_is_wifi(wifi_flags); + } tt_local = batadv_tt_local_hash_find(bat_priv, addr, vid); @@ -684,7 +687,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, */ tt_local->common.flags = BATADV_TT_CLIENT_NEW; tt_local->common.vid = vid; - if (batadv_is_wifi_hardif(in_hardif)) + if (iif_is_wifi) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; kref_init(&tt_local->common.refcount); tt_local->last_seen = jiffies; @@ -743,7 +746,7 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, */ remote_flags = tt_local->common.flags & BATADV_TT_REMOTE_MASK; - if (batadv_is_wifi_hardif(in_hardif)) + if (iif_is_wifi) tt_local->common.flags |= BATADV_TT_CLIENT_WIFI; else tt_local->common.flags &= ~BATADV_TT_CLIENT_WIFI; @@ -767,7 +770,6 @@ bool batadv_tt_local_add(struct net_device *mesh_iface, const u8 *addr, ret = true; out: - batadv_hardif_put(in_hardif); dev_put(in_dev); batadv_tt_local_entry_put(tt_local); batadv_tt_global_entry_put(tt_global);