wifi: iwlwifi: mld: update the TLC when we deactivate a link

We hit a problem in the channel switch flow.
We had link 0 using PHY 0, so the TLC object in the firmware is using
PHY 0.
Then we switched channel, so mac80211 / iwlmld:
* deactivated link 0
* removed PHY 0
* added PHY 1
* modified link 0 to use PHY 1
* activated link 0.

The TLC object was not updated and the firmware was unhappy that the TLC
was still trying to use PHY 0.

Fix that by letting the TLC know about the PHY context before the link
activation.
When we are de-activating a link, let the TLC know so that it'll send a
TLC configuration command with an invalid PHY context to remove the
relationship between the TLC object and the PHY that is going to be
removed.

That last part is not implemented yet in the firmware, so leave this as
a TODO for now.

Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260321192637.317c66b11a31.I591118fa376ed967c0d1a47058c13834bc94605e@changeid
This commit is contained in:
Emmanuel Grumbach 2026-03-21 19:29:16 +02:00 committed by Miri Korenblit
parent f2463eff4a
commit b008f28600
3 changed files with 56 additions and 1 deletions

View File

@ -1148,6 +1148,8 @@ int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
/* Now activate the link */
if (iwl_mld_can_activate_link(mld, vif, link)) {
iwl_mld_tlc_update_phy(mld, vif, link);
ret = iwl_mld_activate_link(mld, link);
if (ret)
goto err;
@ -1209,6 +1211,8 @@ void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
RCU_INIT_POINTER(mld_link->chan_ctx, NULL);
iwl_mld_tlc_update_phy(mld, vif, link);
/* in the non-MLO case, remove/re-add the link to clean up FW state.
* In MLO, it'll be done in drv_change_vif_link
*/

View File

@ -9,6 +9,7 @@
#include "hcmd.h"
#include "sta.h"
#include "phy.h"
#include "iface.h"
#include "fw/api/rs.h"
#include "fw/api/context.h"
@ -530,6 +531,7 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
struct ieee80211_bss_conf *link)
{
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link);
enum nl80211_band band = link->chanreq.oper.chan->band;
struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band];
const struct ieee80211_sta_he_cap *own_he_cap =
@ -566,7 +568,10 @@ static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld,
cmd.sta_mask = cpu_to_le32(BIT(fw_sta_id));
chan_ctx = rcu_dereference_wiphy(mld->wiphy, link->chanctx_conf);
if (WARN_ON_ONCE(!mld_link))
return;
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
if (WARN_ON(!chan_ctx))
return;
@ -658,6 +663,49 @@ void iwl_mld_config_tlc_link(struct iwl_mld *mld,
iwl_mld_send_tlc_cmd(mld, vif, link_sta, link_conf);
}
void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf)
{
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
struct ieee80211_chanctx_conf *chan_ctx;
int link_id = link_conf->link_id;
struct ieee80211_sta *sta;
lockdep_assert_wiphy(mld->wiphy);
if (WARN_ON(!mld_link))
return;
chan_ctx = rcu_dereference_wiphy(mld->wiphy, mld_link->chan_ctx);
for_each_station(sta, mld->hw) {
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
struct iwl_mld_link_sta *mld_link_sta;
struct ieee80211_link_sta *link_sta;
if (mld_sta->vif != vif)
continue;
link_sta = link_sta_dereference_protected(sta, link_id);
if (!link_sta)
continue;
mld_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
link_id);
/* In recovery flow, the station may not be (yet) in the
* firmware, don't send a TLC command for a station the
* firmware does not know.
*/
if (!mld_link_sta || !mld_link_sta->in_fw)
continue;
if (chan_ctx)
iwl_mld_config_tlc_link(mld, vif, link_conf, link_sta);
/* TODO: else, remove the TLC object in the firmware */
}
}
void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_sta *sta)
{

View File

@ -20,4 +20,7 @@ void iwl_mld_handle_tlc_notif(struct iwl_mld *mld,
int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data);
void iwl_mld_tlc_update_phy(struct iwl_mld *mld, struct ieee80211_vif *vif,
struct ieee80211_bss_conf *link_conf);
#endif /* __iwl_mld_tlc_h__ */