From: Johannes Berg <johannes.berg@xxxxxxxxx> There's no need to not call __cleanup_single_sta() here, as it won't do anything if the relevant setup hasn't been done. As such, move all the code into sta_info_free() and adjust it a bit to not skip all the rest when powersave isn't set up quite right. Signed-off-by: Johannes Berg <johannes.berg@xxxxxxxxx> --- net/mac80211/sta_info.c | 172 ++++++++++++++++++---------------------- 1 file changed, 77 insertions(+), 95 deletions(-) diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c index f7070674d1c8..22af230b79d8 100644 --- a/net/mac80211/sta_info.c +++ b/net/mac80211/sta_info.c @@ -81,99 +81,6 @@ static int sta_info_hash_del(struct ieee80211_local *local, sta_rht_params); } -static void __cleanup_single_sta(struct sta_info *sta) -{ - int ac, i; - struct tid_ampdu_tx *tid_tx; - struct ieee80211_sub_if_data *sdata = sta->sdata; - struct ieee80211_local *local = sdata->local; - struct ps_data *ps; - - if (test_sta_flag(sta, WLAN_STA_PS_STA) || - test_sta_flag(sta, WLAN_STA_PS_DRIVER) || - test_sta_flag(sta, WLAN_STA_PS_DELIVER)) { - if (sta->sdata->vif.type == NL80211_IFTYPE_AP || - sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) - ps = &sdata->bss->ps; - else if (ieee80211_vif_is_mesh(&sdata->vif)) - ps = &sdata->u.mesh.ps; - else - return; - - clear_sta_flag(sta, WLAN_STA_PS_STA); - clear_sta_flag(sta, WLAN_STA_PS_DRIVER); - clear_sta_flag(sta, WLAN_STA_PS_DELIVER); - - atomic_dec(&ps->num_sta_ps); - } - - if (sta->sta.txq[0]) { - for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { - struct txq_info *txqi; - - if (!sta->sta.txq[i]) - continue; - - txqi = to_txq_info(sta->sta.txq[i]); - - ieee80211_txq_purge(local, txqi); - } - } - - for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { - local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); - ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); - ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]); - } - - if (ieee80211_vif_is_mesh(&sdata->vif)) - mesh_sta_cleanup(sta); - - cancel_work_sync(&sta->drv_deliver_wk); - - /* - * Destroy aggregation state here. It would be nice to wait for the - * driver to finish aggregation stop and then clean up, but for now - * drivers have to handle aggregation stop being requested, followed - * directly by station destruction. - */ - for (i = 0; i < IEEE80211_NUM_TIDS; i++) { - kfree(sta->ampdu_mlme.tid_start_tx[i]); - tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); - if (!tid_tx) - continue; - ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending); - kfree(tid_tx); - } - - /* - * If we had used sta_info_pre_move_state() then we might not - * have gone through the state transitions down again, so do - * it here now (and warn if it's inserted). - * - * This will clear state such as fast TX/RX that may have been - * allocated during state transitions. - */ - while (sta->sta_state > IEEE80211_STA_NONE) { - int ret; - - WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); - - ret = sta_info_move_state(sta, sta->sta_state - 1); - if (WARN_ONCE(ret, "sta_info_move_state() returned %d\n", ret)) - break; - } -} - -static void cleanup_single_sta(struct sta_info *sta) -{ - struct ieee80211_sub_if_data *sdata = sta->sdata; - struct ieee80211_local *local = sdata->local; - - __cleanup_single_sta(sta); - sta_info_free(local, sta); -} - struct rhlist_head *sta_info_hash_lookup(struct ieee80211_local *local, const u8 *addr) { @@ -276,6 +183,81 @@ struct sta_info *sta_info_get_by_idx(struct ieee80211_sub_if_data *sdata, */ void sta_info_free(struct ieee80211_local *local, struct sta_info *sta) { + struct ieee80211_sub_if_data *sdata = sta->sdata; + struct tid_ampdu_tx *tid_tx; + int ac, i; + + if (test_sta_flag(sta, WLAN_STA_PS_STA) || + test_sta_flag(sta, WLAN_STA_PS_DRIVER) || + test_sta_flag(sta, WLAN_STA_PS_DELIVER)) { + if (sta->sdata->vif.type == NL80211_IFTYPE_AP || + sta->sdata->vif.type == NL80211_IFTYPE_AP_VLAN) + atomic_dec(&sdata->bss->ps.num_sta_ps); + else if (ieee80211_vif_is_mesh(&sdata->vif)) + atomic_dec(&sdata->u.mesh.ps.num_sta_ps); + + clear_sta_flag(sta, WLAN_STA_PS_STA); + clear_sta_flag(sta, WLAN_STA_PS_DRIVER); + clear_sta_flag(sta, WLAN_STA_PS_DELIVER); + } + + if (sta->sta.txq[0]) { + for (i = 0; i < ARRAY_SIZE(sta->sta.txq); i++) { + struct txq_info *txqi; + + if (!sta->sta.txq[i]) + continue; + + txqi = to_txq_info(sta->sta.txq[i]); + + ieee80211_txq_purge(local, txqi); + } + } + + for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { + local->total_ps_buffered -= skb_queue_len(&sta->ps_tx_buf[ac]); + ieee80211_purge_tx_queue(&local->hw, &sta->ps_tx_buf[ac]); + ieee80211_purge_tx_queue(&local->hw, &sta->tx_filtered[ac]); + } + + if (ieee80211_vif_is_mesh(&sdata->vif)) + mesh_sta_cleanup(sta); + + cancel_work_sync(&sta->drv_deliver_wk); + + /* + * Destroy aggregation state here. It would be nice to wait for the + * driver to finish aggregation stop and then clean up, but for now + * drivers have to handle aggregation stop being requested, followed + * directly by station destruction. + */ + for (i = 0; i < IEEE80211_NUM_TIDS; i++) { + kfree(sta->ampdu_mlme.tid_start_tx[i]); + tid_tx = rcu_dereference_raw(sta->ampdu_mlme.tid_tx[i]); + if (!tid_tx) + continue; + ieee80211_purge_tx_queue(&local->hw, &tid_tx->pending); + kfree(tid_tx); + } + + /* + * If we had used sta_info_pre_move_state() then we might not + * have gone through the state transitions down again, so do + * it here now (and warn if it's inserted). + * + * This will clear state such as fast TX/RX that may have been + * allocated during state transitions. + */ + while (sta->sta_state > IEEE80211_STA_NONE) { + int ret; + + WARN_ON_ONCE(test_sta_flag(sta, WLAN_STA_INSERTED)); + + ret = sta_info_move_state(sta, sta->sta_state - 1); + if (WARN_ONCE(ret, "sta_info_move_state() returned %d\n", ret)) + break; + } + if (sta->rate_ctrl) rate_control_free_sta(sta); @@ -705,7 +687,7 @@ static int sta_info_insert_finish(struct sta_info *sta) __acquires(RCU) out_drop_sta: local->num_sta--; synchronize_net(); - cleanup_single_sta(sta); + sta_info_free(local, sta); out_err: mutex_unlock(&local->sta_mtx); kfree(sinfo); @@ -1102,7 +1084,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta) ieee80211_sta_debugfs_remove(sta); - cleanup_single_sta(sta); + sta_info_free(local, sta); } int __must_check __sta_info_destroy(struct sta_info *sta) -- 2.26.2