Commit 72822225 authored by Linus Lüssing's avatar Linus Lüssing Committed by Antonio Quartulli

batman-adv: Fix rcu_barrier() miss due to double call_rcu() in TT code

rcu_barrier() only waits for the currently scheduled rcu functions
to finish - it won't wait for any function scheduled via another
call_rcu() within an rcu scheduled function.

Unfortunately our batadv_tt_orig_list_entry_free_ref() does just that,
via a batadv_orig_node_free_ref() call, leading to our rcu_barrier()
call potentially missing such a batadv_orig_node_free_ref().

This patch fixes this issue by calling the batadv_orig_node_free_rcu()
directly from the rcu callback, removing the unnecessary, additional
call_rcu() layer here.
Signed-off-by: default avatarLinus Lüssing <linus.luessing@web.de>
Signed-off-by: default avatarMarek Lindner <lindner_marek@yahoo.de>
Acked-by: default avatarAntonio Quartulli <ordex@autistici.org>
parent b0ce3508
...@@ -156,12 +156,28 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) ...@@ -156,12 +156,28 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu)
kfree(orig_node); kfree(orig_node);
} }
/**
* batadv_orig_node_free_ref - decrement the orig node refcounter and possibly
* schedule an rcu callback for freeing it
* @orig_node: the orig node to free
*/
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node) void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node)
{ {
if (atomic_dec_and_test(&orig_node->refcount)) if (atomic_dec_and_test(&orig_node->refcount))
call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu);
} }
/**
* batadv_orig_node_free_ref_now - decrement the orig node refcounter and
* possibly free it (without rcu callback)
* @orig_node: the orig node to free
*/
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node)
{
if (atomic_dec_and_test(&orig_node->refcount))
batadv_orig_node_free_rcu(&orig_node->rcu);
}
void batadv_originator_free(struct batadv_priv *bat_priv) void batadv_originator_free(struct batadv_priv *bat_priv)
{ {
struct batadv_hashtable *hash = bat_priv->orig_hash; struct batadv_hashtable *hash = bat_priv->orig_hash;
......
...@@ -26,6 +26,7 @@ int batadv_originator_init(struct batadv_priv *bat_priv); ...@@ -26,6 +26,7 @@ int batadv_originator_init(struct batadv_priv *bat_priv);
void batadv_originator_free(struct batadv_priv *bat_priv); void batadv_originator_free(struct batadv_priv *bat_priv);
void batadv_purge_orig_ref(struct batadv_priv *bat_priv); void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
const uint8_t *addr); const uint8_t *addr);
struct batadv_neigh_node * struct batadv_neigh_node *
......
...@@ -144,7 +144,12 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu) ...@@ -144,7 +144,12 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu)
struct batadv_tt_orig_list_entry *orig_entry; struct batadv_tt_orig_list_entry *orig_entry;
orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu); orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu);
batadv_orig_node_free_ref(orig_entry->orig_node);
/* We are in an rcu callback here, therefore we cannot use
* batadv_orig_node_free_ref() and its call_rcu():
* An rcu_barrier() wouldn't wait for that to finish
*/
batadv_orig_node_free_ref_now(orig_entry->orig_node);
kfree(orig_entry); kfree(orig_entry);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment