aboutsummaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJohannes Berg <johannes.berg@intel.com>2012-12-13 16:54:58 -0500
committerJohannes Berg <johannes.berg@intel.com>2013-01-03 06:59:59 -0500
commit97f97b1f5fe0878b35c8e314f98591771696321b (patch)
treed51b86324030567fe51f9112c2d9d2242e07c008 /net
parentb7cfcd113ac2a1e6b02afc7d283295729fc178a9 (diff)
mac80211: fix station destruction in AP/mesh modes
Unfortunately, commit b22cfcfcae5b, intended to speed up roaming by avoiding the synchronize_rcu() broke AP/mesh modes as it moved some code into that work item that will still call into the driver at a time where it's no longer expected to handle this: after the AP or mesh has been stopped. To fix this problem remove the per-station work struct, maintain a station cleanup list instead and flush this list when stations are flushed. To keep this patch smaller for stable, do this when the stations are flushed (sta_info_flush()). This unfortunately brings back the original roaming delay; I'll fix that again in a separate patch. Also, Ben reported that the original commit could sometimes (with many interfaces) cause long delays when an interface is set down, due to blocking on flush_workqueue(). Since we now maintain the cleanup list, this particular change of the original patch can be reverted. Cc: stable@vger.kernel.org [3.7] Reported-by: Ben Greear <greearb@candelatech.com> Tested-by: Ben Greear <greearb@candelatech.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/iface.c28
-rw-r--r--net/mac80211/sta_info.c44
-rw-r--r--net/mac80211/sta_info.h3
4 files changed, 62 insertions, 17 deletions
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index 28fc9875537c..2fcd274877d7 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -773,6 +773,10 @@ struct ieee80211_sub_if_data {
773 u32 mntr_flags; 773 u32 mntr_flags;
774 } u; 774 } u;
775 775
776 spinlock_t cleanup_stations_lock;
777 struct list_head cleanup_stations;
778 struct work_struct cleanup_stations_wk;
779
776#ifdef CONFIG_MAC80211_DEBUGFS 780#ifdef CONFIG_MAC80211_DEBUGFS
777 struct { 781 struct {
778 struct dentry *dir; 782 struct dentry *dir;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 54fb7f9db564..0f2a9f987f79 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -868,20 +868,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
868 cancel_work_sync(&sdata->work); 868 cancel_work_sync(&sdata->work);
869 /* 869 /*
870 * When we get here, the interface is marked down. 870 * When we get here, the interface is marked down.
871 * Call rcu_barrier() to wait both for the RX path 871 * Call synchronize_rcu() to wait for the RX path
872 * should it be using the interface and enqueuing 872 * should it be using the interface and enqueuing
873 * frames at this very time on another CPU, and 873 * frames at this very time on another CPU.
874 * for the sta free call_rcu callbacks.
875 */ 874 */
876 rcu_barrier(); 875 synchronize_rcu();
877
878 /*
879 * free_sta_rcu() enqueues a work for the actual
880 * sta cleanup, so we need to flush it while
881 * sdata is still valid.
882 */
883 flush_workqueue(local->workqueue);
884
885 skb_queue_purge(&sdata->skb_queue); 876 skb_queue_purge(&sdata->skb_queue);
886 877
887 /* 878 /*
@@ -1501,6 +1492,15 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
1501 mutex_unlock(&local->iflist_mtx); 1492 mutex_unlock(&local->iflist_mtx);
1502} 1493}
1503 1494
1495static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
1496{
1497 struct ieee80211_sub_if_data *sdata;
1498
1499 sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
1500
1501 ieee80211_cleanup_sdata_stas(sdata);
1502}
1503
1504int ieee80211_if_add(struct ieee80211_local *local, const char *name, 1504int ieee80211_if_add(struct ieee80211_local *local, const char *name,
1505 struct wireless_dev **new_wdev, enum nl80211_iftype type, 1505 struct wireless_dev **new_wdev, enum nl80211_iftype type,
1506 struct vif_params *params) 1506 struct vif_params *params)
@@ -1576,6 +1576,10 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
1576 1576
1577 INIT_LIST_HEAD(&sdata->key_list); 1577 INIT_LIST_HEAD(&sdata->key_list);
1578 1578
1579 spin_lock_init(&sdata->cleanup_stations_lock);
1580 INIT_LIST_HEAD(&sdata->cleanup_stations);
1581 INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
1582
1579 for (i = 0; i < IEEE80211_NUM_BANDS; i++) { 1583 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
1580 struct ieee80211_supported_band *sband; 1584 struct ieee80211_supported_band *sband;
1581 sband = local->hw.wiphy->bands[i]; 1585 sband = local->hw.wiphy->bands[i];
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index f3e502502fee..8bbd3b0fdbcc 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -91,9 +91,8 @@ static int sta_info_hash_del(struct ieee80211_local *local,
91 return -ENOENT; 91 return -ENOENT;
92} 92}
93 93
94static void free_sta_work(struct work_struct *wk) 94static void cleanup_single_sta(struct sta_info *sta)
95{ 95{
96 struct sta_info *sta = container_of(wk, struct sta_info, free_sta_wk);
97 int ac, i; 96 int ac, i;
98 struct tid_ampdu_tx *tid_tx; 97 struct tid_ampdu_tx *tid_tx;
99 struct ieee80211_sub_if_data *sdata = sta->sdata; 98 struct ieee80211_sub_if_data *sdata = sta->sdata;
@@ -153,11 +152,35 @@ static void free_sta_work(struct work_struct *wk)
153 sta_info_free(local, sta); 152 sta_info_free(local, sta);
154} 153}
155 154
155void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
156{
157 struct sta_info *sta;
158
159 spin_lock_bh(&sdata->cleanup_stations_lock);
160 while (!list_empty(&sdata->cleanup_stations)) {
161 sta = list_first_entry(&sdata->cleanup_stations,
162 struct sta_info, list);
163 list_del(&sta->list);
164 spin_unlock_bh(&sdata->cleanup_stations_lock);
165
166 cleanup_single_sta(sta);
167
168 spin_lock_bh(&sdata->cleanup_stations_lock);
169 }
170
171 spin_unlock_bh(&sdata->cleanup_stations_lock);
172}
173
156static void free_sta_rcu(struct rcu_head *h) 174static void free_sta_rcu(struct rcu_head *h)
157{ 175{
158 struct sta_info *sta = container_of(h, struct sta_info, rcu_head); 176 struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
177 struct ieee80211_sub_if_data *sdata = sta->sdata;
159 178
160 ieee80211_queue_work(&sta->local->hw, &sta->free_sta_wk); 179 spin_lock(&sdata->cleanup_stations_lock);
180 list_add_tail(&sta->list, &sdata->cleanup_stations);
181 spin_unlock(&sdata->cleanup_stations_lock);
182
183 ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
161} 184}
162 185
163/* protected by RCU */ 186/* protected by RCU */
@@ -310,7 +333,6 @@ struct sta_info *sta_info_alloc(struct ieee80211_sub_if_data *sdata,
310 333
311 spin_lock_init(&sta->lock); 334 spin_lock_init(&sta->lock);
312 INIT_WORK(&sta->drv_unblock_wk, sta_unblock); 335 INIT_WORK(&sta->drv_unblock_wk, sta_unblock);
313 INIT_WORK(&sta->free_sta_wk, free_sta_work);
314 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work); 336 INIT_WORK(&sta->ampdu_mlme.work, ieee80211_ba_session_work);
315 mutex_init(&sta->ampdu_mlme.mtx); 337 mutex_init(&sta->ampdu_mlme.mtx);
316 338
@@ -891,6 +913,20 @@ int sta_info_flush(struct ieee80211_local *local,
891 } 913 }
892 mutex_unlock(&local->sta_mtx); 914 mutex_unlock(&local->sta_mtx);
893 915
916 rcu_barrier();
917
918 if (sdata) {
919 ieee80211_cleanup_sdata_stas(sdata);
920 cancel_work_sync(&sdata->cleanup_stations_wk);
921 } else {
922 mutex_lock(&local->iflist_mtx);
923 list_for_each_entry(sdata, &local->interfaces, list) {
924 ieee80211_cleanup_sdata_stas(sdata);
925 cancel_work_sync(&sdata->cleanup_stations_wk);
926 }
927 mutex_unlock(&local->iflist_mtx);
928 }
929
894 return ret; 930 return ret;
895} 931}
896 932
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 1489bca9ea97..37c1889afd3a 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -299,7 +299,6 @@ struct sta_info {
299 spinlock_t lock; 299 spinlock_t lock;
300 300
301 struct work_struct drv_unblock_wk; 301 struct work_struct drv_unblock_wk;
302 struct work_struct free_sta_wk;
303 302
304 u16 listen_interval; 303 u16 listen_interval;
305 304
@@ -563,4 +562,6 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
563void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); 562void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
564void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); 563void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
565 564
565void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
566
566#endif /* STA_INFO_H */ 567#endif /* STA_INFO_H */