aboutsummaryrefslogtreecommitdiffstats
path: root/net/batman-adv/originator.c
diff options
context:
space:
mode:
authorMarek Lindner <lindner_marek@yahoo.de>2011-02-18 07:28:10 -0500
committerMarek Lindner <lindner_marek@yahoo.de>2011-03-05 06:52:01 -0500
commit7b36e8eef989fc59535b4f1d3fc0f83afaf419d4 (patch)
treef0900101542966e0655ca5f115b5b0bc409b1e74 /net/batman-adv/originator.c
parent7aadf889e897155c45cda230d2a6701ad1fbff61 (diff)
batman-adv: Correct rcu refcounting for orig_node
It might be possible that 2 threads access the same data in the same rcu grace period. The first thread calls call_rcu() to decrement the refcount and free the data while the second thread increases the refcount to use the data. To avoid this race condition all refcount operations have to be atomic. Reported-by: Sven Eckelmann <sven@narfation.org> Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
Diffstat (limited to 'net/batman-adv/originator.c')
-rw-r--r--net/batman-adv/originator.c21
1 files changed, 13 insertions, 8 deletions
diff --git a/net/batman-adv/originator.c b/net/batman-adv/originator.c
index bdcb399329dd..a70debebfc5b 100644
--- a/net/batman-adv/originator.c
+++ b/net/batman-adv/originator.c
@@ -102,13 +102,13 @@ struct neigh_node *create_neighbor(struct orig_node *orig_node,
102 return neigh_node; 102 return neigh_node;
103} 103}
104 104
105void orig_node_free_ref(struct kref *refcount) 105static void orig_node_free_rcu(struct rcu_head *rcu)
106{ 106{
107 struct hlist_node *node, *node_tmp; 107 struct hlist_node *node, *node_tmp;
108 struct neigh_node *neigh_node, *tmp_neigh_node; 108 struct neigh_node *neigh_node, *tmp_neigh_node;
109 struct orig_node *orig_node; 109 struct orig_node *orig_node;
110 110
111 orig_node = container_of(refcount, struct orig_node, refcount); 111 orig_node = container_of(rcu, struct orig_node, rcu);
112 112
113 spin_lock_bh(&orig_node->neigh_list_lock); 113 spin_lock_bh(&orig_node->neigh_list_lock);
114 114
@@ -137,6 +137,12 @@ void orig_node_free_ref(struct kref *refcount)
137 kfree(orig_node); 137 kfree(orig_node);
138} 138}
139 139
140void orig_node_free_ref(struct orig_node *orig_node)
141{
142 if (atomic_dec_and_test(&orig_node->refcount))
143 call_rcu(&orig_node->rcu, orig_node_free_rcu);
144}
145
140void originator_free(struct bat_priv *bat_priv) 146void originator_free(struct bat_priv *bat_priv)
141{ 147{
142 struct hashtable_t *hash = bat_priv->orig_hash; 148 struct hashtable_t *hash = bat_priv->orig_hash;
@@ -163,7 +169,7 @@ void originator_free(struct bat_priv *bat_priv)
163 head, hash_entry) { 169 head, hash_entry) {
164 170
165 hlist_del_rcu(node); 171 hlist_del_rcu(node);
166 kref_put(&orig_node->refcount, orig_node_free_ref); 172 orig_node_free_ref(orig_node);
167 } 173 }
168 spin_unlock_bh(list_lock); 174 spin_unlock_bh(list_lock);
169 } 175 }
@@ -196,7 +202,9 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
196 spin_lock_init(&orig_node->ogm_cnt_lock); 202 spin_lock_init(&orig_node->ogm_cnt_lock);
197 spin_lock_init(&orig_node->bcast_seqno_lock); 203 spin_lock_init(&orig_node->bcast_seqno_lock);
198 spin_lock_init(&orig_node->neigh_list_lock); 204 spin_lock_init(&orig_node->neigh_list_lock);
199 kref_init(&orig_node->refcount); 205
206 /* extra reference for return */
207 atomic_set(&orig_node->refcount, 2);
200 208
201 orig_node->bat_priv = bat_priv; 209 orig_node->bat_priv = bat_priv;
202 memcpy(orig_node->orig, addr, ETH_ALEN); 210 memcpy(orig_node->orig, addr, ETH_ALEN);
@@ -229,8 +237,6 @@ struct orig_node *get_orig_node(struct bat_priv *bat_priv, uint8_t *addr)
229 if (hash_added < 0) 237 if (hash_added < 0)
230 goto free_bcast_own_sum; 238 goto free_bcast_own_sum;
231 239
232 /* extra reference for return */
233 kref_get(&orig_node->refcount);
234 return orig_node; 240 return orig_node;
235free_bcast_own_sum: 241free_bcast_own_sum:
236 kfree(orig_node->bcast_own_sum); 242 kfree(orig_node->bcast_own_sum);
@@ -348,8 +354,7 @@ static void _purge_orig(struct bat_priv *bat_priv)
348 if (orig_node->gw_flags) 354 if (orig_node->gw_flags)
349 gw_node_delete(bat_priv, orig_node); 355 gw_node_delete(bat_priv, orig_node);
350 hlist_del_rcu(node); 356 hlist_del_rcu(node);
351 kref_put(&orig_node->refcount, 357 orig_node_free_ref(orig_node);
352 orig_node_free_ref);
353 continue; 358 continue;
354 } 359 }
355 360