Skip to content

Commit

Permalink
wifi: ath11k: fix firmware assert during bandwidth change for peer sta
Browse files Browse the repository at this point in the history
[ Upstream commit 3ff51d7 ]

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: f187fe8 ("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
Signed-off-by: Sasha Levin <sashal@kernel.org>
  • Loading branch information
Aditya Kumar Singh authored and gregkh committed Dec 31, 2022
1 parent d161b3d commit edf05f0
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 37 deletions.
2 changes: 2 additions & 0 deletions drivers/net/wireless/ath/ath11k/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,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
Expand Down
122 changes: 85 additions & 37 deletions drivers/net/wireless/ath/ath11k/mac.c
Original file line number Diff line number Diff line change
Expand Up @@ -4215,10 +4215,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);
Expand All @@ -4239,6 +4240,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;

Expand All @@ -4252,26 +4254,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);
}
}

Expand Down Expand Up @@ -4352,6 +4385,7 @@ static void ath11k_sta_rc_update_wk(struct work_struct *wk)
}
}

err_rc_bw_changed:
mutex_unlock(&ar->conf_mutex);
}

Expand Down Expand Up @@ -4505,6 +4539,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->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 for %pM\n",
sta->deflink.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,
Expand Down Expand Up @@ -4590,6 +4652,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);
Expand Down Expand Up @@ -4713,28 +4781,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;
}

Expand Down

0 comments on commit edf05f0

Please sign in to comment.