wifi: mt76: fix handling channel context with different bands in mt76_switch_vif_chanctx()

When performing channel switches on different radios within a short
timeframe, channel contexts with different bands can be carried for
each struct ieee80211_vif_chanctx_switch.

Rework mt76_switch_vif_chanctx() to properly handle this scenario.

Fixes: 82334623af ("wifi: mt76: add chanctx functions for multi-channel phy support")
Co-developed-by: Rex Lu <rex.lu@mediatek.com>
Signed-off-by: Rex Lu <rex.lu@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
Link: https://patch.msgid.link/20260720090102.190729-1-shayne.chen@mediatek.com
Signed-off-by: Felix Fietkau <nbd@nbd.name>
This commit is contained in:
Shayne Chen 2026-07-20 17:01:02 +08:00 committed by Felix Fietkau
parent 965cbdbdfb
commit 70869cc429

View File

@ -186,68 +186,74 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
int n_vifs,
enum ieee80211_chanctx_switch_mode mode)
{
struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;
struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;
struct ieee80211_chanctx_conf *conf = vifs->new_ctx;
struct mt76_phy *old_phy = old_ctx->phy;
struct mt76_phy *phy = hw->priv;
struct ieee80211_vif_chanctx_switch *v;
struct mt76_chanctx *old_ctx, *new_ctx;
struct mt76_phy *old_phy, *phy = hw->priv;
struct mt76_dev *dev = phy->dev;
struct mt76_vif_link *mlink;
bool update_chan;
bool need_update[__MT_MAX_BAND] = {};
int i, ret = 0;
if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];
else
phy = new_ctx->phy;
if (!phy)
return -EINVAL;
for (i = 0; i < n_vifs; i++) {
v = &vifs[i];
new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv;
if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
phy = new_ctx->phy = dev->band_phys[v->new_ctx->def.chan->band];
else
phy = new_ctx->phy;
update_chan = phy->chanctx != new_ctx;
if (update_chan) {
if (dev->scan.phy == phy)
mt76_abort_scan(dev);
if (!phy)
return -EINVAL;
cancel_delayed_work_sync(&phy->mac_work);
if (need_update[phy->band_idx])
continue;
if (phy->chanctx != new_ctx) {
if (dev->scan.phy == phy)
mt76_abort_scan(dev);
cancel_delayed_work_sync(&phy->mac_work);
need_update[phy->band_idx] = true;
}
}
mutex_lock(&dev->mutex);
if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
phy != old_phy && old_phy->chanctx == old_ctx)
old_phy->chanctx = NULL;
if (update_chan)
ret = mt76_phy_update_channel(phy, vifs->new_ctx);
if (ret)
goto out;
if (old_phy == phy)
goto skip_link_replace;
for (i = 0; i < n_vifs; i++) {
mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
v = &vifs[i];
old_ctx = (struct mt76_chanctx *)v->old_ctx->drv_priv;
old_phy = old_ctx->phy;
new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv;
phy = new_ctx->phy;
if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && old_phy->chanctx &&
old_phy->chanctx == old_ctx && phy != old_phy)
old_phy->chanctx = NULL;
if (need_update[phy->band_idx]) {
ret = mt76_phy_update_channel(phy, v->new_ctx);
if (ret)
goto out;
need_update[phy->band_idx] = false;
}
mlink = mt76_vif_conf_link(dev, v->vif, v->link_conf);
if (!mlink)
continue;
dev->drv->vif_link_remove(old_phy, vifs[i].vif,
vifs[i].link_conf, mlink);
if (old_phy != phy) {
dev->drv->vif_link_remove(old_phy, v->vif, v->link_conf,
mlink);
ret = dev->drv->vif_link_add(phy, vifs[i].vif,
vifs[i].link_conf, mlink);
if (ret)
goto out;
ret = dev->drv->vif_link_add(phy, v->vif, v->link_conf,
mlink);
if (ret)
goto out;
}
}
skip_link_replace:
for (i = 0; i < n_vifs; i++) {
mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
if (!mlink)
continue;
mlink->ctx = vifs->new_ctx;
mlink->ctx = v->new_ctx;
if (mlink->beacon_mon_interval)
WRITE_ONCE(mlink->beacon_mon_last, jiffies);
}