[PATCH net-next 08/15] batman-adv: tt: remove only the entry which was looked up from the hash
From: Simon Wunderlich <sw@simonwunderlich.de>
Date: 2026-08-31 13:52:51
Also in:
batman
Subsystem:
batman advanced, the rest · Maintainers:
Marek Lindner, Simon Wunderlich, Antonio Quartulli, Sven Eckelmann, Linus Torvalds
From: Sven Eckelmann <sven@narfation.org> batadv_hash_remove() searches a bucket with a compare callback and unlinks the first matching entry. batadv_compare_tt() matches any entry for the same MAC address and VLAN, not the object which was passed in, and the callers in batadv_tt_local_remove() and batadv_tt_global_free() are not serialized against the rest of the translation table in any way. So when the looked up entry was already unlinked by another context and a new entry for the same client was added in the meantime, these two functions unlink that new entry instead. Add batadv_compare_tt_entry(), which matches the very object which is searched for, and use it for both removals. Nothing is unlinked when the entry is gone already, batadv_hash_remove() then simply returns NULL and only the reference of the calling context is dropped. As a side effect the returned hlist_node can no longer belong to a different object, so both functions can operate on the entry they were given. Signed-off-by: Sven Eckelmann <sven@narfation.org> Signed-off-by: Simon Wunderlich <sw@simonwunderlich.de> --- net/batman-adv/translation-table.c | 38 ++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 12 deletions(-)
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c
index 88aeefa97db6e..66456a2d45e26 100644
--- a/net/batman-adv/translation-table.c
+++ b/net/batman-adv/translation-table.c@@ -95,6 +95,26 @@ static bool batadv_compare_tt(const struct hlist_node *node, const void *data2) return (tt1->vid == tt2->vid) && batadv_compare_eth(data1, data2); } +/** + * batadv_compare_tt_entry() - check if a hash node is a specific TT entry + * @node: the list element pointer of the TT entry stored in the bucket + * @data2: pointer to the tt_common_entry which is looked for + * + * Unlike batadv_compare_tt(), this only matches the very object which is + * passed as @data2 and not just any entry for the same TT client. It is meant + * for batadv_hash_remove() callers which must not unlink an entry they did not + * look up themselves. + * + * Return: true if @node belongs to @data2, false otherwise + */ +static bool batadv_compare_tt_entry(const struct hlist_node *node, + const void *data2) +{ + const struct batadv_tt_common_entry *tt = data2; + + return node == &tt->hash_entry; +} + /** * batadv_choose_tt() - return the index of the tt entry in the hash table * @data: pointer to the tt_common_entry object to map
@@ -575,7 +595,6 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, struct batadv_tt_global_entry *tt_global, const char *message) { - struct batadv_tt_global_entry *tt_removed_entry; struct hlist_node *tt_removed_node; batadv_dbg(BATADV_DBG_TT, bat_priv,
@@ -583,18 +602,16 @@ static void batadv_tt_global_free(struct batadv_priv *bat_priv, tt_global->common.addr, batadv_print_vid(tt_global->common.vid), message); + /* remove exactly this object when still present in hash */ tt_removed_node = batadv_hash_remove(bat_priv->tt.global_hash, - batadv_compare_tt, + batadv_compare_tt_entry, batadv_choose_tt, &tt_global->common); if (!tt_removed_node) return; /* drop reference of remove hash entry */ - tt_removed_entry = hlist_entry(tt_removed_node, - struct batadv_tt_global_entry, - common.hash_entry); - batadv_tt_global_entry_put(tt_removed_entry); + batadv_tt_global_entry_put(tt_global); } /**
@@ -1292,7 +1309,6 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, unsigned short vid, const char *message, bool roaming) { - struct batadv_tt_local_entry *tt_removed_entry; struct batadv_tt_local_entry *tt_local_entry; struct hlist_node *tt_removed_node; u16 curr_flags = BATADV_NO_FLAGS;
@@ -1325,18 +1341,16 @@ u16 batadv_tt_local_remove(struct batadv_priv *bat_priv, const u8 *addr, */ batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL); + /* remove exactly this object when still present in hash */ tt_removed_node = batadv_hash_remove(bat_priv->tt.local_hash, - batadv_compare_tt, + batadv_compare_tt_entry, batadv_choose_tt, &tt_local_entry->common); if (!tt_removed_node) goto out; /* drop reference of remove hash entry */ - tt_removed_entry = hlist_entry(tt_removed_node, - struct batadv_tt_local_entry, - common.hash_entry); - batadv_tt_local_entry_put(tt_removed_entry); + batadv_tt_local_entry_put(tt_local_entry); out: batadv_tt_local_entry_put(tt_local_entry);
--
2.47.3