aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorLinus Lüssing <linus.luessing@web.de>2013-04-15 09:43:29 -0400
committerAntonio Quartulli <ordex@autistici.org>2013-05-17 03:54:28 -0400
commit72822225bd41320a98f5d7cde38317766e18983f (patch)
treec1947655ac129d0a1aa3212755097baa9600bb3e /net
parentb0ce3508b25ea6fa10ae3ca254de1d695b521702 (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.c16
-rw-r--r--net/batman-adv/originator.h1
-rw-r--r--net/batman-adv/translation-table.c7
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 */
159void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node) 164void 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 */
175void 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
165void batadv_originator_free(struct batadv_priv *bat_priv) 181void 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);
26void batadv_originator_free(struct batadv_priv *bat_priv); 26void batadv_originator_free(struct batadv_priv *bat_priv);
27void batadv_purge_orig_ref(struct batadv_priv *bat_priv); 27void batadv_purge_orig_ref(struct batadv_priv *bat_priv);
28void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node); 28void batadv_orig_node_free_ref(struct batadv_orig_node *orig_node);
29void batadv_orig_node_free_ref_now(struct batadv_orig_node *orig_node);
29struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv, 30struct batadv_orig_node *batadv_get_orig_node(struct batadv_priv *bat_priv,
30 const uint8_t *addr); 31 const uint8_t *addr);
31struct batadv_neigh_node * 32struct 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