* [PATCH v2] wifi: ath12k: fix survey indexing across bands
@ 2026-07-02 10:50 Matthew Leach
2026-07-02 18:06 ` Rameshkumar Sundaram
2026-07-02 21:14 ` Nicolas Escande
0 siblings, 2 replies; 4+ messages in thread
From: Matthew Leach @ 2026-07-02 10:50 UTC (permalink / raw)
To: Jeff Johnson, Vasanthakumar Thiagarajan, Baochen Qiang,
Ramya Gnanasekar, Karthikeyan Periyasamy
Cc: Kalle Valo, Pradeep Kumar Chitrapu, P Praneesh, Sriram R,
linux-wireless, ath12k, linux-kernel, kernel,
Rameshkumar Sundaram, Matthew Leach, Rameshkumar Sundaram,
Jeff Johnson
When running 'iw dev wlan0 survey dump' the values for the channel busy
time have the same sequence across bands. This is caused by indexing
into the ath12k survey array using a band-local index rather than the
global index passed by mac80211. This results in surveys for 5 GHz and 6
GHz channels returning values from 2.4 GHz slots, making the survey
unusable on those bands. Further, there are redundant survey slots for
multi-radio/single-phy instances.
Fix by moving the survey data into ath12k_hw so multiple radios under a
single wiphy share one table, and index into it using the global
mac80211 index. A new spinlock in ath12k_hw serialises access to the
survey array, which is now shared across all radios under a single hw.
Band busy-times Before this fix:
2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
After this fix, times are independent:
2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
---
Changes in v2:
- Move survey[] from ath12k to ath12k_hw so multi-radio single-wiphy
setups share one global table (suggested by Rameshkumar Sundaram).
- Drop the ar->mac.sbands[] filter in freq_to_idx() so the WMI event
handlers use the same global index
- Add ah->survey_lock to serialise access to the shared survey table
- Update Fixes: tag to the correct commit
- Link to v1: https://patch.msgid.link/20260617-ath12-survey-band-fix-v1-1-e7d9555bb478@collabora.com
To: Jeff Johnson <jjohnson@kernel.org>
To: Sriram R <quic_srirrama@quicinc.com>
To: Kalle Valo <kvalo@kernel.org>
To: Rameshkumar Sundaram <quic_ramess@quicinc.com>
Cc: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
Cc: linux-wireless@vger.kernel.org
Cc: ath12k@lists.infradead.org
Cc: linux-kernel@vger.kernel.org
---
drivers/net/wireless/ath/ath12k/core.h | 6 ++++-
drivers/net/wireless/ath/ath12k/mac.c | 33 ++++++++++++++-------------
drivers/net/wireless/ath/ath12k/wmi.c | 41 ++++++++++++++++++----------------
3 files changed, 45 insertions(+), 35 deletions(-)
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 8be435535a4e..6ce2f7b3fa50 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -712,7 +712,6 @@ struct ath12k {
* avoid reporting garbage data.
*/
bool ch_info_can_report_survey;
- struct survey_info survey[ATH12K_NUM_CHANS];
struct completion bss_survey_done;
struct work_struct regd_update_work;
@@ -774,6 +773,11 @@ struct ath12k_hw {
*/
struct mutex hw_mutex;
enum ath12k_hw_state state;
+
+ /* protects survey[] shared across radios of this hw. */
+ spinlock_t survey_lock;
+ struct survey_info survey[ATH12K_NUM_CHANS];
+
bool regd_updated;
bool use_6ghz_regd;
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 2cff9485c95a..daf9bc8722df 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -13348,52 +13348,54 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar,
int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
struct survey_info *survey)
{
+ struct ath12k_hw *ah = hw->priv;
struct ath12k *ar;
struct ieee80211_supported_band *sband;
- struct survey_info *ar_survey;
+ struct survey_info *ah_survey;
+ int sband_idx = idx;
lockdep_assert_wiphy(hw->wiphy);
- if (idx >= ATH12K_NUM_CHANS)
+ if (sband_idx >= ATH12K_NUM_CHANS)
return -ENOENT;
sband = hw->wiphy->bands[NL80211_BAND_2GHZ];
- if (sband && idx >= sband->n_channels) {
- idx -= sband->n_channels;
+ if (sband && sband_idx >= sband->n_channels) {
+ sband_idx -= sband->n_channels;
sband = NULL;
}
if (!sband)
sband = hw->wiphy->bands[NL80211_BAND_5GHZ];
- if (sband && idx >= sband->n_channels) {
- idx -= sband->n_channels;
+ if (sband && sband_idx >= sband->n_channels) {
+ sband_idx -= sband->n_channels;
sband = NULL;
}
if (!sband)
sband = hw->wiphy->bands[NL80211_BAND_6GHZ];
- if (!sband || idx >= sband->n_channels)
+ if (!sband || sband_idx >= sband->n_channels)
return -ENOENT;
- ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[idx]);
+ ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[sband_idx]);
if (!ar) {
- if (sband->channels[idx].flags & IEEE80211_CHAN_DISABLED) {
+ if (sband->channels[sband_idx].flags & IEEE80211_CHAN_DISABLED) {
memset(survey, 0, sizeof(*survey));
return 0;
}
return -ENOENT;
}
- ar_survey = &ar->survey[idx];
+ ah_survey = &ah->survey[idx];
- ath12k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
+ ath12k_mac_update_bss_chan_survey(ar, &sband->channels[sband_idx]);
- spin_lock_bh(&ar->data_lock);
- memcpy(survey, ar_survey, sizeof(*survey));
- spin_unlock_bh(&ar->data_lock);
+ scoped_guard(spinlock_bh, &ah->survey_lock) {
+ memcpy(survey, ah_survey, sizeof(*survey));
+ }
- survey->channel = &sband->channels[idx];
+ survey->channel = &sband->channels[sband_idx];
if (ar->rx_channel == survey->channel)
survey->filled |= SURVEY_INFO_IN_USE;
@@ -15055,6 +15057,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
mutex_init(&ah->hw_mutex);
+ spin_lock_init(&ah->survey_lock);
spin_lock_init(&ah->dp_hw.peer_lock);
INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index b5e904a55aea..aa70d2a61d38 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6617,16 +6617,12 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
return 0;
}
-static int freq_to_idx(struct ath12k *ar, int freq)
+static int freq_to_idx(struct ieee80211_hw *hw, int freq)
{
struct ieee80211_supported_band *sband;
- struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
int band, ch, idx = 0;
for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
- if (!ar->mac.sbands[band].channels)
- continue;
-
sband = hw->wiphy->bands[band];
if (!sband)
continue;
@@ -7507,6 +7503,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
{
struct wmi_chan_info_event ch_info_ev = {};
struct ath12k *ar;
+ struct ath12k_hw *ah;
struct survey_info *survey;
int idx;
/* HW channel counters frequency value in hertz */
@@ -7538,6 +7535,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
return;
}
spin_lock_bh(&ar->data_lock);
+ ah = ath12k_ar_to_ah(ar);
switch (ar->scan.state) {
case ATH12K_SCAN_IDLE:
@@ -7549,8 +7547,8 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
break;
}
- idx = freq_to_idx(ar, le32_to_cpu(ch_info_ev.freq));
- if (idx >= ARRAY_SIZE(ar->survey)) {
+ idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(ch_info_ev.freq));
+ if (idx >= ARRAY_SIZE(ah->survey)) {
ath12k_warn(ab, "chan info: invalid frequency %d (idx %d out of bounds)\n",
ch_info_ev.freq, idx);
goto exit;
@@ -7563,14 +7561,16 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
cc_freq_hz = (le32_to_cpu(ch_info_ev.mac_clk_mhz) * 1000);
if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) {
- survey = &ar->survey[idx];
- memset(survey, 0, sizeof(*survey));
- survey->noise = le32_to_cpu(ch_info_ev.noise_floor);
- survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
+ scoped_guard(spinlock_bh, &ah->survey_lock) {
+ survey = &ah->survey[idx];
+ memset(survey, 0, sizeof(*survey));
+ survey->noise = le32_to_cpu(ch_info_ev.noise_floor);
+ survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
SURVEY_INFO_TIME_BUSY;
- survey->time = div_u64(le32_to_cpu(ch_info_ev.cycle_count), cc_freq_hz);
- survey->time_busy = div_u64(le32_to_cpu(ch_info_ev.rx_clear_count),
- cc_freq_hz);
+ survey->time = div_u64(le32_to_cpu(ch_info_ev.cycle_count), cc_freq_hz);
+ survey->time_busy = div_u64(le32_to_cpu(ch_info_ev.rx_clear_count),
+ cc_freq_hz);
+ }
}
exit:
spin_unlock_bh(&ar->data_lock);
@@ -7583,6 +7583,7 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
struct wmi_pdev_bss_chan_info_event bss_ch_info_ev = {};
struct survey_info *survey;
struct ath12k *ar;
+ struct ath12k_hw *ah;
u32 cc_freq_hz = ab->cc_freq_hz;
u64 busy, total, tx, rx, rx_bss;
int idx;
@@ -7623,15 +7624,18 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
return;
}
- spin_lock_bh(&ar->data_lock);
- idx = freq_to_idx(ar, le32_to_cpu(bss_ch_info_ev.freq));
- if (idx >= ARRAY_SIZE(ar->survey)) {
+ ah = ath12k_ar_to_ah(ar);
+
+ guard(spinlock_bh)(&ah->survey_lock);
+
+ idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(bss_ch_info_ev.freq));
+ if (idx >= ARRAY_SIZE(ah->survey)) {
ath12k_warn(ab, "bss chan info: invalid frequency %d (idx %d out of bounds)\n",
bss_ch_info_ev.freq, idx);
goto exit;
}
- survey = &ar->survey[idx];
+ survey = &ah->survey[idx];
survey->noise = le32_to_cpu(bss_ch_info_ev.noise_floor);
survey->time = div_u64(total, cc_freq_hz);
@@ -7644,7 +7648,6 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
SURVEY_INFO_TIME_RX |
SURVEY_INFO_TIME_TX);
exit:
- spin_unlock_bh(&ar->data_lock);
complete(&ar->bss_survey_done);
rcu_read_unlock();
---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260617-ath12-survey-band-fix-4b5e78579379
Best regards,
--
Matt
^ permalink raw reply related [flat|nested] 4+ messages in thread* Re: [PATCH v2] wifi: ath12k: fix survey indexing across bands
2026-07-02 10:50 [PATCH v2] wifi: ath12k: fix survey indexing across bands Matthew Leach
@ 2026-07-02 18:06 ` Rameshkumar Sundaram
2026-07-02 21:14 ` Nicolas Escande
1 sibling, 0 replies; 4+ messages in thread
From: Rameshkumar Sundaram @ 2026-07-02 18:06 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson, Vasanthakumar Thiagarajan,
Baochen Qiang
Cc: Kalle Valo, Pradeep Kumar Chitrapu, linux-wireless, ath12k,
linux-kernel, kernel, Rameshkumar Sundaram, Jeff Johnson
On 7/2/2026 4:20 PM, Matthew Leach wrote:
> When running 'iw dev wlan0 survey dump' the values for the channel busy
> time have the same sequence across bands. This is caused by indexing
> into the ath12k survey array using a band-local index rather than the
> global index passed by mac80211. This results in surveys for 5 GHz and 6
> GHz channels returning values from 2.4 GHz slots, making the survey
> unusable on those bands. Further, there are redundant survey slots for
> multi-radio/single-phy instances.
>
> Fix by moving the survey data into ath12k_hw so multiple radios under a
> single wiphy share one table, and index into it using the global
> mac80211 index. A new spinlock in ath12k_hw serialises access to the
> survey array, which is now shared across all radios under a single hw.
>
> Band busy-times Before this fix:
>
> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>
> After this fix, times are independent:
>
> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>
> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>
> Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
> Changes in v2:
> - Move survey[] from ath12k to ath12k_hw so multi-radio single-wiphy
> setups share one global table (suggested by Rameshkumar Sundaram).
> - Drop the ar->mac.sbands[] filter in freq_to_idx() so the WMI event
> handlers use the same global index
> - Add ah->survey_lock to serialise access to the shared survey table
> - Update Fixes: tag to the correct commit
> - Link to v1: https://patch.msgid.link/20260617-ath12-survey-band-fix-v1-1-e7d9555bb478@collabora.com
>
> To: Jeff Johnson <jjohnson@kernel.org>
> To: Sriram R <quic_srirrama@quicinc.com>
> To: Kalle Valo <kvalo@kernel.org>
> To: Rameshkumar Sundaram <quic_ramess@quicinc.com>
> Cc: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
> Cc: linux-wireless@vger.kernel.org
> Cc: ath12k@lists.infradead.org
> Cc: linux-kernel@vger.kernel.org
> ---
> drivers/net/wireless/ath/ath12k/core.h | 6 ++++-
> drivers/net/wireless/ath/ath12k/mac.c | 33 ++++++++++++++-------------
> drivers/net/wireless/ath/ath12k/wmi.c | 41 ++++++++++++++++++----------------
> 3 files changed, 45 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
> index 8be435535a4e..6ce2f7b3fa50 100644
> --- a/drivers/net/wireless/ath/ath12k/core.h
> +++ b/drivers/net/wireless/ath/ath12k/core.h
> @@ -712,7 +712,6 @@ struct ath12k {
> * avoid reporting garbage data.
> */
> bool ch_info_can_report_survey;
> - struct survey_info survey[ATH12K_NUM_CHANS];
> struct completion bss_survey_done;
>
> struct work_struct regd_update_work;
> @@ -774,6 +773,11 @@ struct ath12k_hw {
> */
> struct mutex hw_mutex;
> enum ath12k_hw_state state;
> +
> + /* protects survey[] shared across radios of this hw. */
> + spinlock_t survey_lock;
> + struct survey_info survey[ATH12K_NUM_CHANS];
> +
> bool regd_updated;
> bool use_6ghz_regd;
>
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 2cff9485c95a..daf9bc8722df 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -13348,52 +13348,54 @@ ath12k_mac_update_bss_chan_survey(struct ath12k *ar,
> int ath12k_mac_op_get_survey(struct ieee80211_hw *hw, int idx,
> struct survey_info *survey)
> {
> + struct ath12k_hw *ah = hw->priv;
> struct ath12k *ar;
> struct ieee80211_supported_band *sband;
> - struct survey_info *ar_survey;
> + struct survey_info *ah_survey;
> + int sband_idx = idx;
>
> lockdep_assert_wiphy(hw->wiphy);
>
> - if (idx >= ATH12K_NUM_CHANS)
> + if (sband_idx >= ATH12K_NUM_CHANS)
> return -ENOENT;
>
> sband = hw->wiphy->bands[NL80211_BAND_2GHZ];
> - if (sband && idx >= sband->n_channels) {
> - idx -= sband->n_channels;
> + if (sband && sband_idx >= sband->n_channels) {
> + sband_idx -= sband->n_channels;
> sband = NULL;
> }
>
> if (!sband)
> sband = hw->wiphy->bands[NL80211_BAND_5GHZ];
> - if (sband && idx >= sband->n_channels) {
> - idx -= sband->n_channels;
> + if (sband && sband_idx >= sband->n_channels) {
> + sband_idx -= sband->n_channels;
> sband = NULL;
> }
>
> if (!sband)
> sband = hw->wiphy->bands[NL80211_BAND_6GHZ];
>
> - if (!sband || idx >= sband->n_channels)
> + if (!sband || sband_idx >= sband->n_channels)
> return -ENOENT;
>
> - ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[idx]);
> + ar = ath12k_mac_get_ar_by_chan(hw, &sband->channels[sband_idx]);
> if (!ar) {
> - if (sband->channels[idx].flags & IEEE80211_CHAN_DISABLED) {
> + if (sband->channels[sband_idx].flags & IEEE80211_CHAN_DISABLED) {
> memset(survey, 0, sizeof(*survey));
> return 0;
> }
> return -ENOENT;
> }
>
> - ar_survey = &ar->survey[idx];
> + ah_survey = &ah->survey[idx];
>
> - ath12k_mac_update_bss_chan_survey(ar, &sband->channels[idx]);
> + ath12k_mac_update_bss_chan_survey(ar, &sband->channels[sband_idx]);
>
> - spin_lock_bh(&ar->data_lock);
> - memcpy(survey, ar_survey, sizeof(*survey));
> - spin_unlock_bh(&ar->data_lock);
> + scoped_guard(spinlock_bh, &ah->survey_lock) {
> + memcpy(survey, ah_survey, sizeof(*survey));
> + }
>
> - survey->channel = &sband->channels[idx];
> + survey->channel = &sband->channels[sband_idx];
>
> if (ar->rx_channel == survey->channel)
> survey->filled |= SURVEY_INFO_IN_USE;
> @@ -15055,6 +15057,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
>
> mutex_init(&ah->hw_mutex);
>
> + spin_lock_init(&ah->survey_lock);
> spin_lock_init(&ah->dp_hw.peer_lock);
> INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
>
> diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
> index b5e904a55aea..aa70d2a61d38 100644
> --- a/drivers/net/wireless/ath/ath12k/wmi.c
> +++ b/drivers/net/wireless/ath/ath12k/wmi.c
> @@ -6617,16 +6617,12 @@ static int ath12k_pull_roam_ev(struct ath12k_base *ab, struct sk_buff *skb,
> return 0;
> }
>
> -static int freq_to_idx(struct ath12k *ar, int freq)
> +static int freq_to_idx(struct ieee80211_hw *hw, int freq)
> {
> struct ieee80211_supported_band *sband;
> - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar);
> int band, ch, idx = 0;
>
> for (band = NL80211_BAND_2GHZ; band < NUM_NL80211_BANDS; band++) {
> - if (!ar->mac.sbands[band].channels)
> - continue;
> -
> sband = hw->wiphy->bands[band];
> if (!sband)
> continue;
> @@ -7507,6 +7503,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> {
> struct wmi_chan_info_event ch_info_ev = {};
> struct ath12k *ar;
> + struct ath12k_hw *ah;
> struct survey_info *survey;
> int idx;
> /* HW channel counters frequency value in hertz */
> @@ -7538,6 +7535,7 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> return;
> }
> spin_lock_bh(&ar->data_lock);
> + ah = ath12k_ar_to_ah(ar);
>
> switch (ar->scan.state) {
> case ATH12K_SCAN_IDLE:
> @@ -7549,8 +7547,8 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> break;
> }
>
> - idx = freq_to_idx(ar, le32_to_cpu(ch_info_ev.freq));
> - if (idx >= ARRAY_SIZE(ar->survey)) {
> + idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(ch_info_ev.freq));
> + if (idx >= ARRAY_SIZE(ah->survey)) {
> ath12k_warn(ab, "chan info: invalid frequency %d (idx %d out of bounds)\n",
> ch_info_ev.freq, idx);
> goto exit;
> @@ -7563,14 +7561,16 @@ static void ath12k_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> cc_freq_hz = (le32_to_cpu(ch_info_ev.mac_clk_mhz) * 1000);
>
> if (ch_info_ev.cmd_flags == WMI_CHAN_INFO_START_RESP) {
> - survey = &ar->survey[idx];
> - memset(survey, 0, sizeof(*survey));
> - survey->noise = le32_to_cpu(ch_info_ev.noise_floor);
> - survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
> + scoped_guard(spinlock_bh, &ah->survey_lock) {
> + survey = &ah->survey[idx];
> + memset(survey, 0, sizeof(*survey));
> + survey->noise = le32_to_cpu(ch_info_ev.noise_floor);
> + survey->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
> SURVEY_INFO_TIME_BUSY;
> - survey->time = div_u64(le32_to_cpu(ch_info_ev.cycle_count), cc_freq_hz);
> - survey->time_busy = div_u64(le32_to_cpu(ch_info_ev.rx_clear_count),
> - cc_freq_hz);
> + survey->time = div_u64(le32_to_cpu(ch_info_ev.cycle_count), cc_freq_hz);
> + survey->time_busy = div_u64(le32_to_cpu(ch_info_ev.rx_clear_count),
> + cc_freq_hz);
> + }
> }
> exit:
> spin_unlock_bh(&ar->data_lock);
> @@ -7583,6 +7583,7 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> struct wmi_pdev_bss_chan_info_event bss_ch_info_ev = {};
> struct survey_info *survey;
> struct ath12k *ar;
> + struct ath12k_hw *ah;
> u32 cc_freq_hz = ab->cc_freq_hz;
> u64 busy, total, tx, rx, rx_bss;
> int idx;
> @@ -7623,15 +7624,18 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> return;
> }
>
> - spin_lock_bh(&ar->data_lock);
> - idx = freq_to_idx(ar, le32_to_cpu(bss_ch_info_ev.freq));
> - if (idx >= ARRAY_SIZE(ar->survey)) {
> + ah = ath12k_ar_to_ah(ar);
> +
> + guard(spinlock_bh)(&ah->survey_lock);
> +
> + idx = freq_to_idx(ath12k_ar_to_hw(ar), le32_to_cpu(bss_ch_info_ev.freq));
> + if (idx >= ARRAY_SIZE(ah->survey)) {
> ath12k_warn(ab, "bss chan info: invalid frequency %d (idx %d out of bounds)\n",
> bss_ch_info_ev.freq, idx);
> goto exit;
> }
>
the scope of survey_lock should start here and ..
> - survey = &ar->survey[idx];
> + survey = &ah->survey[idx];
>
> survey->noise = le32_to_cpu(bss_ch_info_ev.noise_floor);
> survey->time = div_u64(total, cc_freq_hz);
> @@ -7644,7 +7648,6 @@ ath12k_pdev_bss_chan_info_event(struct ath12k_base *ab, struct sk_buff *skb)
> SURVEY_INFO_TIME_RX |
> SURVEY_INFO_TIME_TX);
> exit:
> - spin_unlock_bh(&ar->data_lock);
end here, may be a scoped guard here as well ?
> complete(&ar->bss_survey_done);
>
> rcu_read_unlock();
>
> ---
> base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
> change-id: 20260617-ath12-survey-band-fix-4b5e78579379
>
> Best regards,
> --
> Matt
>
Rest of the changes looks fine to me.
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v2] wifi: ath12k: fix survey indexing across bands
2026-07-02 10:50 [PATCH v2] wifi: ath12k: fix survey indexing across bands Matthew Leach
2026-07-02 18:06 ` Rameshkumar Sundaram
@ 2026-07-02 21:14 ` Nicolas Escande
2026-07-03 7:06 ` Matthew Leach
1 sibling, 1 reply; 4+ messages in thread
From: Nicolas Escande @ 2026-07-02 21:14 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson, Vasanthakumar Thiagarajan,
Baochen Qiang, Ramya Gnanasekar, Karthikeyan Periyasamy
Cc: Pradeep Kumar Chitrapu, P Praneesh, Sriram R, linux-wireless,
ath12k, linux-kernel, kernel, Rameshkumar Sundaram,
Rameshkumar Sundaram, Jeff Johnson
On Thu Jul 2, 2026 at 12:50 PM CEST, Matthew Leach wrote:
> When running 'iw dev wlan0 survey dump' the values for the channel busy
> time have the same sequence across bands. This is caused by indexing
> into the ath12k survey array using a band-local index rather than the
> global index passed by mac80211. This results in surveys for 5 GHz and 6
> GHz channels returning values from 2.4 GHz slots, making the survey
> unusable on those bands. Further, there are redundant survey slots for
> multi-radio/single-phy instances.
>
> Fix by moving the survey data into ath12k_hw so multiple radios under a
> single wiphy share one table, and index into it using the global
> mac80211 index. A new spinlock in ath12k_hw serialises access to the
> survey array, which is now shared across all radios under a single hw.
>
> Band busy-times Before this fix:
>
> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>
> After this fix, times are independent:
>
> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>
> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>
Hello,
I see that this is supposed to work on single phy device, but could someone
do a proper test to ensure that it will work on QCN9274 split phy ?
I know this is a big ask, and usually I would do it, but I do not have a setup
with a new enough kernel running to test this right now.
Thanks
> Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
^ permalink raw reply [flat|nested] 4+ messages in thread* Re: [PATCH v2] wifi: ath12k: fix survey indexing across bands
2026-07-02 21:14 ` Nicolas Escande
@ 2026-07-03 7:06 ` Matthew Leach
0 siblings, 0 replies; 4+ messages in thread
From: Matthew Leach @ 2026-07-03 7:06 UTC (permalink / raw)
To: Nicolas Escande
Cc: Jeff Johnson, Vasanthakumar Thiagarajan, Baochen Qiang,
Ramya Gnanasekar, Karthikeyan Periyasamy, Pradeep Kumar Chitrapu,
P Praneesh, Sriram R, linux-wireless, ath12k, linux-kernel,
kernel, Rameshkumar Sundaram, Rameshkumar Sundaram, Jeff Johnson
"Nicolas Escande" <nico.escande@gmail.com> writes:
> On Thu Jul 2, 2026 at 12:50 PM CEST, Matthew Leach wrote:
>> When running 'iw dev wlan0 survey dump' the values for the channel busy
>> time have the same sequence across bands. This is caused by indexing
>> into the ath12k survey array using a band-local index rather than the
>> global index passed by mac80211. This results in surveys for 5 GHz and 6
>> GHz channels returning values from 2.4 GHz slots, making the survey
>> unusable on those bands. Further, there are redundant survey slots for
>> multi-radio/single-phy instances.
>>
>> Fix by moving the survey data into ath12k_hw so multiple radios under a
>> single wiphy share one table, and index into it using the global
>> mac80211 index. A new spinlock in ath12k_hw serialises access to the
>> survey array, which is now shared across all radios under a single hw.
>>
>> Band busy-times Before this fix:
>>
>> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>>
>> After this fix, times are independent:
>>
>> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
>> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
>> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>>
>> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>>
>
> Hello,
Hi Nicolas,
> I see that this is supposed to work on single phy device, but could someone
> do a proper test to ensure that it will work on QCN9274 split phy ?
>
> I know this is a big ask, and usually I would do it, but I do not have a setup
> with a new enough kernel running to test this right now.
If you're looking at getting this tested on a split-phy, I'll get v3
pushed out asap which incorporates Rameshkumar's comment.
Thanks,
--
Matt
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2026-07-03 7:06 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-02 10:50 [PATCH v2] wifi: ath12k: fix survey indexing across bands Matthew Leach
2026-07-02 18:06 ` Rameshkumar Sundaram
2026-07-02 21:14 ` Nicolas Escande
2026-07-03 7:06 ` Matthew Leach
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox