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: Linus Lüssing <linus.luessing@web.de> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de> Acked-by: Antonio Quartulli <ordex@autistici.org>
This commit is contained in:
Родитель
b0ce3508b2
Коммит
72822225bd
|
@ -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);
|
||||||
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)
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче