aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/sta_info.c
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2014-02-17 14:49:03 -0500
committerJohannes Berg <johannes.berg@intel.com>2014-02-20 04:34:33 -0500
commit5108ca828017120981880eeec8a9ec369334a899 (patch)
tree9930ecfd70e14e82144a229c6be80ff51a116e7f /net/mac80211/sta_info.c
parent1d147bfa64293b2723c4fec50922168658e613ba (diff)
mac80211: insert stations before adding to driver
There's a race condition in mac80211 because we add stations to the internal lists after adding them to the driver, which means that (for example) the following can happen: 1. a station connects and is added 2. first, it is added to the driver 3. then, it is added to the mac80211 lists If the station goes to sleep between steps 2 and 3, and the firmware/hardware records it as being asleep, mac80211 will never instruct the driver to wake it up again as it never realized it went to sleep since the RX path discarded the frame as a "spurious class 3 frame", no station entry was present yet. Fix this by adding the station in software first, and only then adding it to the driver. That way, any state that the driver changes will be reflected properly in mac80211's station state. The problematic part is the roll-back if the driver fails to add the station, in that case a bit more is needed. To not make that overly complex prevent starting BA sessions in the meantime. Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net/mac80211/sta_info.c')
-rw-r--r--net/mac80211/sta_info.c30
1 files changed, 24 insertions, 6 deletions
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index 62a5f0889583..ffc1ee6a2ec1 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,7 +91,7 @@ static int sta_info_hash_del(struct ieee80211_local *local,
91 return -ENOENT; 91 return -ENOENT;
92} 92}
93 93
94static void cleanup_single_sta(struct sta_info *sta) 94static void __cleanup_single_sta(struct sta_info *sta)
95{ 95{
96 int ac, i; 96 int ac, i;
97 struct tid_ampdu_tx *tid_tx; 97 struct tid_ampdu_tx *tid_tx;
@@ -139,7 +139,14 @@ static void cleanup_single_sta(struct sta_info *sta)
139 ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending); 139 ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending);
140 kfree(tid_tx); 140 kfree(tid_tx);
141 } 141 }
142}
142 143
144static void cleanup_single_sta(struct sta_info *sta)
145{
146 struct ieee80211_sub_if_data *sdata = sta->sdata;
147 struct ieee80211_local *local = sdata->local;
148
149 __cleanup_single_sta(sta);
143 sta_info_free(local, sta); 150 sta_info_free(local, sta);
144} 151}
145 152
@@ -488,21 +495,26 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
488 goto out_err; 495 goto out_err;
489 } 496 }
490 497
491 /* notify driver */
492 err = sta_info_insert_drv_state(local, sdata, sta);
493 if (err)
494 goto out_err;
495
496 local->num_sta++; 498 local->num_sta++;
497 local->sta_generation++; 499 local->sta_generation++;
498 smp_mb(); 500 smp_mb();
499 501
502 /* simplify things and don't accept BA sessions yet */
503 set_sta_flag(sta, WLAN_STA_BLOCK_BA);
504
500 /* make the station visible */ 505 /* make the station visible */
501 sta_info_hash_add(local, sta); 506 sta_info_hash_add(local, sta);
502 507
503 list_add_rcu(&sta->list, &local->sta_list); 508 list_add_rcu(&sta->list, &local->sta_list);
504 509
510 /* notify driver */
511 err = sta_info_insert_drv_state(local, sdata, sta);
512 if (err)
513 goto out_remove;
514
505 set_sta_flag(sta, WLAN_STA_INSERTED); 515 set_sta_flag(sta, WLAN_STA_INSERTED);
516 /* accept BA sessions now */
517 clear_sta_flag(sta, WLAN_STA_BLOCK_BA);
506 518
507 ieee80211_recalc_min_chandef(sdata); 519 ieee80211_recalc_min_chandef(sdata);
508 ieee80211_sta_debugfs_add(sta); 520 ieee80211_sta_debugfs_add(sta);
@@ -523,6 +535,12 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU)
523 mesh_accept_plinks_update(sdata); 535 mesh_accept_plinks_update(sdata);
524 536
525 return 0; 537 return 0;
538 out_remove:
539 sta_info_hash_del(local, sta);
540 list_del_rcu(&sta->list);
541 local->num_sta--;
542 synchronize_net();
543 __cleanup_single_sta(sta);
526 out_err: 544 out_err:
527 mutex_unlock(&local->sta_mtx); 545 mutex_unlock(&local->sta_mtx);
528 rcu_read_lock(); 546 rcu_read_lock();