diff options
author | Johannes Berg <johannes@sipsolutions.net> | 2008-02-25 10:27:46 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2008-03-06 15:30:46 -0500 |
commit | d0709a65181beb787ef3f58cfe45536a2bb254c8 (patch) | |
tree | 29e5f36583b0e0a3f11b291347e57672eab41dad /net/mac80211/mesh_pathtbl.c | |
parent | 5cf121c3cdb955583bf0c5d28c992b7968a4aa1a (diff) |
mac80211: RCU-ify STA info structure access
This makes access to the STA hash table/list use RCU to protect
against freeing of items. However, it's not a true RCU, the
copy step is missing: whenever somebody changes a STA item it
is simply updated. This is an existing race condition that is
now somewhat understandable.
This patch also fixes the race key freeing vs. STA destruction
by making sure that sta_info_destroy() is always called under
RTNL and frees the key.
Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/mesh_pathtbl.c')
-rw-r--r-- | net/mac80211/mesh_pathtbl.c | 30 |
1 files changed, 14 insertions, 16 deletions
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c index 3cbdbb23d75a..a17f2b299045 100644 --- a/net/mac80211/mesh_pathtbl.c +++ b/net/mac80211/mesh_pathtbl.c | |||
@@ -55,10 +55,7 @@ static DEFINE_RWLOCK(pathtbl_resize_lock); | |||
55 | */ | 55 | */ |
56 | void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) | 56 | void mesh_path_assign_nexthop(struct mesh_path *mpath, struct sta_info *sta) |
57 | { | 57 | { |
58 | __sta_info_get(sta); | 58 | rcu_assign_pointer(mpath->next_hop, sta); |
59 | if (mpath->next_hop) | ||
60 | sta_info_put(mpath->next_hop); | ||
61 | mpath->next_hop = sta; | ||
62 | } | 59 | } |
63 | 60 | ||
64 | 61 | ||
@@ -236,7 +233,7 @@ void mesh_plink_broken(struct sta_info *sta) | |||
236 | struct mesh_path *mpath; | 233 | struct mesh_path *mpath; |
237 | struct mpath_node *node; | 234 | struct mpath_node *node; |
238 | struct hlist_node *p; | 235 | struct hlist_node *p; |
239 | struct net_device *dev = sta->dev; | 236 | struct net_device *dev = sta->sdata->dev; |
240 | int i; | 237 | int i; |
241 | 238 | ||
242 | rcu_read_lock(); | 239 | rcu_read_lock(); |
@@ -266,9 +263,9 @@ EXPORT_SYMBOL(mesh_plink_broken); | |||
266 | * | 263 | * |
267 | * RCU notes: this function is called when a mesh plink transitions from ESTAB | 264 | * RCU notes: this function is called when a mesh plink transitions from ESTAB |
268 | * to any other state, since ESTAB state is the only one that allows path | 265 | * to any other state, since ESTAB state is the only one that allows path |
269 | * creation. This will happen before the sta can be freed (since we hold | 266 | * creation. This will happen before the sta can be freed (because |
270 | * a reference to it) so any reader in a rcu read block will be protected | 267 | * sta_info_destroy() calls this) so any reader in a rcu read block will be |
271 | * against the plink dissapearing. | 268 | * protected against the plink disappearing. |
272 | */ | 269 | */ |
273 | void mesh_path_flush_by_nexthop(struct sta_info *sta) | 270 | void mesh_path_flush_by_nexthop(struct sta_info *sta) |
274 | { | 271 | { |
@@ -280,7 +277,7 @@ void mesh_path_flush_by_nexthop(struct sta_info *sta) | |||
280 | for_each_mesh_entry(mesh_paths, p, node, i) { | 277 | for_each_mesh_entry(mesh_paths, p, node, i) { |
281 | mpath = node->mpath; | 278 | mpath = node->mpath; |
282 | if (mpath->next_hop == sta) | 279 | if (mpath->next_hop == sta) |
283 | mesh_path_del(mpath->dst, mpath->dev); | 280 | mesh_path_del(mpath->dst, mpath->dev, true); |
284 | } | 281 | } |
285 | } | 282 | } |
286 | 283 | ||
@@ -294,7 +291,7 @@ void mesh_path_flush(struct net_device *dev) | |||
294 | for_each_mesh_entry(mesh_paths, p, node, i) { | 291 | for_each_mesh_entry(mesh_paths, p, node, i) { |
295 | mpath = node->mpath; | 292 | mpath = node->mpath; |
296 | if (mpath->dev == dev) | 293 | if (mpath->dev == dev) |
297 | mesh_path_del(mpath->dst, mpath->dev); | 294 | mesh_path_del(mpath->dst, mpath->dev, false); |
298 | } | 295 | } |
299 | } | 296 | } |
300 | 297 | ||
@@ -303,8 +300,8 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) | |||
303 | struct mpath_node *node = container_of(rp, struct mpath_node, rcu); | 300 | struct mpath_node *node = container_of(rp, struct mpath_node, rcu); |
304 | struct ieee80211_sub_if_data *sdata = | 301 | struct ieee80211_sub_if_data *sdata = |
305 | IEEE80211_DEV_TO_SUB_IF(node->mpath->dev); | 302 | IEEE80211_DEV_TO_SUB_IF(node->mpath->dev); |
306 | if (node->mpath->next_hop) | 303 | |
307 | sta_info_put(node->mpath->next_hop); | 304 | rcu_assign_pointer(node->mpath->next_hop, NULL); |
308 | atomic_dec(&sdata->u.sta.mpaths); | 305 | atomic_dec(&sdata->u.sta.mpaths); |
309 | kfree(node->mpath); | 306 | kfree(node->mpath); |
310 | kfree(node); | 307 | kfree(node); |
@@ -319,9 +316,10 @@ static void mesh_path_node_reclaim(struct rcu_head *rp) | |||
319 | * Returns: 0 if succesful | 316 | * Returns: 0 if succesful |
320 | * | 317 | * |
321 | * State: if the path is being resolved, the deletion will be postponed until | 318 | * State: if the path is being resolved, the deletion will be postponed until |
322 | * the path resolution completes or times out. | 319 | * the path resolution completes or times out, unless the force parameter |
320 | * is given. | ||
323 | */ | 321 | */ |
324 | int mesh_path_del(u8 *addr, struct net_device *dev) | 322 | int mesh_path_del(u8 *addr, struct net_device *dev, bool force) |
325 | { | 323 | { |
326 | struct mesh_path *mpath; | 324 | struct mesh_path *mpath; |
327 | struct mpath_node *node; | 325 | struct mpath_node *node; |
@@ -340,7 +338,7 @@ int mesh_path_del(u8 *addr, struct net_device *dev) | |||
340 | if (mpath->dev == dev && | 338 | if (mpath->dev == dev && |
341 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { | 339 | memcmp(addr, mpath->dst, ETH_ALEN) == 0) { |
342 | spin_lock_bh(&mpath->state_lock); | 340 | spin_lock_bh(&mpath->state_lock); |
343 | if (mpath->flags & MESH_PATH_RESOLVING) { | 341 | if (!force && mpath->flags & MESH_PATH_RESOLVING) { |
344 | mpath->flags |= MESH_PATH_DELETE; | 342 | mpath->flags |= MESH_PATH_DELETE; |
345 | } else { | 343 | } else { |
346 | mpath->flags |= MESH_PATH_RESOLVING; | 344 | mpath->flags |= MESH_PATH_RESOLVING; |
@@ -510,7 +508,7 @@ void mesh_path_expire(struct net_device *dev) | |||
510 | time_after(jiffies, | 508 | time_after(jiffies, |
511 | mpath->exp_time + MESH_PATH_EXPIRE)) { | 509 | mpath->exp_time + MESH_PATH_EXPIRE)) { |
512 | spin_unlock_bh(&mpath->state_lock); | 510 | spin_unlock_bh(&mpath->state_lock); |
513 | mesh_path_del(mpath->dst, mpath->dev); | 511 | mesh_path_del(mpath->dst, mpath->dev, false); |
514 | } else | 512 | } else |
515 | spin_unlock_bh(&mpath->state_lock); | 513 | spin_unlock_bh(&mpath->state_lock); |
516 | } | 514 | } |