From 75de9113bb9dc4939a7cd54e4bdfad555b35f5b1 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Fri, 14 Dec 2012 14:56:03 +0100 Subject: [PATCH] mac80211: optimise AP stop RCU handling If there are VLANs, stopping an AP is inefficient as it calls rcu_barrier() once for each interface (the VLANs and the AP itself). Optimise this by moving rcu_barrier() out of the station cleanups and calling it only once for all interfaces combined. Signed-off-by: Johannes Berg --- net/mac80211/cfg.c | 9 +++++++-- net/mac80211/iface.c | 13 ++++++++----- net/mac80211/sta_info.c | 2 -- net/mac80211/sta_info.h | 14 ++++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index be01953..908f115 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c @@ -1010,8 +1010,13 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) kfree_rcu(old_probe_resp, rcu_head); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) - sta_info_flush(vlan); - sta_info_flush(sdata); + sta_info_flush_defer(vlan); + sta_info_flush_defer(sdata); + rcu_barrier(); + list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) + sta_info_flush_cleanup(vlan); + sta_info_flush_cleanup(sdata); + ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_BEACON_ENABLED); drv_stop_ap(sdata->local, sdata); diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c index 12341ef..1ab7d8e 100644 --- a/net/mac80211/iface.c +++ b/net/mac80211/iface.c @@ -863,12 +863,15 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, cancel_work_sync(&sdata->work); /* * When we get here, the interface is marked down. - * sta_info_flush_cleanup() calls rcu_barrier to - * wait for the station call_rcu() calls to complete, - * here we require it to wait for the RX path in case - * it is using the interface and enqueuing frames at - * this very time on another CPU. + * + * sta_info_flush_cleanup() requires rcu_barrier() + * first to wait for the station call_rcu() calls + * to complete, here we need at least sychronize_rcu() + * it to wait for the RX path in case it is using the + * interface and enqueuing frames at this very time on + * another CPU. */ + rcu_barrier(); sta_info_flush_cleanup(sdata); skb_queue_purge(&sdata->skb_queue); diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index 7199b9d..738f934 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -920,8 +920,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata) { - rcu_barrier(); - ieee80211_cleanup_sdata_stas(sdata); cancel_work_sync(&sdata->cleanup_stations_wk); } diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h index c3266aed..031e4a5 100644 --- a/net/mac80211/sta_info.h +++ b/net/mac80211/sta_info.h @@ -549,6 +549,19 @@ void sta_info_recalc_tim(struct sta_info *sta); void sta_info_init(struct ieee80211_local *local); void sta_info_stop(struct ieee80211_local *local); int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata); + +/** + * sta_info_flush_cleanup - flush the sta_info cleanup queue + * @sdata: the interface + * + * Flushes the sta_info cleanup queue for a given interface; + * this is necessary before the interface is removed or, for + * AP/mesh interfaces, before it is deconfigured. + * + * Note an rcu_barrier() must precede the function, after all + * stations have been flushed/removed to ensure the call_rcu() + * calls that add stations to the cleanup queue have completed. + */ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); /** @@ -562,6 +575,7 @@ static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) { int ret = sta_info_flush_defer(sdata); + rcu_barrier(); sta_info_flush_cleanup(sdata); return ret; -- 2.7.4