diff options
author | Linus Lüssing <linus.luessing@web.de> | 2013-04-15 09:43:29 -0400 |
---|---|---|
committer | Antonio Quartulli <ordex@autistici.org> | 2013-05-17 03:54:28 -0400 |
commit | 72822225bd41320a98f5d7cde38317766e18983f (patch) | |
tree | c1947655ac129d0a1aa3212755097baa9600bb3e /net | |
parent | b0ce3508b25ea6fa10ae3ca254de1d695b521702 (diff) |
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>
Diffstat (limited to 'net')
-rw-r--r-- | net/batman-adv/originator.c | 16 | ||||
-rw-r--r-- | net/batman-adv/originator.h | 1 | ||||
-rw-r--r-- | net/batman-adv/translation-table.c | 7 |
3 files changed, 23 insertions, 1 deletions
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c index 2f3452546636..fad1a2093e15 100644 --- a/net/batman-adv/originator.c +++ b/net/batman-adv/originator.c | |||
@@ -156,12 +156,28 @@ static void batadv_orig_node_free_rcu(struct rcu_head *rcu) | |||
156 | kfree(orig_node); | 156 | kfree(orig_node); |
157 | } | 157 | } |
158 | 158 | ||
159 | /** | ||
160 | * batadv_orig_node_free_ref - decrement the orig node refcounter and possibly | ||
161 | * schedule an rcu callback for freeing it | ||
162 | * @orig_node: the orig node to free | ||
163 | */ | ||
159 | void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node) | 164 | void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node) |
160 | { | 165 | { |
161 | if (atomic_dec_and_test(&orig_node->refcount)) | 166 | if (atomic_dec_and_test(&orig_node->refcount)) |
162 | call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); | 167 | call_rcu(&orig_node->rcu, batadv_orig_node_free_rcu); |
163 | } | 168 | } |
164 | 169 | ||
170 | /** | ||
171 | * batadv_orig_node_free_ref_now - decrement the orig node refcounter and | ||
172 | * possibly free it (without rcu callback) | ||
173 | * @orig_node: the orig node to free | ||
174 | */ | ||
175 | void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node) | ||
176 | { | ||
177 | if (atomic_dec_and_test(&orig_node->refcount)) | ||
178 | batadv_orig_node_free_rcu(&orig_node->rcu); | ||
179 | } | ||
180 | |||
165 | void batadv_originator_free(struct batadv_priv *bat_priv) | 181 | void batadv_originator_free(struct batadv_priv *bat_priv) |
166 | { | 182 | { |
167 | struct batadv_hashtable *hash = bat_priv->orig_hash; | 183 | struct batadv_hashtable *hash = bat_priv->orig_hash; |
diff --git a/net/batman-adv/originator.h b/net/batman-adv/originator.h index 7df48fa7669d..734e5a3d8a5b 100644 --- a/net/batman-adv/originator.h +++ b/net/batman-adv/originator.h | |||
@@ -26,6 +26,7 @@ int batadv_originator_init(struct batadv_priv *bat_priv); | |||
26 | void batadv_originator_free(struct batadv_priv *bat_priv); | 26 | void batadv_originator_free(struct batadv_priv *bat_priv); |
27 | void batadv_purge_orig_ref(struct batadv_priv *bat_priv); | 27 | void batadv_purge_orig_ref(struct batadv_priv *bat_priv); |
28 | void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); | 28 | void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); |
29 | void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node); | ||
29 | struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, | 30 | struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, |
30 | const uint8_t *addr); | 31 | const uint8_t *addr); |
31 | struct batadv_neigh_node * | 32 | struct batadv_neigh_node * |
diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index 5e89deeb9542..9e8748575845 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c | |||
@@ -144,7 +144,12 @@ static void batadv_tt_orig_list_entry_free_rcu(struct rcu_head *rcu) | |||
144 | struct batadv_tt_orig_list_entry *orig_entry; | 144 | struct batadv_tt_orig_list_entry *orig_entry; |
145 | 145 | ||
146 | orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu); | 146 | orig_entry = container_of(rcu, struct batadv_tt_orig_list_entry, rcu); |
147 | batadv_orig_node_free_ref(orig_entry->orig_node); | 147 | |
148 | /* We are in an rcu callback here, therefore we cannot use | ||
149 | * batadv_orig_node_free_ref() and its call_rcu(): | ||
150 | * An rcu_barrier() wouldn't wait for that to finish | ||
151 | */ | ||
152 | batadv_orig_node_free_ref_now(orig_entry->orig_node); | ||
148 | kfree(orig_entry); | 153 | kfree(orig_entry); |
149 | } | 154 | } |
150 | 155 | ||