[PATCH batadv 2/4] batman-adv: tt: don't directly clear NEW flag when removing local entry
Sven Eckelmann <[email protected]> Thu, 30 Jul 2026 19:32:57 +0200
| Newsgroups | org.open-mesh.lists.batman |
|---|---|
| Message-ID | <[email protected]> |
The conversion of the translation table flags to atomic_t turned the pure
BATADV_TT_CLIENT_NEW test in batadv_tt_local_remove() into a
test-and-clear. This is not needed for the atomic flag handling and instead
breaks the local translation table size accounting.
BATADV_TT_CLIENT_NEW is the only marker which tells the commit worker that
an entry still has to be counted. The single increment site is the NEW ->
!NEW transition in batadv_tt_local_transition_new(), while
batadv_tt_local_purge_pending_clients() unconditionally decrements every
still hashed entry which carries BATADV_TT_CLIENT_PENDING. Clearing the
flag outside of batadv_tt_local_transition_new() therefore drops the
increment while keeping the decrement:
CPU A batadv_tt_local_remove()
atomic_fetch_andnot(NEW) -> old_flags has NEW
batadv_tt_local_event(..., BATADV_TT_CLIENT_DEL)
CPU B batadv_tt_local_remove() (entry found before A unlinked it)
atomic_fetch_andnot(NEW) -> old_flags has no NEW
batadv_tt_local_set_pending() -> atomic_or(PENDING)
CPU C commit worker
batadv_tt_local_transition_new() -> no NEW, no size_inc
batadv_tt_local_purge_pending_clients() -> size_dec + hlist_del_rcu
CPU A batadv_tt_local_remove()
batadv_hash_remove() -> NULL
Restore the read-only test at the start of the function and add an explicit
batadv_tt_local_size_dec() for the !NEW case. Losing the race against
another remover is already handled by batadv_hash_remove() returning NULL.
Reported-by: Sashiko <[email protected]>
Fixes: 8b4cee3c9355 ("batman-adv: tt: use atomic flag modifications")
Signed-off-by: Sven Eckelmann <[email protected]>
---
net/batman-adv/translation-table.c | 21 ++++++++++++++++++---
1 file changed, 18 insertions(+), 3 deletions(-)
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 99887237..248b2966 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c
@@ -1416,13 +1416,19 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
atomic_or(BATADV_TT_CLIENT_ROAM, &tt_local_entry->common.flags);
}
- old_flags = atomic_fetch_andnot(BATADV_TT_CLIENT_NEW,
- &tt_local_entry->common.flags);
- if (!(old_flags & BATADV_TT_CLIENT_NEW)) {
+ /* This must stay a read-only test while the entry is still hashed:
+ * clearing the flag here would make a concurrent remover of the same
+ * entry pick the branch below and mark a not yet committed entry as
+ * pending. A parallel batadv_tt_local_purge_pending_clients()
+ * then calls batadv_tt_local_size_dec() for this entry even when
+ * it was never counted by via batadv_tt_local_set_flags()
+ */
+ if (!(atomic_read(&tt_local_entry->common.flags) & BATADV_TT_CLIENT_NEW)) {
batadv_tt_local_set_pending(bat_priv, tt_local_entry, flags,
message);
goto out;
}
+
/* if this client has been added right now, it is possible to
* immediately purge it
*/
@@ -1436,6 +1442,15 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr,
if (!tt_removed_node)
goto out;
+ /* batadv_tt_local_transition_new() may have committed the entry and
+ * thus counted it in the local table size since the BATADV_TT_CLIENT_NEW
+ * check above.
+ */
+ old_flags = atomic_fetch_andnot(BATADV_TT_CLIENT_NEW,
+ &tt_local_entry->common.flags);
+ if (!(old_flags & BATADV_TT_CLIENT_NEW))
+ batadv_tt_local_size_dec(bat_priv, tt_local_entry->common.vid);
+
/* drop reference of remove hash entry */
batadv_tt_local_entry_put(tt_local_entry);
--
2.47.3