Commit d34ba216 authored by Johannes Berg's avatar Johannes Berg

mac80211: don't delay station destruction

If we can assume that stations are never referenced by the
driver after sta_state returns (and this is true since the
previous iwlmvm patch and for all other drivers) then we
don't need to delay station destruction, and don't need to
play tricks with rcu_barrier() etc.

This should speed up some scenarios like hostapd shutdown.
Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent a710c816
...@@ -1098,15 +1098,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev) ...@@ -1098,15 +1098,11 @@ static int ieee80211_stop_ap(struct wiphy *wiphy, struct net_device *dev)
kfree_rcu(old_probe_resp, rcu_head); kfree_rcu(old_probe_resp, rcu_head);
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
sta_info_flush_defer(vlan); sta_info_flush(vlan);
sta_info_flush_defer(sdata); sta_info_flush(sdata);
synchronize_net(); synchronize_net();
rcu_barrier(); list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list)
list_for_each_entry(vlan, &sdata->u.ap.vlans, u.vlan.list) {
sta_info_flush_cleanup(vlan);
ieee80211_free_keys(vlan); ieee80211_free_keys(vlan);
}
sta_info_flush_cleanup(sdata);
ieee80211_free_keys(sdata); ieee80211_free_keys(sdata);
sdata->vif.bss_conf.enable_beacon = false; sdata->vif.bss_conf.enable_beacon = false;
......
...@@ -776,10 +776,6 @@ struct ieee80211_sub_if_data { ...@@ -776,10 +776,6 @@ struct ieee80211_sub_if_data {
u32 mntr_flags; u32 mntr_flags;
} u; } u;
spinlock_t cleanup_stations_lock;
struct list_head cleanup_stations;
struct work_struct cleanup_stations_wk;
#ifdef CONFIG_MAC80211_DEBUGFS #ifdef CONFIG_MAC80211_DEBUGFS
struct { struct {
struct dentry *subdir_stations; struct dentry *subdir_stations;
......
...@@ -786,10 +786,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -786,10 +786,8 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
* This is relevant only in WDS mode, in all other modes we've * This is relevant only in WDS mode, in all other modes we've
* already removed all stations when disconnecting or similar, * already removed all stations when disconnecting or similar,
* so warn otherwise. * so warn otherwise.
*
* We call sta_info_flush_cleanup() later, to combine RCU waits.
*/ */
flushed = sta_info_flush_defer(sdata); flushed = sta_info_flush(sdata);
WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) || WARN_ON_ONCE((sdata->vif.type != NL80211_IFTYPE_WDS && flushed > 0) ||
(sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1)); (sdata->vif.type == NL80211_IFTYPE_WDS && flushed != 1));
...@@ -892,16 +890,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata, ...@@ -892,16 +890,11 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
/* /*
* When we get here, the interface is marked down. * When we get here, the interface is marked down.
* *
* sta_info_flush_cleanup() requires rcu_barrier() * We need synchronize_rcu() to wait for the RX path in
* first to wait for the station call_rcu() calls * case it is using the interface and enqueuing frames
* to complete, and we also need synchronize_rcu() * at this very time on another CPU.
* to wait for the RX path in case it is using the
* interface and enqueuing frames at this very time on
* another CPU.
*/ */
synchronize_rcu(); synchronize_rcu();
rcu_barrier();
sta_info_flush_cleanup(sdata);
/* /*
* Free all remaining keys, there shouldn't be any, * Free all remaining keys, there shouldn't be any,
...@@ -1568,15 +1561,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local, ...@@ -1568,15 +1561,6 @@ static void ieee80211_assign_perm_addr(struct ieee80211_local *local,
mutex_unlock(&local->iflist_mtx); mutex_unlock(&local->iflist_mtx);
} }
static void ieee80211_cleanup_sdata_stas_wk(struct work_struct *wk)
{
struct ieee80211_sub_if_data *sdata;
sdata = container_of(wk, struct ieee80211_sub_if_data, cleanup_stations_wk);
ieee80211_cleanup_sdata_stas(sdata);
}
int ieee80211_if_add(struct ieee80211_local *local, const char *name, int ieee80211_if_add(struct ieee80211_local *local, const char *name,
struct wireless_dev **new_wdev, enum nl80211_iftype type, struct wireless_dev **new_wdev, enum nl80211_iftype type,
struct vif_params *params) struct vif_params *params)
...@@ -1649,9 +1633,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name, ...@@ -1649,9 +1633,6 @@ int ieee80211_if_add(struct ieee80211_local *local, const char *name,
INIT_LIST_HEAD(&sdata->key_list); INIT_LIST_HEAD(&sdata->key_list);
spin_lock_init(&sdata->cleanup_stations_lock);
INIT_LIST_HEAD(&sdata->cleanup_stations);
INIT_WORK(&sdata->cleanup_stations_wk, ieee80211_cleanup_sdata_stas_wk);
INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work, INIT_DELAYED_WORK(&sdata->dfs_cac_timer_work,
ieee80211_dfs_cac_timer_work); ieee80211_dfs_cac_timer_work);
INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk, INIT_DELAYED_WORK(&sdata->dec_tailroom_needed_wk,
......
...@@ -1698,7 +1698,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata, ...@@ -1698,7 +1698,7 @@ static void ieee80211_set_disassoc(struct ieee80211_sub_if_data *sdata,
memset(ifmgd->bssid, 0, ETH_ALEN); memset(ifmgd->bssid, 0, ETH_ALEN);
/* remove AP and TDLS peers */ /* remove AP and TDLS peers */
sta_info_flush_defer(sdata); sta_info_flush(sdata);
/* finally reset all BSS / config parameters */ /* finally reset all BSS / config parameters */
changed |= ieee80211_reset_erp_info(sdata); changed |= ieee80211_reset_erp_info(sdata);
......
...@@ -37,9 +37,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) ...@@ -37,9 +37,8 @@ int __ieee80211_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
IEEE80211_MAX_QUEUE_MAP, IEEE80211_MAX_QUEUE_MAP,
IEEE80211_QUEUE_STOP_REASON_SUSPEND); IEEE80211_QUEUE_STOP_REASON_SUSPEND);
/* flush out all packets and station cleanup call_rcu()s */ /* flush out all packets */
synchronize_net(); synchronize_net();
rcu_barrier();
ieee80211_flush_queues(local, NULL); ieee80211_flush_queues(local, NULL);
......
...@@ -99,23 +99,6 @@ static void cleanup_single_sta(struct sta_info *sta) ...@@ -99,23 +99,6 @@ static void cleanup_single_sta(struct sta_info *sta)
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct ps_data *ps; struct ps_data *ps;
/*
* At this point, when being called as call_rcu callback,
* neither mac80211 nor the driver can reference this
* sta struct any more except by still existing timers
* associated with this station that we clean up below.
*
* Note though that this still uses the sdata and even
* calls the driver in AP and mesh mode, so interfaces
* of those types mush use call sta_info_flush_cleanup()
* (typically via sta_info_flush()) before deconfiguring
* the driver.
*
* In station mode, nothing happens here so it doesn't
* have to (and doesn't) do that, this is intentional to
* speed up roaming.
*/
if (test_sta_flag(sta, WLAN_STA_PS_STA)) { if (test_sta_flag(sta, WLAN_STA_PS_STA)) {
if (sta->sdata->vif.type == NL80211_IFTYPE_AP || if (sta->sdata->vif.type == NL80211_IFTYPE_AP ||
sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
...@@ -160,37 +143,6 @@ static void cleanup_single_sta(struct sta_info *sta) ...@@ -160,37 +143,6 @@ static void cleanup_single_sta(struct sta_info *sta)
sta_info_free(local, sta); sta_info_free(local, sta);
} }
void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata)
{
struct sta_info *sta;
spin_lock_bh(&sdata->cleanup_stations_lock);
while (!list_empty(&sdata->cleanup_stations)) {
sta = list_first_entry(&sdata->cleanup_stations,
struct sta_info, list);
list_del(&sta->list);
spin_unlock_bh(&sdata->cleanup_stations_lock);
cleanup_single_sta(sta);
spin_lock_bh(&sdata->cleanup_stations_lock);
}
spin_unlock_bh(&sdata->cleanup_stations_lock);
}
static void free_sta_rcu(struct rcu_head *h)
{
struct sta_info *sta = container_of(h, struct sta_info, rcu_head);
struct ieee80211_sub_if_data *sdata = sta->sdata;
spin_lock(&sdata->cleanup_stations_lock);
list_add_tail(&sta->list, &sdata->cleanup_stations);
spin_unlock(&sdata->cleanup_stations_lock);
ieee80211_queue_work(&sdata->local->hw, &sdata->cleanup_stations_wk);
}
/* protected by RCU */ /* protected by RCU */
struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata, struct sta_info *sta_info_get(struct ieee80211_sub_if_data *sdata,
const u8 *addr) const u8 *addr)
...@@ -909,7 +861,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta) ...@@ -909,7 +861,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)
ieee80211_sta_debugfs_remove(sta); ieee80211_sta_debugfs_remove(sta);
ieee80211_recalc_min_chandef(sdata); ieee80211_recalc_min_chandef(sdata);
call_rcu(&sta->rcu_head, free_sta_rcu); cleanup_single_sta(sta);
return 0; return 0;
} }
...@@ -979,7 +931,7 @@ void sta_info_stop(struct ieee80211_local *local) ...@@ -979,7 +931,7 @@ void sta_info_stop(struct ieee80211_local *local)
} }
int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) int sta_info_flush(struct ieee80211_sub_if_data *sdata)
{ {
struct ieee80211_local *local = sdata->local; struct ieee80211_local *local = sdata->local;
struct sta_info *sta, *tmp; struct sta_info *sta, *tmp;
...@@ -999,12 +951,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata) ...@@ -999,12 +951,6 @@ int sta_info_flush_defer(struct ieee80211_sub_if_data *sdata)
return ret; return ret;
} }
void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata)
{
ieee80211_cleanup_sdata_stas(sdata);
cancel_work_sync(&sdata->cleanup_stations_wk);
}
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata, void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time) unsigned long exp_time)
{ {
......
...@@ -605,21 +605,6 @@ void sta_info_recalc_tim(struct sta_info *sta); ...@@ -605,21 +605,6 @@ void sta_info_recalc_tim(struct sta_info *sta);
void sta_info_init(struct ieee80211_local *local); void sta_info_init(struct ieee80211_local *local);
void sta_info_stop(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);
/** /**
* sta_info_flush - flush matching STA entries from the STA table * sta_info_flush - flush matching STA entries from the STA table
...@@ -628,15 +613,7 @@ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata); ...@@ -628,15 +613,7 @@ void sta_info_flush_cleanup(struct ieee80211_sub_if_data *sdata);
* *
* @sdata: sdata to remove all stations from * @sdata: sdata to remove all stations from
*/ */
static inline int sta_info_flush(struct ieee80211_sub_if_data *sdata) 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;
}
void sta_set_rate_info_tx(struct sta_info *sta, void sta_set_rate_info_tx(struct sta_info *sta,
const struct ieee80211_tx_rate *rate, const struct ieee80211_tx_rate *rate,
...@@ -651,6 +628,4 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta); ...@@ -651,6 +628,4 @@ void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta); void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta); void ieee80211_sta_ps_deliver_uapsd(struct sta_info *sta);
void ieee80211_cleanup_sdata_stas(struct ieee80211_sub_if_data *sdata);
#endif /* STA_INFO_H */ #endif /* STA_INFO_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment