aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/iface.c
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/mac80211/iface.c
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/mac80211/iface.c')
-rw-r--r--net/mac80211/iface.c28
1 files changed, 16 insertions, 12 deletions
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];