diff options
author | Thomas Pedersen <thomas@cozybit.com> | 2013-01-23 15:18:12 -0500 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-01-24 10:03:34 -0500 |
commit | 296fcba3ba1b8888aa8f5211de1e25a78b47aeee (patch) | |
tree | a8bb05498d62558513074cd6ded9975f2f2a5043 /net/mac80211/mesh_plink.c | |
parent | d437c86baacf265a640dfc462c75941d02c0e153 (diff) |
mac80211: clean up mesh sta allocation warning
This refactoring fixes a "scheduling while atomic" warning
when allocating a mesh station entry while holding the RCU
read lock. Fix this by creating a new function
mesh_sta_info_get(), which correctly handles the locking
and returns under RCU.
Also move some unnecessarily #ifdefed mesh station init
code from sta_info_alloc() to __mesh_sta_info_alloc().
Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
[change code flow to make sparse happy]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/mesh_plink.c')
-rw-r--r-- | net/mac80211/mesh_plink.c | 158 |
1 files changed, 95 insertions, 63 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9e0416696a83..ef92d2705851 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -55,30 +55,6 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) | |||
55 | sta->plink_retries = 0; | 55 | sta->plink_retries = 0; |
56 | } | 56 | } |
57 | 57 | ||
58 | /* | ||
59 | * Allocate mesh sta entry and insert into station table | ||
60 | */ | ||
61 | static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, | ||
62 | u8 *hw_addr) | ||
63 | { | ||
64 | struct sta_info *sta; | ||
65 | |||
66 | if (sdata->local->num_sta >= MESH_MAX_PLINKS) | ||
67 | return NULL; | ||
68 | |||
69 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); | ||
70 | if (!sta) | ||
71 | return NULL; | ||
72 | |||
73 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); | ||
74 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); | ||
75 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); | ||
76 | |||
77 | set_sta_flag(sta, WLAN_STA_WME); | ||
78 | |||
79 | return sta; | ||
80 | } | ||
81 | |||
82 | /** | 58 | /** |
83 | * mesh_set_ht_prot_mode - set correct HT protection mode | 59 | * mesh_set_ht_prot_mode - set correct HT protection mode |
84 | * | 60 | * |
@@ -309,52 +285,24 @@ free: | |||
309 | return err; | 285 | return err; |
310 | } | 286 | } |
311 | 287 | ||
312 | /** | 288 | static void mesh_sta_info_init(struct ieee80211_sub_if_data *sdata, |
313 | * mesh_peer_init - initialize new mesh peer and return resulting sta_info | 289 | struct sta_info *sta, |
314 | * | 290 | struct ieee802_11_elems *elems, bool insert) |
315 | * @sdata: local meshif | ||
316 | * @addr: peer's address | ||
317 | * @elems: IEs from beacon or mesh peering frame | ||
318 | * | ||
319 | * call under RCU | ||
320 | */ | ||
321 | static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, | ||
322 | u8 *addr, | ||
323 | struct ieee802_11_elems *elems) | ||
324 | { | 291 | { |
325 | struct ieee80211_local *local = sdata->local; | 292 | struct ieee80211_local *local = sdata->local; |
326 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); | 293 | enum ieee80211_band band = ieee80211_get_sdata_band(sdata); |
327 | struct ieee80211_supported_band *sband; | 294 | struct ieee80211_supported_band *sband; |
328 | u32 rates, basic_rates = 0; | 295 | u32 rates, basic_rates = 0; |
329 | struct sta_info *sta; | ||
330 | bool insert = false; | ||
331 | 296 | ||
332 | sband = local->hw.wiphy->bands[band]; | 297 | sband = local->hw.wiphy->bands[band]; |
333 | rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); | 298 | rates = ieee80211_sta_get_rates(local, elems, band, &basic_rates); |
334 | 299 | ||
335 | sta = sta_info_get(sdata, addr); | ||
336 | if (!sta) { | ||
337 | /* Userspace handles peer allocation when security is enabled */ | ||
338 | if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { | ||
339 | cfg80211_notify_new_peer_candidate(sdata->dev, addr, | ||
340 | elems->ie_start, | ||
341 | elems->total_len, | ||
342 | GFP_ATOMIC); | ||
343 | return NULL; | ||
344 | } | ||
345 | |||
346 | sta = mesh_plink_alloc(sdata, addr); | ||
347 | if (!sta) | ||
348 | return NULL; | ||
349 | insert = true; | ||
350 | } | ||
351 | |||
352 | spin_lock_bh(&sta->lock); | 300 | spin_lock_bh(&sta->lock); |
353 | sta->last_rx = jiffies; | 301 | sta->last_rx = jiffies; |
354 | if (sta->plink_state == NL80211_PLINK_ESTAB) { | 302 | |
355 | spin_unlock_bh(&sta->lock); | 303 | /* rates and capabilities don't change during peering */ |
356 | return sta; | 304 | if (sta->plink_state == NL80211_PLINK_ESTAB) |
357 | } | 305 | goto out; |
358 | 306 | ||
359 | sta->sta.supp_rates[band] = rates; | 307 | sta->sta.supp_rates[band] = rates; |
360 | if (elems->ht_cap_elem && | 308 | if (elems->ht_cap_elem && |
@@ -379,22 +327,104 @@ static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, | |||
379 | 327 | ||
380 | if (insert) | 328 | if (insert) |
381 | rate_control_rate_init(sta); | 329 | rate_control_rate_init(sta); |
330 | out: | ||
382 | spin_unlock_bh(&sta->lock); | 331 | spin_unlock_bh(&sta->lock); |
332 | } | ||
333 | |||
334 | static struct sta_info * | ||
335 | __mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *hw_addr) | ||
336 | { | ||
337 | struct sta_info *sta; | ||
383 | 338 | ||
384 | if (insert && sta_info_insert(sta)) | 339 | if (sdata->local->num_sta >= MESH_MAX_PLINKS) |
385 | return NULL; | 340 | return NULL; |
386 | 341 | ||
342 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); | ||
343 | if (!sta) | ||
344 | return NULL; | ||
345 | |||
346 | sta->plink_state = NL80211_PLINK_LISTEN; | ||
347 | init_timer(&sta->plink_timer); | ||
348 | |||
349 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTH); | ||
350 | sta_info_pre_move_state(sta, IEEE80211_STA_ASSOC); | ||
351 | sta_info_pre_move_state(sta, IEEE80211_STA_AUTHORIZED); | ||
352 | |||
353 | set_sta_flag(sta, WLAN_STA_WME); | ||
354 | |||
355 | return sta; | ||
356 | } | ||
357 | |||
358 | static struct sta_info * | ||
359 | mesh_sta_info_alloc(struct ieee80211_sub_if_data *sdata, u8 *addr, | ||
360 | struct ieee802_11_elems *elems) | ||
361 | { | ||
362 | struct sta_info *sta = NULL; | ||
363 | |||
364 | /* Userspace handles peer allocation when security is enabled */ | ||
365 | if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) | ||
366 | cfg80211_notify_new_peer_candidate(sdata->dev, addr, | ||
367 | elems->ie_start, | ||
368 | elems->total_len, | ||
369 | GFP_KERNEL); | ||
370 | else | ||
371 | sta = __mesh_sta_info_alloc(sdata, addr); | ||
372 | |||
387 | return sta; | 373 | return sta; |
388 | } | 374 | } |
389 | 375 | ||
376 | /* | ||
377 | * mesh_sta_info_get - return mesh sta info entry for @addr. | ||
378 | * | ||
379 | * @sdata: local meshif | ||
380 | * @addr: peer's address | ||
381 | * @elems: IEs from beacon or mesh peering frame. | ||
382 | * | ||
383 | * Return existing or newly allocated sta_info under RCU read lock. | ||
384 | * (re)initialize with given IEs. | ||
385 | */ | ||
386 | static struct sta_info * | ||
387 | mesh_sta_info_get(struct ieee80211_sub_if_data *sdata, | ||
388 | u8 *addr, struct ieee802_11_elems *elems) __acquires(RCU) | ||
389 | { | ||
390 | struct sta_info *sta = NULL; | ||
391 | |||
392 | rcu_read_lock(); | ||
393 | sta = sta_info_get(sdata, addr); | ||
394 | if (sta) { | ||
395 | mesh_sta_info_init(sdata, sta, elems, false); | ||
396 | } else { | ||
397 | rcu_read_unlock(); | ||
398 | /* can't run atomic */ | ||
399 | sta = mesh_sta_info_alloc(sdata, addr, elems); | ||
400 | if (!sta) { | ||
401 | rcu_read_lock(); | ||
402 | return NULL; | ||
403 | } | ||
404 | |||
405 | if (sta_info_insert_rcu(sta)) | ||
406 | return NULL; | ||
407 | } | ||
408 | |||
409 | return sta; | ||
410 | } | ||
411 | |||
412 | /* | ||
413 | * mesh_neighbour_update - update or initialize new mesh neighbor. | ||
414 | * | ||
415 | * @sdata: local meshif | ||
416 | * @addr: peer's address | ||
417 | * @elems: IEs from beacon or mesh peering frame | ||
418 | * | ||
419 | * Initiates peering if appropriate. | ||
420 | */ | ||
390 | void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, | 421 | void mesh_neighbour_update(struct ieee80211_sub_if_data *sdata, |
391 | u8 *hw_addr, | 422 | u8 *hw_addr, |
392 | struct ieee802_11_elems *elems) | 423 | struct ieee802_11_elems *elems) |
393 | { | 424 | { |
394 | struct sta_info *sta; | 425 | struct sta_info *sta; |
395 | 426 | ||
396 | rcu_read_lock(); | 427 | sta = mesh_sta_info_get(sdata, hw_addr, elems); |
397 | sta = mesh_peer_init(sdata, hw_addr, elems); | ||
398 | if (!sta) | 428 | if (!sta) |
399 | goto out; | 429 | goto out; |
400 | 430 | ||
@@ -632,6 +662,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
632 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) | 662 | (ftype == WLAN_SP_MESH_PEERING_CLOSE && ie_len == 8)) |
633 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); | 663 | memcpy(&llid, PLINK_GET_PLID(elems.peering), 2); |
634 | 664 | ||
665 | /* WARNING: Only for sta pointer, is dropped & re-acquired */ | ||
635 | rcu_read_lock(); | 666 | rcu_read_lock(); |
636 | 667 | ||
637 | sta = sta_info_get(sdata, mgmt->sa); | 668 | sta = sta_info_get(sdata, mgmt->sa); |
@@ -735,8 +766,9 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
735 | } | 766 | } |
736 | 767 | ||
737 | if (event == OPN_ACPT) { | 768 | if (event == OPN_ACPT) { |
769 | rcu_read_unlock(); | ||
738 | /* allocate sta entry if necessary and update info */ | 770 | /* allocate sta entry if necessary and update info */ |
739 | sta = mesh_peer_init(sdata, mgmt->sa, &elems); | 771 | sta = mesh_sta_info_get(sdata, mgmt->sa, &elems); |
740 | if (!sta) { | 772 | if (!sta) { |
741 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); | 773 | mpl_dbg(sdata, "Mesh plink: failed to init peer!\n"); |
742 | rcu_read_unlock(); | 774 | rcu_read_unlock(); |