mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
batman-adv: tt: remove only the entry which was looked up from the hash
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>
This commit is contained in:
parent
d9304a90ec
commit
bbfb0d2cf0
|
|
@ -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);
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user