Skip to content

Commit

Permalink
wifi: mac80211: batch recalc during STA flush
Browse files Browse the repository at this point in the history
When we flush stations, we first take them off the list
and then destroy them one by one. If we do the different
mode recalculations while destroying them, we cause the
following scenario:
 - STA 1 has 80 MHz - min chanctx width is now 80 MHz
 - STA 2 has 80 MHz
 - empty STA list
 - destroy STA 2
 - recalc min chanctx width -> results in 20 MHz as
   the STA list is already empty

This is broken, since as far as the driver is concerned
STA 1 still exists at this point, and this causes issues
at least with iwlwifi.

Fix - and also optimize - this by doing the recalc of
min chanctx width (and also P2P PS) only after all the
stations were removed.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Gregory Greenman <gregory.greenman@intel.com>
Link: https://lore.kernel.org/r/20230604120651.48d262b6b42d.Ia15532657c17535c28ec0c5df263b65f0f80663c@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
  • Loading branch information
Johannes Berg committed Jun 6, 2023
1 parent 10a7ba9 commit 92747f1
Showing 1 changed file with 34 additions and 14 deletions.
48 changes: 34 additions & 14 deletions net/mac80211/sta_info.c
Original file line number Diff line number Diff line change
Expand Up @@ -1274,8 +1274,9 @@ static int __must_check __sta_info_destroy_part1(struct sta_info *sta)
return 0;
}

int sta_info_move_state(struct sta_info *sta,
enum ieee80211_sta_state new_state)
static int _sta_info_move_state(struct sta_info *sta,
enum ieee80211_sta_state new_state,
bool recalc)
{
might_sleep();

Expand Down Expand Up @@ -1333,18 +1334,22 @@ int sta_info_move_state(struct sta_info *sta,
set_bit(WLAN_STA_AUTH, &sta->_flags);
} else if (sta->sta_state == IEEE80211_STA_ASSOC) {
clear_bit(WLAN_STA_ASSOC, &sta->_flags);
ieee80211_recalc_min_chandef(sta->sdata, -1);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
if (recalc) {
ieee80211_recalc_min_chandef(sta->sdata, -1);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
}
}
break;
case IEEE80211_STA_ASSOC:
if (sta->sta_state == IEEE80211_STA_AUTH) {
set_bit(WLAN_STA_ASSOC, &sta->_flags);
sta->assoc_at = ktime_get_boottime_ns();
ieee80211_recalc_min_chandef(sta->sdata, -1);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
if (recalc) {
ieee80211_recalc_min_chandef(sta->sdata, -1);
if (!sta->sta.support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sta->sdata);
}
} else if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
ieee80211_vif_dec_num_mcast(sta->sdata);
clear_bit(WLAN_STA_AUTHORIZED, &sta->_flags);
Expand Down Expand Up @@ -1373,7 +1378,13 @@ int sta_info_move_state(struct sta_info *sta,
return 0;
}

static void __sta_info_destroy_part2(struct sta_info *sta)
int sta_info_move_state(struct sta_info *sta,
enum ieee80211_sta_state new_state)
{
return _sta_info_move_state(sta, new_state, true);
}

static void __sta_info_destroy_part2(struct sta_info *sta, bool recalc)
{
struct ieee80211_local *local = sta->local;
struct ieee80211_sub_if_data *sdata = sta->sdata;
Expand All @@ -1389,7 +1400,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
lockdep_assert_held(&local->sta_mtx);

if (sta->sta_state == IEEE80211_STA_AUTHORIZED) {
ret = sta_info_move_state(sta, IEEE80211_STA_ASSOC);
ret = _sta_info_move_state(sta, IEEE80211_STA_ASSOC, recalc);
WARN_ON_ONCE(ret);
}

Expand Down Expand Up @@ -1417,7 +1428,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)
local->sta_generation++;

while (sta->sta_state > IEEE80211_STA_NONE) {
ret = sta_info_move_state(sta, sta->sta_state - 1);
ret = _sta_info_move_state(sta, sta->sta_state - 1, recalc);
if (ret) {
WARN_ON_ONCE(1);
break;
Expand Down Expand Up @@ -1454,7 +1465,7 @@ int __must_check __sta_info_destroy(struct sta_info *sta)

synchronize_net();

__sta_info_destroy_part2(sta);
__sta_info_destroy_part2(sta, true);

return 0;
}
Expand Down Expand Up @@ -1561,9 +1572,18 @@ int __sta_info_flush(struct ieee80211_sub_if_data *sdata, bool vlans)
}

if (!list_empty(&free_list)) {
bool support_p2p_ps = true;

synchronize_net();
list_for_each_entry_safe(sta, tmp, &free_list, free_list)
__sta_info_destroy_part2(sta);
list_for_each_entry_safe(sta, tmp, &free_list, free_list) {
if (!sta->sta.support_p2p_ps)
support_p2p_ps = false;
__sta_info_destroy_part2(sta, false);
}

ieee80211_recalc_min_chandef(sdata, -1);
if (!support_p2p_ps)
ieee80211_recalc_p2p_go_ps_allowed(sdata);
}
mutex_unlock(&local->sta_mtx);

Expand Down

0 comments on commit 92747f1

Please sign in to comment.