| From 12a738093165fe89aa9d093c80dcac8ecefce471 Mon Sep 17 00:00:00 2001 |
| From: Aditya Kumar Singh <quic_adisi@quicinc.com> |
| Date: Wed, 5 Oct 2022 15:24:30 +0530 |
| Subject: [PATCH] BACKPORT: FROMGIT: wifi: ath11k: fix firmware assert during |
| bandwidth change for peer sta |
| |
| Currently, ath11k sends peer assoc command for each peer to |
| firmware when bandwidth changes. Peer assoc command is a |
| bulky command and if many clients are connected, this could |
| lead to firmware buffer getting overflowed leading to a firmware |
| assert. |
| |
| However, during bandwidth change, only phymode and bandwidth |
| also can be updated by WMI set peer param command. This makes |
| the overall command light when compared to peer assoc and for |
| multi-client cases, firmware buffer overflow also does not |
| occur. |
| |
| Remove sending peer assoc command during sta bandwidth change |
| and instead add sending WMI set peer param command for phymode |
| and bandwidth. |
| |
| Tested-on: QCN9074 hw1.0 PCI WLAN.HK.2.5.0.1-01100-QCAHKSWPL_SILICONZ-1 |
| |
| Fixes: f187fe8e3bc65 ("ath11k: fix firmware crash during channel switch") |
| Signed-off-by: Aditya Kumar Singh <quic_adisi@quicinc.com> |
| Signed-off-by: Kalle Valo <quic_kvalo@quicinc.com> |
| Link: https://lore.kernel.org/r/20221005095430.19890-1-quic_adisi@quicinc.com |
| |
| Conflicts: |
| drivers/net/wireless/ath/ath11k/mac.c |
| sta->deflink.bandwidth was introduced to support MLO. However |
| kernel 5.15 doesn't support MLO. So use the older version |
| sta->bandwidth instead. |
| |
| (cherry picked from commit 3ff51d7416ee1ea2d771051a0ffa1ec8be054768 |
| https://git.kernel.org/pub/scm/linux/kernel/git/kvalo/ath.git ath-next) |
| |
| BUG=b:248457431 |
| TEST=emerge-herobrine chromeos-kernel-5_15; deploy and test basic WiFi |
| functionalities on herobrine device. |
| TEST=suite:wifi_matfunc results are consistent with lab results. |
| |
| Signed-off-by: Jun Yu <junyuu@chromium.org> |
| Change-Id: I66fa456a526b688cd7c91b32fa70064e6ec170ef |
| Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/4024231 |
| Reviewed-by: Abhishek Kumar <kuabhs@chromium.org> |
| --- |
| drivers/net/wireless/ath/ath11k/core.h | 2 + |
| drivers/net/wireless/ath/ath11k/mac.c | 122 +++++++++++++++++-------- |
| 2 files changed, 87 insertions(+), 37 deletions(-) |
| |
| diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h |
| index 2be46f45951fd8e4785269f07bc4cd410c330e71..5aa1fee8c46e84b48d85107f27454e968db4948a 100644 |
| --- a/drivers/net/wireless/ath/ath11k/core.h |
| +++ b/drivers/net/wireless/ath/ath11k/core.h |
| @@ -503,6 +503,8 @@ struct ath11k_sta { |
| u64 ps_start_jiffies; |
| u64 ps_total_duration; |
| bool peer_current_ps_valid; |
| + |
| + u32 bw_prev; |
| }; |
| |
| #define ATH11K_MIN_5G_FREQ 4150 |
| diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c |
| index 2d93b08859b394808df0aee7f8748bc5838174e9..2aca852fe7545d9228fa119fc9039d2e39de5352 100644 |
| --- a/drivers/net/wireless/ath/ath11k/mac.c |
| +++ b/drivers/net/wireless/ath/ath11k/mac.c |
| @@ -4240,10 +4240,11 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) |
| const u8 *ht_mcs_mask; |
| const u16 *vht_mcs_mask; |
| const u16 *he_mcs_mask; |
| - u32 changed, bw, nss, smps; |
| + u32 changed, bw, nss, smps, bw_prev; |
| int err, num_vht_rates, num_he_rates; |
| const struct cfg80211_bitrate_mask *mask; |
| struct peer_assoc_params peer_arg; |
| + enum wmi_phy_mode peer_phymode; |
| |
| arsta = container_of(wk, struct ath11k_sta, update_wk); |
| sta = container_of((void *)arsta, struct ieee80211_sta, drv_priv); |
| @@ -4264,6 +4265,7 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) |
| arsta->changed = 0; |
| |
| bw = arsta->bw; |
| + bw_prev = arsta->bw_prev; |
| nss = arsta->nss; |
| smps = arsta->smps; |
| |
| @@ -4277,26 +4279,57 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) |
| ath11k_mac_max_he_nss(he_mcs_mask))); |
| |
| if (changed & IEEE80211_RC_BW_CHANGED) { |
| - /* Send peer assoc command before set peer bandwidth param to |
| - * avoid the mismatch between the peer phymode and the peer |
| - * bandwidth. |
| - */ |
| - ath11k_peer_assoc_prepare(ar, arvif->vif, sta, &peer_arg, true); |
| - |
| - peer_arg.is_assoc = false; |
| - err = ath11k_wmi_send_peer_assoc_cmd(ar, &peer_arg); |
| - if (err) { |
| - ath11k_warn(ar->ab, "failed to send peer assoc for STA %pM vdev %i: %d\n", |
| - sta->addr, arvif->vdev_id, err); |
| - } else if (wait_for_completion_timeout(&ar->peer_assoc_done, 1 * HZ)) { |
| + /* Get the peer phymode */ |
| + ath11k_peer_assoc_h_phymode(ar, arvif->vif, sta, &peer_arg); |
| + peer_phymode = peer_arg.peer_phymode; |
| + |
| + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac update sta %pM peer bw %d phymode %d\n", |
| + sta->addr, bw, peer_phymode); |
| + |
| + if (bw > bw_prev) { |
| + /* BW is upgraded. In this case we send WMI_PEER_PHYMODE |
| + * followed by WMI_PEER_CHWIDTH |
| + */ |
| + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW upgrade for sta %pM new BW %d, old BW %d\n", |
| + sta->addr, bw, bw_prev); |
| + |
| + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, |
| + WMI_PEER_PHYMODE, peer_phymode); |
| + |
| + if (err) { |
| + ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", |
| + sta->addr, peer_phymode, err); |
| + goto err_rc_bw_changed; |
| + } |
| + |
| err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, |
| WMI_PEER_CHWIDTH, bw); |
| + |
| if (err) |
| ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", |
| sta->addr, bw, err); |
| } else { |
| - ath11k_warn(ar->ab, "failed to get peer assoc conf event for %pM vdev %i\n", |
| - sta->addr, arvif->vdev_id); |
| + /* BW is downgraded. In this case we send WMI_PEER_CHWIDTH |
| + * followed by WMI_PEER_PHYMODE |
| + */ |
| + ath11k_dbg(ar->ab, ATH11K_DBG_MAC, "mac BW downgrade for sta %pM new BW %d,old BW %d\n", |
| + sta->addr, bw, bw_prev); |
| + |
| + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, |
| + WMI_PEER_CHWIDTH, bw); |
| + |
| + if (err) { |
| + ath11k_warn(ar->ab, "failed to update STA %pM peer bw %d: %d\n", |
| + sta->addr, bw, err); |
| + goto err_rc_bw_changed; |
| + } |
| + |
| + err = ath11k_wmi_set_peer_param(ar, sta->addr, arvif->vdev_id, |
| + WMI_PEER_PHYMODE, peer_phymode); |
| + |
| + if (err) |
| + ath11k_warn(ar->ab, "failed to update STA %pM peer phymode %d: %d\n", |
| + sta->addr, peer_phymode, err); |
| } |
| } |
| |
| @@ -4377,6 +4410,7 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk) |
| } |
| } |
| |
| +err_rc_bw_changed: |
| mutex_unlock(&ar->conf_mutex); |
| } |
| |
| @@ -4530,6 +4564,34 @@ static int ath11k_mac_station_add(struct ath11k *ar, |
| return ret; |
| } |
| |
| +static u32 ath11k_mac_ieee80211_sta_bw_to_wmi(struct ath11k *ar, |
| + struct ieee80211_sta *sta) |
| +{ |
| + u32 bw = WMI_PEER_CHWIDTH_20MHZ; |
| + |
| + switch (sta->bandwidth) { |
| + case IEEE80211_STA_RX_BW_20: |
| + bw = WMI_PEER_CHWIDTH_20MHZ; |
| + break; |
| + case IEEE80211_STA_RX_BW_40: |
| + bw = WMI_PEER_CHWIDTH_40MHZ; |
| + break; |
| + case IEEE80211_STA_RX_BW_80: |
| + bw = WMI_PEER_CHWIDTH_80MHZ; |
| + break; |
| + case IEEE80211_STA_RX_BW_160: |
| + bw = WMI_PEER_CHWIDTH_160MHZ; |
| + break; |
| + default: |
| + ath11k_warn(ar->ab, "Invalid bandwidth %d for %pM\n", |
| + sta->bandwidth, sta->addr); |
| + bw = WMI_PEER_CHWIDTH_20MHZ; |
| + break; |
| + } |
| + |
| + return bw; |
| +} |
| + |
| static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, |
| struct ieee80211_vif *vif, |
| struct ieee80211_sta *sta, |
| @@ -4615,6 +4677,12 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw, |
| if (ret) |
| ath11k_warn(ar->ab, "Failed to associate station: %pM\n", |
| sta->addr); |
| + |
| + spin_lock_bh(&ar->data_lock); |
| + /* Set arsta bw and prev bw */ |
| + arsta->bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); |
| + arsta->bw_prev = arsta->bw; |
| + spin_unlock_bh(&ar->data_lock); |
| } else if (old_state == IEEE80211_STA_ASSOC && |
| new_state == IEEE80211_STA_AUTHORIZED) { |
| spin_lock_bh(&ar->ab->base_lock); |
| @@ -4738,28 +4806,8 @@ static void ath11k_mac_op_sta_rc_update(struct ieee80211_hw *hw, |
| spin_lock_bh(&ar->data_lock); |
| |
| if (changed & IEEE80211_RC_BW_CHANGED) { |
| - bw = WMI_PEER_CHWIDTH_20MHZ; |
| - |
| - switch (sta->deflink.bandwidth) { |
| - case IEEE80211_STA_RX_BW_20: |
| - bw = WMI_PEER_CHWIDTH_20MHZ; |
| - break; |
| - case IEEE80211_STA_RX_BW_40: |
| - bw = WMI_PEER_CHWIDTH_40MHZ; |
| - break; |
| - case IEEE80211_STA_RX_BW_80: |
| - bw = WMI_PEER_CHWIDTH_80MHZ; |
| - break; |
| - case IEEE80211_STA_RX_BW_160: |
| - bw = WMI_PEER_CHWIDTH_160MHZ; |
| - break; |
| - default: |
| - ath11k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", |
| - sta->deflink.bandwidth, sta->addr); |
| - bw = WMI_PEER_CHWIDTH_20MHZ; |
| - break; |
| - } |
| - |
| + bw = ath11k_mac_ieee80211_sta_bw_to_wmi(ar, sta); |
| + arsta->bw_prev = arsta->bw; |
| arsta->bw = bw; |
| } |
| |
| -- |
| 2.38.1.584.g0f3c55d4c2-goog |
| |