diff options
author | Thomas Pedersen <thomas@cozybit.com> | 2012-04-18 22:23:42 -0400 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2012-04-23 15:34:07 -0400 |
commit | 54ab1ffb6cd94e5c013d61c192e78e30fdf25f8a (patch) | |
tree | 4ec8399914d37eb20d2231bb16cf5e7bdfc80eab /net | |
parent | 75acd5a82afda30fb615335ff6c8e5f3a1ca5e83 (diff) |
mac80211: refactor mesh peer initialization
This patch unifies the previous two paths toward mesh peer creation a
bit. It also fixes a bug where a peer's changing rates or HT mode
wouldn't register on leaving and then returning to the mesh with a sta
entry still present.
Also clean up locking and clear possibly stale ht cap.
Signed-off-by: Thomas Pedersen <thomas@cozybit.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/mesh_plink.c | 126 |
1 files changed, 72 insertions, 54 deletions
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c index 9c836e774fbd..c3a0b0a4f97f 100644 --- a/net/mac80211/mesh_plink.c +++ b/net/mac80211/mesh_plink.c | |||
@@ -82,20 +82,14 @@ static inline void mesh_plink_fsm_restart(struct sta_info *sta) | |||
82 | } | 82 | } |
83 | 83 | ||
84 | /* | 84 | /* |
85 | * NOTE: This is just an alias for sta_info_alloc(), see notes | 85 | * Allocate mesh sta entry and insert into station table |
86 | * on it in the lifecycle management section! | ||
87 | */ | 86 | */ |
88 | static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, | 87 | static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, |
89 | u8 *hw_addr, u32 rates, | 88 | u8 *hw_addr) |
90 | struct ieee802_11_elems *elems) | ||
91 | { | 89 | { |
92 | struct ieee80211_local *local = sdata->local; | ||
93 | struct ieee80211_supported_band *sband; | ||
94 | struct sta_info *sta; | 90 | struct sta_info *sta; |
95 | 91 | ||
96 | sband = local->hw.wiphy->bands[local->oper_channel->band]; | 92 | if (sdata->local->num_sta >= MESH_MAX_PLINKS) |
97 | |||
98 | if (local->num_sta >= MESH_MAX_PLINKS) | ||
99 | return NULL; | 93 | return NULL; |
100 | 94 | ||
101 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); | 95 | sta = sta_info_alloc(sdata, hw_addr, GFP_KERNEL); |
@@ -108,12 +102,8 @@ static struct sta_info *mesh_plink_alloc(struct ieee80211_sub_if_data *sdata, | |||
108 | 102 | ||
109 | set_sta_flag(sta, WLAN_STA_WME); | 103 | set_sta_flag(sta, WLAN_STA_WME); |
110 | 104 | ||
111 | sta->sta.supp_rates[local->hw.conf.channel->band] = rates; | 105 | if (sta_info_insert(sta)) |
112 | if (elems->ht_cap_elem) | 106 | return NULL; |
113 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | ||
114 | elems->ht_cap_elem, | ||
115 | &sta->sta.ht_cap); | ||
116 | rate_control_rate_init(sta); | ||
117 | 107 | ||
118 | return sta; | 108 | return sta; |
119 | } | 109 | } |
@@ -274,43 +264,76 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata, | |||
274 | return 0; | 264 | return 0; |
275 | } | 265 | } |
276 | 266 | ||
277 | void mesh_neighbour_update(u8 *hw_addr, u32 rates, | 267 | /* mesh_peer_init - initialize new mesh peer and return resulting sta_info |
278 | struct ieee80211_sub_if_data *sdata, | 268 | * |
279 | struct ieee802_11_elems *elems) | 269 | * @sdata: local meshif |
270 | * @addr: peer's address | ||
271 | * @rates: station's supported rates | ||
272 | * @elems: IEs from beacon or mesh peering frame | ||
273 | * | ||
274 | * call under RCU | ||
275 | */ | ||
276 | static struct sta_info *mesh_peer_init(struct ieee80211_sub_if_data *sdata, | ||
277 | u8 *addr, u32 rates, | ||
278 | struct ieee802_11_elems *elems) | ||
280 | { | 279 | { |
281 | struct ieee80211_local *local = sdata->local; | 280 | struct ieee80211_local *local = sdata->local; |
281 | struct ieee80211_supported_band *sband; | ||
282 | struct sta_info *sta; | 282 | struct sta_info *sta; |
283 | 283 | ||
284 | rcu_read_lock(); | 284 | sband = local->hw.wiphy->bands[local->oper_channel->band]; |
285 | 285 | ||
286 | sta = sta_info_get(sdata, hw_addr); | 286 | sta = sta_info_get(sdata, addr); |
287 | if (!sta) { | 287 | if (!sta) { |
288 | rcu_read_unlock(); | 288 | sta = mesh_plink_alloc(sdata, addr); |
289 | /* Userspace handles peer allocation when security is enabled | ||
290 | * */ | ||
291 | if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) | ||
292 | cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, | ||
293 | elems->ie_start, elems->total_len, | ||
294 | GFP_KERNEL); | ||
295 | else | ||
296 | sta = mesh_plink_alloc(sdata, hw_addr, rates, elems); | ||
297 | if (!sta) | 289 | if (!sta) |
298 | return; | 290 | return NULL; |
299 | if (sta_info_insert_rcu(sta)) { | ||
300 | rcu_read_unlock(); | ||
301 | return; | ||
302 | } | ||
303 | } | 291 | } |
304 | 292 | ||
293 | spin_lock_bh(&sta->lock); | ||
305 | sta->last_rx = jiffies; | 294 | sta->last_rx = jiffies; |
306 | sta->sta.supp_rates[local->hw.conf.channel->band] = rates; | 295 | sta->sta.supp_rates[local->hw.conf.channel->band] = rates; |
296 | if (elems->ht_cap_elem) | ||
297 | ieee80211_ht_cap_ie_to_sta_ht_cap(sdata, sband, | ||
298 | elems->ht_cap_elem, | ||
299 | &sta->sta.ht_cap); | ||
300 | else | ||
301 | memset(&sta->sta.ht_cap, 0, sizeof(sta->sta.ht_cap)); | ||
302 | |||
303 | rate_control_rate_init(sta); | ||
304 | spin_unlock_bh(&sta->lock); | ||
305 | |||
306 | return sta; | ||
307 | } | ||
308 | |||
309 | void mesh_neighbour_update(u8 *hw_addr, u32 rates, | ||
310 | struct ieee80211_sub_if_data *sdata, | ||
311 | struct ieee802_11_elems *elems) | ||
312 | { | ||
313 | struct sta_info *sta; | ||
314 | |||
315 | /* Userspace handles peer allocation when security is enabled */ | ||
316 | if (sdata->u.mesh.security & IEEE80211_MESH_SEC_AUTHED) { | ||
317 | cfg80211_notify_new_peer_candidate(sdata->dev, hw_addr, | ||
318 | elems->ie_start, | ||
319 | elems->total_len, | ||
320 | GFP_KERNEL); | ||
321 | return; | ||
322 | } | ||
323 | |||
324 | rcu_read_lock(); | ||
325 | sta = mesh_peer_init(sdata, hw_addr, rates, elems); | ||
326 | if (!sta) | ||
327 | goto out; | ||
328 | |||
307 | if (mesh_peer_accepts_plinks(elems) && | 329 | if (mesh_peer_accepts_plinks(elems) && |
308 | sta->plink_state == NL80211_PLINK_LISTEN && | 330 | sta->plink_state == NL80211_PLINK_LISTEN && |
309 | sdata->u.mesh.accepting_plinks && | 331 | sdata->u.mesh.accepting_plinks && |
310 | sdata->u.mesh.mshcfg.auto_open_plinks && | 332 | sdata->u.mesh.mshcfg.auto_open_plinks && |
311 | rssi_threshold_check(sta, sdata)) | 333 | rssi_threshold_check(sta, sdata)) |
312 | mesh_plink_open(sta); | 334 | mesh_plink_open(sta); |
313 | 335 | ||
336 | out: | ||
314 | rcu_read_unlock(); | 337 | rcu_read_unlock(); |
315 | } | 338 | } |
316 | 339 | ||
@@ -587,26 +610,13 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
587 | return; | 610 | return; |
588 | } else if (!sta) { | 611 | } else if (!sta) { |
589 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ | 612 | /* ftype == WLAN_SP_MESH_PEERING_OPEN */ |
590 | |||
591 | rcu_read_unlock(); | ||
592 | |||
593 | if (!mesh_plink_free_count(sdata)) { | 613 | if (!mesh_plink_free_count(sdata)) { |
594 | mpl_dbg("Mesh plink error: no more free plinks\n"); | 614 | mpl_dbg("Mesh plink error: no more free plinks\n"); |
595 | return; | ||
596 | } | ||
597 | sta = mesh_plink_alloc(sdata, mgmt->sa, rates, &elems); | ||
598 | if (!sta) { | ||
599 | mpl_dbg("Mesh plink error: plink table full\n"); | ||
600 | return; | ||
601 | } | ||
602 | if (sta_info_insert_rcu(sta)) { | ||
603 | rcu_read_unlock(); | 615 | rcu_read_unlock(); |
604 | return; | 616 | return; |
605 | } | 617 | } |
606 | event = OPN_ACPT; | 618 | event = OPN_ACPT; |
607 | spin_lock_bh(&sta->lock); | ||
608 | } else if (matches_local) { | 619 | } else if (matches_local) { |
609 | spin_lock_bh(&sta->lock); | ||
610 | switch (ftype) { | 620 | switch (ftype) { |
611 | case WLAN_SP_MESH_PEERING_OPEN: | 621 | case WLAN_SP_MESH_PEERING_OPEN: |
612 | if (!mesh_plink_free_count(sdata) || | 622 | if (!mesh_plink_free_count(sdata) || |
@@ -643,12 +653,19 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
643 | break; | 653 | break; |
644 | default: | 654 | default: |
645 | mpl_dbg("Mesh plink: unknown frame subtype\n"); | 655 | mpl_dbg("Mesh plink: unknown frame subtype\n"); |
646 | spin_unlock_bh(&sta->lock); | ||
647 | rcu_read_unlock(); | 656 | rcu_read_unlock(); |
648 | return; | 657 | return; |
649 | } | 658 | } |
650 | } else { | 659 | } |
651 | spin_lock_bh(&sta->lock); | 660 | |
661 | if (event == OPN_ACPT) { | ||
662 | /* allocate sta entry if necessary and update info */ | ||
663 | sta = mesh_peer_init(sdata, mgmt->sa, rates, &elems); | ||
664 | if (!sta) { | ||
665 | mpl_dbg("Mesh plink: failed to init peer!\n"); | ||
666 | rcu_read_unlock(); | ||
667 | return; | ||
668 | } | ||
652 | } | 669 | } |
653 | 670 | ||
654 | mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", | 671 | mpl_dbg("Mesh plink (peer, state, llid, plid, event): %pM %s %d %d %d\n", |
@@ -656,6 +673,7 @@ void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_m | |||
656 | le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), | 673 | le16_to_cpu(sta->llid), le16_to_cpu(sta->plid), |
657 | event); | 674 | event); |
658 | reason = 0; | 675 | reason = 0; |
676 | spin_lock_bh(&sta->lock); | ||
659 | switch (sta->plink_state) { | 677 | switch (sta->plink_state) { |
660 | /* spin_unlock as soon as state is updated at each case */ | 678 | /* spin_unlock as soon as state is updated at each case */ |
661 | case NL80211_PLINK_LISTEN: | 679 | case NL80211_PLINK_LISTEN: |