Skip to content

Commit 675aa75

Browse files
Aditya Kumar Singhjeff-t-johnson
authored andcommitted
wifi: ath12k: Prevent incorrect vif chanctx switch when handling multi-radio contexts
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@oss.qualcomm.com> Co-developed-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com> Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com> Reviewed-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com> Reviewed-by: Rameshkumar Sundaram <rameshkumar.sundaram@oss.qualcomm.com> Link: https://patch.msgid.link/20260522091828.3199584-1-maharaja.kennadyrajan@oss.qualcomm.com Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
1 parent 05337d0 commit 675aa75

1 file changed

Lines changed: 76 additions & 11 deletions

File tree

  • drivers/net/wireless/ath/ath12k

drivers/net/wireless/ath/ath12k/mac.c

Lines changed: 76 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11479,6 +11479,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
1147911479
continue;
1148011480
}
1148111481

11482+
if (WARN_ON(!arvif))
11483+
continue;
11484+
1148211485
ath12k_dbg(ab, ATH12K_DBG_MAC,
1148311486
"mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n",
1148411487
arvif->vdev_id,
@@ -12270,23 +12273,85 @@ ath12k_mac_op_switch_vif_chanctx(struct ieee80211_hw *hw,
1227012273
int n_vifs,
1227112274
enum ieee80211_chanctx_switch_mode mode)
1227212275
{
12273-
struct ath12k *ar;
12276+
struct ath12k *curr_ar, *new_ar, *group_ar;
12277+
struct ieee80211_vif_chanctx_switch *v;
12278+
int i, j, count = 0;
1227412279

1227512280
lockdep_assert_wiphy(hw->wiphy);
1227612281

12277-
ar = ath12k_get_ar_by_ctx(hw, vifs->old_ctx);
12278-
if (!ar)
12279-
return -EINVAL;
12282+
if (n_vifs == 0)
12283+
return 0;
1228012284

12281-
/* Switching channels across radio is not allowed */
12282-
if (ar != ath12k_get_ar_by_ctx(hw, vifs->new_ctx))
12283-
return -EINVAL;
12285+
struct ath12k **ar_map __free(kfree) = kzalloc_objs(*ar_map, n_vifs);
1228412286

12285-
ath12k_dbg(ar->ab, ATH12K_DBG_MAC,
12286-
"mac chanctx switch n_vifs %d mode %d\n",
12287-
n_vifs, mode);
12288-
ath12k_mac_update_vif_chan(ar, vifs, n_vifs);
12287+
if (!ar_map)
12288+
return -ENOMEM;
12289+
12290+
for (i = 0; i < n_vifs; i++) {
12291+
v = &vifs[i];
12292+
12293+
if (v->old_ctx->def.chan->band != v->new_ctx->def.chan->band) {
12294+
ath12k_generic_dbg(ATH12K_DBG_MAC,
12295+
"mac chanctx switch band change not supported\n");
12296+
return -EOPNOTSUPP;
12297+
}
12298+
12299+
curr_ar = ath12k_get_ar_by_ctx(hw, v->old_ctx);
12300+
new_ar = ath12k_get_ar_by_ctx(hw, v->new_ctx);
12301+
12302+
if (!curr_ar || !new_ar) {
12303+
ath12k_generic_dbg(ATH12K_DBG_MAC,
12304+
"unable to determine device for the passed channel ctx\n");
12305+
ath12k_generic_dbg(ATH12K_DBG_MAC,
12306+
"Old freq %d MHz (device %s) to new freq %d MHz (device %s)\n",
12307+
v->old_ctx->def.chan->center_freq,
12308+
curr_ar ? "valid" : "invalid",
12309+
v->new_ctx->def.chan->center_freq,
12310+
new_ar ? "valid" : "invalid");
12311+
return -EINVAL;
12312+
}
1228912313

12314+
/* Switching a vif between two radios is not allowed */
12315+
if (curr_ar != new_ar) {
12316+
ath12k_dbg(curr_ar->ab, ATH12K_DBG_MAC,
12317+
"mac chanctx switch to another radio not supported\n");
12318+
return -EOPNOTSUPP;
12319+
}
12320+
12321+
ar_map[i] = curr_ar;
12322+
}
12323+
12324+
/* Group vifs by radio (ar) and process each group independently. */
12325+
bool *processed __free(kfree) = kzalloc_objs(*processed, n_vifs);
12326+
12327+
if (!processed)
12328+
return -ENOMEM;
12329+
12330+
struct ieee80211_vif_chanctx_switch *group_vifs __free(kfree) =
12331+
kzalloc_objs(*group_vifs, n_vifs);
12332+
12333+
if (!group_vifs)
12334+
return -ENOMEM;
12335+
12336+
for (i = 0; i < n_vifs; i++) {
12337+
if (processed[i])
12338+
continue;
12339+
12340+
group_ar = ar_map[i];
12341+
12342+
count = 0;
12343+
for (j = 0; j < n_vifs; j++) {
12344+
if (!processed[j] && ar_map[j] == group_ar) {
12345+
group_vifs[count++] = vifs[j];
12346+
processed[j] = true;
12347+
}
12348+
}
12349+
12350+
ath12k_dbg(group_ar->ab, ATH12K_DBG_MAC,
12351+
"mac chanctx switch n_vifs %d mode %d\n",
12352+
count, mode);
12353+
ath12k_mac_update_vif_chan(group_ar, group_vifs, count);
12354+
}
1229012355
return 0;
1229112356
}
1229212357
EXPORT_SYMBOL(ath12k_mac_op_switch_vif_chanctx);

0 commit comments

Comments
 (0)