[PATCH ath-next] wifi: ath12k: Prevent incorrect vif chanctx switch when handling multi-radio contexts
Maharaja Kennadyrajan
maharaja.kennadyrajan at oss.qualcomm.com
Fri May 22 02:19:48 PDT 2026
On 22-05-2026 02:42 pm, Maharaja Kennadyrajan wrote:
> From: Aditya Kumar Singh <aditya.kumar.singh at oss.qualcomm.com>
Kindly ignore this patch. I will send v2 of this patch with
Co-developed-by tag added.
> When multiple links switch channel contexts around the same time, mac80211
> may complete CSA for several links together and invoke
> ath12k_mac_op_switch_vif_chanctx() with an array of vifs spanning more than
> one underlying radio in a single-wiphy configuration.
>
> The driver currently assumes that all entries in the vifs array belong to the
> same radio and derives the radio context from the first element. On multi-radio
> hardware, this can lead to incorrect vdev selection/updates and may corrupt
> driver state when the number of vifs exceeds what a single radio supports.
>
> Fix this by validating each vif's switch request and then processing vifs
> grouped by their associated radio. For each vif, ensure the band does not
> change across the switch and that both old/new channel contexts resolve to a
> valid ath12k device. Reject attempts to move a vif between radios (not
> supported for now) and return -EOPNOTSUPP to upper layers.
>
> Then, iterate through the input vifs, collect all unprocessed entries that map
> to the same radio, and invoke ath12k_mac_update_vif_chan() separately for each
> radio group. This removes any reliance on mac80211 providing the array grouped
> by radio or sharing old_ctx pointers across vifs.
>
> Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.5-01651-QCAHKSWPL_SILICONZ-1
>
> Signed-off-by: Aditya Kumar Singh <aditya.kumar.singh at oss.qualcomm.com>
> Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan at oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath12k/mac.c | 87 +++++++++++++++++++++++----
> 1 file changed, 76 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
> index 54081beb9a5f..5c8e25d40a98 100644
> --- a/drivers/net/wireless/ath/ath12k/mac.c
> +++ b/drivers/net/wireless/ath/ath12k/mac.c
> @@ -11476,6 +11476,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
> continue;
> }
>
> + if (WARN_ON(!arvif))
> + continue;
> +
> ath12k_dbg(ab, ATH12K_DBG_MAC,
> "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
> arvif->vdev_id,
> @@ -12267,23 +12270,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
> int n_vifs,
> enum ieee80211_chanctx_switch_mode mode)
> {
> - struct ath12k *ar;
> + struct ath12k *curr_ar, *new_ar, *group_ar;
> + struct ieee80211_vif_chanctx_switch *v;
> + int i, j, count = 0;
>
> lockdep_assert_wiphy(hw->wiphy);
>
> - ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx);
> - if (!ar)
> - return -EINVAL;
> + if (n_vifs == 0)
> + return 0;
>
> - /* Switching channels across radio is not allowed */
> - if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx))
> - return -EINVAL;
> + struct ath12k **ar_map __free(kfree) = kzalloc_objs(*ar_map, n_vifs);
>
> - ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
> - "mac chanctx switch n_vifs %d mode %d\n",
> - n_vifs, mode);
> - ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
> + if (!ar_map)
> + return -ENOMEM;
> +
> + for (i = 0; i < n_vifs; i++) {
> + v = &vifs[i];
> +
> + if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) {
> + ath12k_generic_dbg(ATH12K_DBG_MAC,
> + "mac chanctx switch band change not supported\n");
> + return -EOPNOTSUPP;
> + }
> +
> + curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx);
> + new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx);
> +
> + if (!curr_ar || !new_ar) {
> + ath12k_generic_dbg(ATH12K_DBG_MAC,
> + "unable to determine device for the passed channel ctx\n");
> + ath12k_generic_dbg(ATH12K_DBG_MAC,
> + "Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n",
> + v->old_ctx->def.chan->center_freq,
> + curr_ar ? "valid" : "invalid",
> + v->new_ctx->def.chan->center_freq,
> + new_ar ? "valid" : "invalid");
> + return -EINVAL;
> + }
>
> + /* Switching a vif between two radios is not allowed */
> + if (curr_ar != new_ar) {
> + ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC,
> + "mac chanctx switch to another radio not supported\n");
> + return -EOPNOTSUPP;
> + }
> +
> + ar_map[i] = curr_ar;
> + }
> +
> + /* Group vifs by radio (ar) and process each group independently. */
> + bool *processed __free(kfree) = kzalloc_objs(*processed, n_vifs);
> +
> + if (!processed)
> + return -ENOMEM;
> +
> + struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) =
> + kzalloc_objs(*group_vifs, n_vifs);
> +
> + if (!group_vifs)
> + return -ENOMEM;
> +
> + for (i = 0; i < n_vifs; i++) {
> + if (processed[i])
> + continue;
> +
> + group_ar = ar_map[i];
> +
> + count = 0;
> + for (j = 0; j < n_vifs; j++) {
> + if (!processed[j] && ar_map[j] == group_ar) {
> + group_vifs[count++] = vifs[j];
> + processed[j] = true;
> + }
> + }
> +
> + ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC,
> + "mac chanctx switch n_vifs %d mode %d\n",
> + count, mode);
> + ath12k_mac_update_vif_chan(group_ar, group_vifs, count);
> + }
> return 0;
> }
> EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx);
>
> base-commit: 30d516006fa1f72f957c18c6171f5680dcdebfb0
More information about the ath12k
mailing list