Skip to content

Commit 93370f2

Browse files
Miriam-Racheljmberg-intel
authored andcommitted
wifi: mac80211: handle WLAN_HT_ACTION_NOTIFY_CHANWIDTH async
If this action frame, with the value of IEEE80211_HT_CHANWIDTH_ANY, arrives right after a beacon that changed the operational bandwidth from 20 MHz to 40 MHz, then updating the rate control bandwidth to 40 can race with updating the chanctx width (that happens in the beacon proccesing) back to 40 MHz: cpu0 cpu1 ieee80211_rx_mgmt_beacon ieee80211_config_bw ieee80211_link_change_chanreq (*)ieee80211_link_update_chanreq ieee80211_rx_h_action (**)ieee80211_sta_cur_vht_bw (***) ieee80211_recalc_chanctx_chantype in (**), the maximum between the capability width and the bss width is returned. But the bss width was just updated to 40 in (*), so the action frame handling code will increase the width of the rate control before the chanctx was increased (in ***), leading to a FW error (at least in iwlwifi driver. But this is wrong regardless). Fix this by simply handling the action frame async, so it won't race with the beacon proccessing. Closes: https://bugzilla.kernel.org/show_bug.cgi?id=218632 Reviewed-by: Johannes Berg <[email protected]> Signed-off-by: Miri Korenblit <[email protected]> Link: https://patch.msgid.link/20250709233537.bb9dc6f36c35.I39782d6077424e075974c3bee4277761494a1527@changeid Signed-off-by: Johannes Berg <[email protected]>
1 parent 6ee152b commit 93370f2

File tree

4 files changed

+80
-30
lines changed

4 files changed

+80
-30
lines changed

net/mac80211/ht.c

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
* Copyright 2007, Michael Wu <[email protected]>
1010
* Copyright 2007-2010, Intel Corporation
1111
* Copyright 2017 Intel Deutschland GmbH
12-
* Copyright(c) 2020-2024 Intel Corporation
12+
* Copyright(c) 2020-2025 Intel Corporation
1313
*/
1414

1515
#include <linux/ieee80211.h>
@@ -603,3 +603,41 @@ void ieee80211_request_smps(struct ieee80211_vif *vif, unsigned int link_id,
603603
}
604604
/* this might change ... don't want non-open drivers using it */
605605
EXPORT_SYMBOL_GPL(ieee80211_request_smps);
606+
607+
void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
608+
struct ieee80211_sub_if_data *sdata,
609+
struct sta_info *sta,
610+
struct link_sta_info *link_sta,
611+
u8 chanwidth, enum nl80211_band band)
612+
{
613+
enum ieee80211_sta_rx_bandwidth max_bw, new_bw;
614+
struct ieee80211_supported_band *sband;
615+
struct sta_opmode_info sta_opmode = {};
616+
617+
lockdep_assert_wiphy(local->hw.wiphy);
618+
619+
if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
620+
max_bw = IEEE80211_STA_RX_BW_20;
621+
else
622+
max_bw = ieee80211_sta_cap_rx_bw(link_sta);
623+
624+
/* set cur_max_bandwidth and recalc sta bw */
625+
link_sta->cur_max_bandwidth = max_bw;
626+
new_bw = ieee80211_sta_cur_vht_bw(link_sta);
627+
628+
if (link_sta->pub->bandwidth == new_bw)
629+
return;
630+
631+
link_sta->pub->bandwidth = new_bw;
632+
sband = local->hw.wiphy->bands[band];
633+
sta_opmode.bw =
634+
ieee80211_sta_rx_bw_to_chan_width(link_sta);
635+
sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED;
636+
637+
rate_control_rate_update(local, sband, link_sta,
638+
IEEE80211_RC_BW_CHANGED);
639+
cfg80211_sta_opmode_change_notify(sdata->dev,
640+
sta->addr,
641+
&sta_opmode,
642+
GFP_KERNEL);
643+
}

net/mac80211/ieee80211_i.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2204,6 +2204,12 @@ u8 ieee80211_mcs_to_chains(const struct ieee80211_mcs_info *mcs);
22042204
enum nl80211_smps_mode
22052205
ieee80211_smps_mode_to_smps_mode(enum ieee80211_smps_mode smps);
22062206

2207+
void ieee80211_ht_handle_chanwidth_notif(struct ieee80211_local *local,
2208+
struct ieee80211_sub_if_data *sdata,
2209+
struct sta_info *sta,
2210+
struct link_sta_info *link_sta,
2211+
u8 chanwidth, enum nl80211_band band);
2212+
22072213
/* VHT */
22082214
void
22092215
ieee80211_vht_cap_ie_to_sta_vht_cap(struct ieee80211_sub_if_data *sdata,

net/mac80211/iface.c

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1556,6 +1556,35 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
15561556
break;
15571557
}
15581558
}
1559+
} else if (ieee80211_is_action(mgmt->frame_control) &&
1560+
mgmt->u.action.category == WLAN_CATEGORY_HT) {
1561+
switch (mgmt->u.action.u.ht_smps.action) {
1562+
case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
1563+
u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
1564+
struct ieee80211_rx_status *status;
1565+
struct link_sta_info *link_sta;
1566+
struct sta_info *sta;
1567+
1568+
sta = sta_info_get_bss(sdata, mgmt->sa);
1569+
if (!sta)
1570+
break;
1571+
1572+
status = IEEE80211_SKB_RXCB(skb);
1573+
if (!status->link_valid)
1574+
link_sta = &sta->deflink;
1575+
else
1576+
link_sta = rcu_dereference_protected(sta->link[status->link_id],
1577+
lockdep_is_held(&local->hw.wiphy->mtx));
1578+
if (link_sta)
1579+
ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
1580+
link_sta, chanwidth,
1581+
status->band);
1582+
break;
1583+
}
1584+
default:
1585+
WARN_ON(1);
1586+
break;
1587+
}
15591588
} else if (ieee80211_is_action(mgmt->frame_control) &&
15601589
mgmt->u.action.category == WLAN_CATEGORY_VHT) {
15611590
switch (mgmt->u.action.u.vht_group_notif.action_code) {

net/mac80211/rx.c

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3580,41 +3580,18 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
35803580
goto handled;
35813581
}
35823582
case WLAN_HT_ACTION_NOTIFY_CHANWIDTH: {
3583-
struct ieee80211_supported_band *sband;
35843583
u8 chanwidth = mgmt->u.action.u.ht_notify_cw.chanwidth;
3585-
enum ieee80211_sta_rx_bandwidth max_bw, new_bw;
3586-
struct sta_opmode_info sta_opmode = {};
3584+
3585+
if (chanwidth != IEEE80211_HT_CHANWIDTH_20MHZ &&
3586+
chanwidth != IEEE80211_HT_CHANWIDTH_ANY)
3587+
goto invalid;
35873588

35883589
/* If it doesn't support 40 MHz it can't change ... */
35893590
if (!(rx->link_sta->pub->ht_cap.cap &
3590-
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
3591-
goto handled;
3592-
3593-
if (chanwidth == IEEE80211_HT_CHANWIDTH_20MHZ)
3594-
max_bw = IEEE80211_STA_RX_BW_20;
3595-
else
3596-
max_bw = ieee80211_sta_cap_rx_bw(rx->link_sta);
3597-
3598-
/* set cur_max_bandwidth and recalc sta bw */
3599-
rx->link_sta->cur_max_bandwidth = max_bw;
3600-
new_bw = ieee80211_sta_cur_vht_bw(rx->link_sta);
3601-
3602-
if (rx->link_sta->pub->bandwidth == new_bw)
3591+
IEEE80211_HT_CAP_SUP_WIDTH_20_40))
36033592
goto handled;
36043593

3605-
rx->link_sta->pub->bandwidth = new_bw;
3606-
sband = rx->local->hw.wiphy->bands[status->band];
3607-
sta_opmode.bw =
3608-
ieee80211_sta_rx_bw_to_chan_width(rx->link_sta);
3609-
sta_opmode.changed = STA_OPMODE_MAX_BW_CHANGED;
3610-
3611-
rate_control_rate_update(local, sband, rx->link_sta,
3612-
IEEE80211_RC_BW_CHANGED);
3613-
cfg80211_sta_opmode_change_notify(sdata->dev,
3614-
rx->sta->addr,
3615-
&sta_opmode,
3616-
GFP_ATOMIC);
3617-
goto handled;
3594+
goto queue;
36183595
}
36193596
default:
36203597
goto invalid;

0 commit comments

Comments
 (0)