wifi: iwlwifi: mvm: Block EMLSR when a p2p/softAP vif is active
authorYedidya Benshimol <yedidya.ben.shimol@intel.com>
Sun, 5 May 2024 06:19:57 +0000 (09:19 +0300)
committerJohannes Berg <johannes.berg@intel.com>
Mon, 6 May 2024 14:33:24 +0000 (16:33 +0200)
When there's an active link in a non-station vif, the station vif is
not allowed to enter EMLSR

Note that blocking EMLSR by calling iwl_mvm_block_esr() we will schedule
an exit from EMLSR worker, but the worker cannot run before the
activation of the non-BSS link, as ieee80211_remain_on_channel already
holds the wiphy mutex.

Handle that by explicitly calling ieee80211_set_active_links()
to leave EMLSR, and then doing iwl_mvm_block_esr() only for
consistency and to avoid re-entering it before ready.

Note that a call to ieee80211_set_active_links requires to release the
mvm mutex, but that's ok since we still hold the wiphy lock. The only
thing that might race here is the ESR_MODE_NOTIF, so this changes its
handler to run under the wiphy lock.

Signed-off-by: Yedidya Benshimol <yedidya.ben.shimol@intel.com>
Co-developed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://msgid.link/20240505091420.916193759f8a.Idf3a3caf5cdc3e69c81710b7ceb57e87f2de87e4@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/link.c
drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/time-event.c

index 778ea64f3f288d9acf49f56ffeeca2235cb644db..71e6b06481a93b6bf6f6eb14b1b1656510cdbc92 100644 (file)
@@ -1243,7 +1243,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
                .data[0] = &d3_cfg_cmd_data,
                .len[0] = sizeof(d3_cfg_cmd_data),
        };
-       int ret, primary_link;
+       int ret;
        int len __maybe_unused;
        bool unified_image = fw_has_capa(&mvm->fw->ucode_capa,
                                         IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG);
@@ -1261,18 +1261,11 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
        if (IS_ERR_OR_NULL(vif))
                return 1;
 
-       primary_link = iwl_mvm_get_primary_link(vif);
-
-       /* leave ESR immediately, not only async with iwl_mvm_block_esr() */
-       if (ieee80211_vif_is_mld(vif)) {
-               ret = ieee80211_set_active_links(vif, BIT(primary_link));
-               if (ret)
-                       return ret;
-       }
+       ret = iwl_mvm_block_esr_sync(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN);
+       if (ret)
+               return ret;
 
        mutex_lock(&mvm->mutex);
-       /* only additionally block for consistency and to avoid concurrency */
-       iwl_mvm_block_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN, primary_link);
 
        set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
 
@@ -1280,7 +1273,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-       mvm_link = mvmvif->link[primary_link];
+       mvm_link = mvmvif->link[iwl_mvm_get_primary_link(vif)];
        if (WARN_ON_ONCE(!mvm_link)) {
                ret = -EINVAL;
                goto out_noreset;
index 7db4a834569c7e601450fcb8bd54ce2ec7b3cc31..7c8cfa72b8bb74a60843eec7eab09ce903ea99ac 100644 (file)
@@ -108,6 +108,65 @@ int iwl_mvm_add_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        return iwl_mvm_link_cmd_send(mvm, &cmd, FW_CTXT_ACTION_ADD);
 }
 
+struct iwl_mvm_esr_iter_data {
+       struct ieee80211_vif *vif;
+       unsigned int link_id;
+       bool lift_block;
+};
+
+static void iwl_mvm_esr_vif_iterator(void *_data, u8 *mac,
+                                    struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_esr_iter_data *data = _data;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+       int link_id;
+
+       if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION)
+               return;
+
+       for_each_mvm_vif_valid_link(mvmvif, link_id) {
+               struct iwl_mvm_vif_link_info *link_info =
+                       mvmvif->link[link_id];
+               if (vif == data->vif && link_id == data->link_id)
+                       continue;
+               if (link_info->active)
+                       data->lift_block = false;
+       }
+}
+
+int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            unsigned int link_id, bool active)
+{
+       /* An active link of a non-station vif blocks EMLSR. Upon activation
+        * block EMLSR on the bss vif. Upon deactivation, check if this link
+        * was the last non-station link active, and if so unblock the bss vif
+        */
+       struct ieee80211_vif *bss_vif = iwl_mvm_get_bss_vif(mvm);
+       struct iwl_mvm_esr_iter_data data = {
+               .vif = vif,
+               .link_id = link_id,
+               .lift_block = true,
+       };
+
+       if (IS_ERR_OR_NULL(bss_vif))
+               return 0;
+
+       if (active)
+               return iwl_mvm_block_esr_sync(mvm, bss_vif,
+                                             IWL_MVM_ESR_BLOCKED_NON_BSS);
+
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_esr_vif_iterator, &data);
+       if (data.lift_block) {
+               mutex_lock(&mvm->mutex);
+               iwl_mvm_unblock_esr(mvm, bss_vif, IWL_MVM_ESR_BLOCKED_NON_BSS);
+               mutex_unlock(&mvm->mutex);
+       }
+
+       return 0;
+}
+
 int iwl_mvm_link_changed(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                         struct ieee80211_bss_conf *link_conf,
                         u32 changes, bool active)
@@ -924,6 +983,32 @@ void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
        iwl_mvm_exit_esr(mvm, vif, reason, link_to_keep);
 }
 
+int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          enum iwl_mvm_esr_state reason)
+{
+       int primary_link = iwl_mvm_get_primary_link(vif);
+       int ret;
+
+       if (!IWL_MVM_AUTO_EML_ENABLE || !ieee80211_vif_is_mld(vif))
+               return 0;
+
+       /* This should be called only with blocking reasons */
+       if (WARN_ON(!(reason & IWL_MVM_BLOCK_ESR_REASONS)))
+               return 0;
+
+       /* leave ESR immediately, not only async with iwl_mvm_block_esr() */
+       ret = ieee80211_set_active_links(vif, BIT(primary_link));
+       if (ret)
+               return ret;
+
+       mutex_lock(&mvm->mutex);
+       /* only additionally block for consistency and to avoid concurrency */
+       iwl_mvm_block_esr(mvm, vif, reason, primary_link);
+       mutex_unlock(&mvm->mutex);
+
+       return 0;
+}
+
 static void iwl_mvm_esr_unblocked(struct iwl_mvm *mvm,
                                  struct ieee80211_vif *vif)
 {
index f28ffefd434e22d3ce934d96f207d37d1e943d3e..dbfbbfb5b678e3e9874b56c657f5a825b134f483 100644 (file)
@@ -4830,6 +4830,10 @@ int iwl_mvm_roc_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
         */
        flush_work(&mvm->roc_done_wk);
 
+       ret = iwl_mvm_esr_non_bss_link(mvm, vif, 0, true);
+       if (ret)
+               return ret;
+
        mutex_lock(&mvm->mutex);
 
        switch (vif->type) {
@@ -4873,9 +4877,7 @@ int iwl_mvm_cancel_roc(struct ieee80211_hw *hw,
 
        IWL_DEBUG_MAC80211(mvm, "enter\n");
 
-       mutex_lock(&mvm->mutex);
        iwl_mvm_stop_roc(mvm, vif);
-       mutex_unlock(&mvm->mutex);
 
        IWL_DEBUG_MAC80211(mvm, "leave\n");
        return 0;
index 748c72cbb2dc8a2f072ed6d04afdd57d3cbec37d..bceafd551e8f19788079635133e28c717fcf6479 100644 (file)
@@ -391,6 +391,18 @@ static int iwl_mvm_mld_assign_vif_chanctx(struct ieee80211_hw *hw,
        struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw);
        int ret;
 
+       /* update EMLSR mode */
+       if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) {
+               ret = iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id,
+                                              true);
+               /*
+                * Don't activate this link if failed to exit EMLSR in
+                * the BSS interface
+                */
+               if (ret)
+                       return ret;
+       }
+
        mutex_lock(&mvm->mutex);
        ret = __iwl_mvm_mld_assign_vif_chanctx(mvm, vif, link_conf, ctx, false);
        mutex_unlock(&mvm->mutex);
@@ -514,6 +526,10 @@ static void iwl_mvm_mld_unassign_vif_chanctx(struct ieee80211_hw *hw,
                iwl_mvm_add_link(mvm, vif, link_conf);
        }
        mutex_unlock(&mvm->mutex);
+
+       /* update EMLSR mode */
+       if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION)
+               iwl_mvm_esr_non_bss_link(mvm, vif, link_conf->link_id, false);
 }
 
 static void
index 96d29eae89032ae371e035656a65523f9fbcbcb4..115aa1ad970e288fe234a3cffbf60091642cb716 100644 (file)
@@ -358,6 +358,7 @@ struct iwl_mvm_vif_link_info {
  * @IWL_MVM_ESR_BLOCKED_WOWLAN: WOWLAN is preventing the enablement of EMLSR
  * @IWL_MVM_ESR_BLOCKED_TPT: block EMLSR when there is not enough traffic
  * @IWL_MVM_ESR_BLOCKED_FW: FW didn't recommended/forced exit from EMLSR
+ * @IWL_MVM_ESR_BLOCKED_NON_BSS: An active non-bssid link's preventing EMLSR
  * @IWL_MVM_ESR_EXIT_MISSED_BEACON: exited EMLSR due to missed beacons
  * @IWL_MVM_ESR_EXIT_LOW_RSSI: link is deactivated/not allowed for EMLSR
  *     due to low RSSI.
@@ -372,6 +373,7 @@ enum iwl_mvm_esr_state {
        IWL_MVM_ESR_BLOCKED_WOWLAN      = 0x2,
        IWL_MVM_ESR_BLOCKED_TPT         = 0x4,
        IWL_MVM_ESR_BLOCKED_FW          = 0x8,
+       IWL_MVM_ESR_BLOCKED_NON_BSS     = 0x10,
        IWL_MVM_ESR_EXIT_MISSED_BEACON  = 0x10000,
        IWL_MVM_ESR_EXIT_LOW_RSSI       = 0x20000,
        IWL_MVM_ESR_EXIT_COEX           = 0x40000,
@@ -2888,6 +2890,8 @@ bool iwl_mvm_vif_has_esr_cap(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
 void iwl_mvm_block_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                       enum iwl_mvm_esr_state reason,
                       u8 link_to_keep);
+int iwl_mvm_block_esr_sync(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                          enum iwl_mvm_esr_state reason);
 void iwl_mvm_unblock_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                         enum iwl_mvm_esr_state reason);
 void iwl_mvm_exit_esr(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
@@ -2904,4 +2908,6 @@ iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif,
                                   s32 link_rssi,
                                   bool primary);
+int iwl_mvm_esr_non_bss_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
+                            unsigned int link_id, bool active);
 #endif /* __IWL_MVM_H__ */
index 55ac5552b2f8b6a57ed31af4e44d41920a3ab698..155a44e8ab0727708ffd27ef7931643a1537fd6c 100644 (file)
@@ -445,7 +445,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                       struct iwl_channel_switch_error_notif),
 
        RX_HANDLER_GRP(DATA_PATH_GROUP, ESR_MODE_NOTIF,
-                      iwl_mvm_rx_esr_mode_notif, RX_HANDLER_ASYNC_LOCKED,
+                      iwl_mvm_rx_esr_mode_notif,
+                      RX_HANDLER_ASYNC_LOCKED_WIPHY,
                       struct iwl_mvm_esr_mode_notif),
 
        RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF,
index ad960faceb0d8f6b05160a547b056d45f47563f2..8ee4498f42455583554f0b53fc235eddd6b5459d 100644 (file)
@@ -47,6 +47,10 @@ void iwl_mvm_te_clear_data(struct iwl_mvm *mvm,
 
 static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
 {
+       struct ieee80211_vif *vif = mvm->p2p_device_vif;
+
+       lockdep_assert_held(&mvm->mutex);
+
        /*
         * Clear the ROC_RUNNING status bit.
         * This will cause the TX path to drop offchannel transmissions.
@@ -70,9 +74,7 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
                 * not really racy.
                 */
 
-               if (!WARN_ON(!mvm->p2p_device_vif)) {
-                       struct ieee80211_vif *vif = mvm->p2p_device_vif;
-
+               if (!WARN_ON(!vif)) {
                        mvmvif = iwl_mvm_vif_from_mac80211(vif);
                        iwl_mvm_flush_sta(mvm, mvmvif->deflink.bcast_sta.sta_id,
                                          mvmvif->deflink.bcast_sta.tfd_queue_msk);
@@ -106,6 +108,7 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
 
                if (mvm->mld_api_is_used) {
                        iwl_mvm_mld_rm_aux_sta(mvm);
+                       mutex_unlock(&mvm->mutex);
                        return;
                }
 
@@ -115,6 +118,10 @@ static void iwl_mvm_cleanup_roc(struct iwl_mvm *mvm)
                if (iwl_mvm_has_new_station_api(mvm->fw))
                        iwl_mvm_rm_aux_sta(mvm);
        }
+
+       mutex_unlock(&mvm->mutex);
+       if (vif)
+               iwl_mvm_esr_non_bss_link(mvm, vif, 0, false);
 }
 
 void iwl_mvm_roc_done_wk(struct work_struct *wk)
@@ -122,8 +129,8 @@ void iwl_mvm_roc_done_wk(struct work_struct *wk)
        struct iwl_mvm *mvm = container_of(wk, struct iwl_mvm, roc_done_wk);
 
        mutex_lock(&mvm->mutex);
+       /* Mutex is released inside */
        iwl_mvm_cleanup_roc(mvm);
-       mutex_unlock(&mvm->mutex);
 }
 
 static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
@@ -1220,6 +1227,8 @@ void iwl_mvm_stop_roc(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
        struct iwl_mvm_vif *mvmvif;
        struct iwl_mvm_time_event_data *te_data;
 
+       mutex_lock(&mvm->mutex);
+
        if (fw_has_capa(&mvm->fw->ucode_capa,
                        IWL_UCODE_TLV_CAPA_SESSION_PROT_CMD)) {
                mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -1263,6 +1272,8 @@ cleanup_roc:
        set_bit(vif->type == NL80211_IFTYPE_P2P_DEVICE ?
                IWL_MVM_STATUS_ROC_RUNNING : IWL_MVM_STATUS_ROC_AUX_RUNNING,
                &mvm->status);
+
+       /* Mutex is released inside this function */
        iwl_mvm_cleanup_roc(mvm);
 }