[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