static bool
 iwl_mvm_bt_coex_calculate_esr_mode(struct iwl_mvm *mvm,
                                   struct ieee80211_vif *vif,
-                                  int link_id, int primary_link)
+                                  int link_id)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        struct iwl_mvm_vif_link_info *link_info = mvmvif->link[link_id];
                return true;
 
         /* If LB link is the primary one we should always disable eSR */
-       if (link_id == primary_link)
+       if (link_id == iwl_mvm_get_primary_link(vif))
                return false;
 
        /* The feature is not supported */
                                     struct ieee80211_vif *vif,
                                     int link_id)
 {
-       unsigned long usable_links = ieee80211_vif_usable_links(vif);
-       int primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
-                                                       usable_links);
        bool enable;
 
-       /* Not assoc, not MLD vif or only one usable link */
-       if (primary_link < 0)
+       if (!ieee80211_vif_is_mld(vif) ||
+           !iwl_mvm_vif_from_mac80211(vif)->authorized)
                return;
 
-       enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id,
-                                                   primary_link);
+       enable = iwl_mvm_bt_coex_calculate_esr_mode(mvm, vif, link_id);
 
        iwl_mvm_bt_coex_enable_esr(mvm, vif, enable);
 }
 
        if (IS_ERR_OR_NULL(vif))
                return 1;
 
-       if (hweight16(vif->active_links) > 1) {
+       mutex_lock(&mvm->mutex);
+
+       primary_link = iwl_mvm_get_primary_link(vif);
+       if (ieee80211_vif_is_mld(vif) && vif->cfg.assoc &&
+           mvmvif->esr_active) {
                /*
-                * Select the 'best' link.
-                * May need to revisit, it seems better to not optimize
-                * for throughput but rather range, reliability and
-                * power here - and select 2.4 GHz ...
+                * Select the 'best' link. May need to revisit, it seems
+                * better to not optimize for throughput but rather
+                * range, reliability and power here - and select
+                * 2.4 GHz ...
                 */
-               primary_link = iwl_mvm_mld_get_primary_link(mvm, vif,
-                                                           vif->active_links);
-
-               if (WARN_ONCE(primary_link < 0, "no primary link in 0x%x\n",
-                             vif->active_links))
-                       primary_link = __ffs(vif->active_links);
-
                ret = ieee80211_set_active_links(vif, BIT(primary_link));
                if (ret)
                        return ret;
-       } else if (vif->active_links) {
-               primary_link = __ffs(vif->active_links);
-       } else {
-               primary_link = 0;
        }
 
-       mutex_lock(&mvm->mutex);
-
        set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status);
 
        synchronize_net();
 
 }
 
 static unsigned int
-iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
+iwl_mvm_get_chan_load(struct ieee80211_bss_conf *link_conf)
 {
        struct iwl_mvm_vif_link_info *mvm_link =
                iwl_mvm_vif_from_mac80211(link_conf->vif)->link[link_conf->link_id];
-       const struct element *bss_load_elem =
-               ieee80211_bss_get_elem(link_conf->bss, WLAN_EID_QBSS_LOAD);
+       const struct element *bss_load_elem;
        const struct ieee80211_bss_load_elem *bss_load;
        enum nl80211_band band = link_conf->chanreq.oper.chan->band;
        unsigned int chan_load;
        u32 chan_load_by_us;
 
+       rcu_read_lock();
+       bss_load_elem = ieee80211_bss_get_elem(link_conf->bss,
+                                              WLAN_EID_QBSS_LOAD);
+
        /* If there isn't BSS Load element, take the defaults */
        if (!bss_load_elem ||
            bss_load_elem->datalen != sizeof(*bss_load)) {
+               rcu_read_unlock();
                switch (band) {
                case NL80211_BAND_2GHZ:
                        chan_load = DEFAULT_CHAN_LOAD_LB;
                        break;
                }
                /* The defaults are given in percentage */
-               return SCALE_FACTOR - NORMALIZE_PERCENT_TO_255(chan_load);
+               return NORMALIZE_PERCENT_TO_255(chan_load);
        }
 
        bss_load = (const void *)bss_load_elem->data;
        /* Channel util is in range 0-255 */
        chan_load = bss_load->channel_util;
+       rcu_read_unlock();
 
        if (!mvm_link || !mvm_link->active)
-               goto done;
+               return chan_load;
 
        if (WARN_ONCE(!mvm_link->phy_ctxt,
                      "Active link (%u) without phy ctxt assigned!\n",
                      link_conf->link_id))
-               goto done;
+               return chan_load;
 
        /* channel load by us is given in percentage */
        chan_load_by_us =
        /* Use only values that firmware sends that can possibly be valid */
        if (chan_load_by_us <= chan_load)
                chan_load -= chan_load_by_us;
-done:
-       return  SCALE_FACTOR - chan_load;
+
+       return chan_load;
+}
+
+static unsigned int
+iwl_mvm_get_chan_load_factor(struct ieee80211_bss_conf *link_conf)
+{
+       return SCALE_FACTOR - iwl_mvm_get_chan_load(link_conf);
 }
 
 /* This function calculates the grade of a link. Returns 0 in error case */
+VISIBLE_IF_IWLWIFI_KUNIT
 unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf)
 {
        enum nl80211_band band;
 
        rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
 
+       /* No valid RSSI - take the lowest grade */
+       if (!link_rssi)
+               link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
+
        /* Get grade based on RSSI */
        for (i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) {
                const struct iwl_mvm_rssi_to_grade *line =
 }
 EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_get_link_grade);
 
-/*
- * This function receives a subset of the usable links bitmap and
- * returns the primary link id, and -1 if such link doesn't exist
- * (e.g. non-MLO connection) or wasn't found.
- */
-int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                unsigned long usable_links)
-{
-       struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
-       u8 link_id, n_data = 0;
-
-       if (!ieee80211_vif_is_mld(vif) || !vif->cfg.assoc)
-               return -1;
-
-       for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
-               struct ieee80211_bss_conf *link_conf =
-                       link_conf_dereference_protected(vif, link_id);
-
-               if (WARN_ON_ONCE(!link_conf))
-                       continue;
-
-               data[n_data].link_id = link_id;
-               data[n_data].band = link_conf->chanreq.oper.chan->band;
-               data[n_data].width = link_conf->chanreq.oper.width;
-               data[n_data].active = true;
-               n_data++;
-       }
-
-       if (n_data <= 1)
-               return -1;
-
-       /* The logic should be modified to handle more than 2 links */
-       WARN_ON_ONCE(n_data > 2);
-
-       /* Primary link is the link with the wider bandwidth or higher band */
-       if (data[0].width > data[1].width)
-               return data[0].link_id;
-       if (data[0].width < data[1].width)
-               return data[1].link_id;
-       if (data[0].band >= data[1].band)
-               return data[0].link_id;
-
-       return data[1].link_id;
-}
-
 u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
                                   struct iwl_mvm_link_sel_data *data,
-                                  unsigned long usable_links)
+                                  unsigned long usable_links,
+                                  u8 *best_link_idx)
 {
        u8 n_data = 0;
+       u16 max_grade = 0;
        unsigned long link_id;
 
-       rcu_read_lock();
-
        for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) {
                struct ieee80211_bss_conf *link_conf =
-                       rcu_dereference(vif->link_conf[link_id]);
+                       link_conf_dereference_protected(vif, link_id);
 
                if (WARN_ON_ONCE(!link_conf))
                        continue;
 
                data[n_data].link_id = link_id;
                data[n_data].band = link_conf->chanreq.oper.chan->band;
-               data[n_data].width = link_conf->chanreq.oper.width;
-               data[n_data].active = vif->active_links & BIT(link_id);
+               data[n_data].grade = iwl_mvm_get_link_grade(link_conf);
+
+               if (data[n_data].grade > max_grade) {
+                       max_grade = data[n_data].grade;
+                       *best_link_idx = n_data;
+               }
                n_data++;
        }
 
-       rcu_read_unlock();
-
        return n_data;
 }
 
+VISIBLE_IF_IWLWIFI_KUNIT
 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
-                                struct iwl_mvm_link_sel_data *a,
-                                struct iwl_mvm_link_sel_data *b)
+                                const struct iwl_mvm_link_sel_data *a,
+                                const struct iwl_mvm_link_sel_data *b)
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
 
        return true;
 }
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_mld_valid_link_pair);
+
+/*
+ * Returns the combined eSR grade of two given links.
+ * Returns 0 if eSR is not allowed with these 2 links.
+ */
+static
+unsigned int iwl_mvm_get_esr_grade(struct ieee80211_vif *vif,
+                                  const struct iwl_mvm_link_sel_data *a,
+                                  const struct iwl_mvm_link_sel_data *b,
+                                  u8 *primary_id)
+{
+       struct ieee80211_bss_conf *primary_conf;
+       struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy;
+       unsigned int primary_load;
+
+       lockdep_assert_wiphy(wiphy);
+
+       /* a is always primary, b is always secondary */
+       if (b->grade > a->grade)
+               swap(a, b);
+
+       *primary_id = a->link_id;
+
+       if (!iwl_mvm_mld_valid_link_pair(vif, a, b))
+               return 0;
+
+       primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]);
+
+       if (WARN_ON_ONCE(!primary_conf))
+               return 0;
+
+       primary_load = iwl_mvm_get_chan_load(primary_conf);
+
+       return a->grade +
+               ((b->grade * primary_load) / SCALE_FACTOR);
+}
 
-void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                             bool valid_links_changed)
+void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif)
 {
        struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
-       unsigned long usable_links = ieee80211_vif_usable_links(vif);
+       struct iwl_mvm_link_sel_data *best_link;
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        u32 max_active_links = iwl_mvm_max_active_links(mvm, vif);
-       u16 new_active_links;
-       u8 n_data, i, j;
+       u16 usable_links = ieee80211_vif_usable_links(vif);
+       u8 best, primary_link, best_in_pair, n_data;
+       u16 max_esr_grade = 0, new_active_links;
+
+       lockdep_assert_wiphy(mvm->hw->wiphy);
+
+       if (!mvmvif->authorized || !ieee80211_vif_is_mld(vif))
+               return;
 
        if (!IWL_MVM_AUTO_EML_ENABLE)
                return;
         */
        WARN_ON_ONCE(max_active_links > 2);
 
-       /* if only a single active link is supported, assume that the one
-        * selected by higher layer for connection establishment is the best.
-        */
-       if (max_active_links == 1 && !valid_links_changed)
-               return;
+       n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links,
+                                                &best);
 
-       /* If we are already using the maximal number of active links, don't do
-        * any change. This can later be optimized to pick a 'better' link pair.
-        */
-       if (hweight16(vif->active_links) == max_active_links)
+       if (WARN(!n_data, "Couldn't find a valid grade for any link!\n"))
                return;
 
-       if (!iwl_mvm_esr_allowed_on_vif(mvm, vif))
-               return;
+       best_link = &data[best];
+       primary_link = best_link->link_id;
+       new_active_links = BIT(best_link->link_id);
 
-       n_data = iwl_mvm_set_link_selection_data(vif, data, usable_links);
+       /* eSR is not supported/allowed, or only one usable link */
+       if (max_active_links == 1 || !iwl_mvm_esr_allowed_on_vif(mvm, vif) ||
+           n_data == 1)
+               goto set_active;
 
-       /* this is expected to be the current active link */
-       if (n_data == 1)
-               return;
+       for (u8 a = 0; a < n_data; a++)
+               for (u8 b = a + 1; b < n_data; b++) {
+                       u16 esr_grade = iwl_mvm_get_esr_grade(vif, &data[a],
+                                                             &data[b],
+                                                             &best_in_pair);
 
-       new_active_links = 0;
+                       if (esr_grade <= max_esr_grade)
+                               continue;
 
-       /* Assume that after association only a single link is active, thus,
-        * select only the 2nd link
-        */
-       if (!valid_links_changed) {
-               for (i = 0; i < n_data; i++) {
-                       if (data[i].active)
-                               break;
+                       max_esr_grade = esr_grade;
+                       primary_link = best_in_pair;
+                       new_active_links = BIT(data[a].link_id) |
+                                          BIT(data[b].link_id);
                }
 
-               if (WARN_ON_ONCE(i == n_data))
-                       return;
+       /* No valid pair was found, go with the best link */
+       if (hweight16(new_active_links) <= 1)
+               goto set_active;
 
-               for (j = 0; j < n_data; j++) {
-                       if (i == j)
-                               continue;
+       /* prefer single link over marginal eSR improvement */
+       if (best_link->grade * 110 / 100 >= max_esr_grade) {
+               primary_link = best_link->link_id;
+               new_active_links = BIT(best_link->link_id);
+       }
+set_active:
+       IWL_DEBUG_INFO(mvm, "Link selection result: 0x%x. Primary = %d\n",
+                      new_active_links, primary_link);
+       ieee80211_set_active_links_async(vif, new_active_links);
+       mvmvif->link_selection_res = new_active_links;
+       mvmvif->link_selection_primary = primary_link;
+}
 
-                       if (iwl_mvm_mld_valid_link_pair(vif, &data[i],
-                                                       &data[j]))
-                               break;
-               }
+u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
 
-               if (j != n_data)
-                       new_active_links = BIT(data[i].link_id) |
-                               BIT(data[j].link_id);
-       } else {
-               /* Try to find a valid link pair for EMLSR operation. If a pair
-                * is not found continue using the current active link.
-                */
-               for (i = 0; i < n_data; i++) {
-                       for (j = 0; j < n_data; j++) {
-                               if (i == j)
-                                       continue;
-
-                               if (iwl_mvm_mld_valid_link_pair(vif, &data[i],
-                                                               &data[j]))
-                                       break;
-                       }
-
-                       /* found a valid pair for EMLSR, use it */
-                       if (j != n_data) {
-                               new_active_links = BIT(data[i].link_id) |
-                                       BIT(data[j].link_id);
-                               break;
-                       }
-               }
-       }
+       lockdep_assert_held(&mvmvif->mvm->mutex);
 
-       if (!new_active_links)
-               return;
+       if (!ieee80211_vif_is_mld(vif))
+               return 0;
+
+       /* In AP mode, there is no primary link */
+       if (vif->type == NL80211_IFTYPE_AP)
+               return __ffs(vif->active_links);
+
+       if (mvmvif->esr_active &&
+           !WARN_ON(!(BIT(mvmvif->primary_link) & vif->active_links)))
+               return mvmvif->primary_link;
 
-       if (vif->active_links != new_active_links)
-               ieee80211_set_active_links_async(vif, new_active_links);
+       return __ffs(vif->active_links);
 }
 
        iwl_mvm_scan_stop(mvm, IWL_MVM_SCAN_INT_MLO, false);
        mutex_unlock(&mvm->mutex);
 
+       wiphy_work_flush(mvm->hw->wiphy, &mvm->async_handlers_wiphy_wk);
        flush_work(&mvm->async_handlers_wk);
        flush_work(&mvm->add_stream_wk);
 
                WARN_ON(iwl_mvm_enable_beacon_filter(mvm, vif));
 
                mvmvif->authorized = 1;
+               mvmvif->link_selection_res = 0;
+               mvmvif->link_selection_primary =
+                       vif->active_links ? __ffs(vif->active_links) : 0;
 
                callbacks->mac_ctxt_changed(mvm, vif, false);
                iwl_mvm_mei_host_associated(mvm, vif, mvm_sta);
                iwl_mvm_bt_coex_update_vif_esr(mvm, vif);
 
                /* when client is authorized (AP station marked as such),
-                * try to enable more links
+                * try to enable the best link(s).
                 */
                if (vif->type == NL80211_IFTYPE_STATION &&
                    !test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))
-                       iwl_mvm_mld_select_links(mvm, vif, false);
+                       iwl_mvm_select_links(mvm, vif);
        }
 
        mvm_sta->authorized = true;
                 * time.
                 */
                mvmvif->authorized = 0;
+               mvmvif->link_selection_res = 0;
 
                /* disable beacon filtering */
                iwl_mvm_disable_beacon_filter(mvm, vif);
 
                link->phy_ctxt->rlc_disabled = true;
        }
 
+       if (vif->active_links == mvmvif->link_selection_res &&
+           !WARN_ON(!(vif->active_links & BIT(mvmvif->link_selection_primary))))
+               mvmvif->primary_link = mvmvif->link_selection_primary;
+       else
+               mvmvif->primary_link = __ffs(vif->active_links);
+
        return ret;
 }
 
        if (ret)
                IWL_ERR(mvm, "failed to update MAC %pM\n", vif->addr);
 
-       if (changes & BSS_CHANGED_MLD_VALID_LINKS)
-               iwl_mvm_mld_select_links(mvm, vif, true);
-
        memcpy(mvmvif->link[link_conf->link_id]->bssid, link_conf->bssid,
               ETH_ALEN);
 
        if (new_links == 0) {
                mvmvif->link[0] = &mvmvif->deflink;
                err = iwl_mvm_add_link(mvm, vif, &vif->bss_conf);
+               if (err == 0)
+                       mvmvif->primary_link = 0;
+       } else if (!(new_links & BIT(mvmvif->primary_link))) {
+               /*
+                * Ensure we always have a valid primary_link, the real
+                * decision happens later when PHY is activated.
+                */
+               mvmvif->primary_link = BIT(__ffs(new_links));
        }
 
 out_err:
 {
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        bool enable = !mvmvif->esr_disable_reason;
-       int link_id;
+       u16 new_active_links;
 
        /* Nothing to do */
        if (mvmvif->esr_active == enable)
                return;
 
-       if (enable) {
-               /* Try to re-enable eSR */
-               iwl_mvm_mld_select_links(mvm, vif, false);
+       /* The next link selection will enter eSR if possible */
+       if (enable)
                return;
-       }
 
        /*
         * Find the primary link, as we want to switch to it and drop the
         * secondary one.
         */
-       link_id = iwl_mvm_mld_get_primary_link(mvm, vif, vif->active_links);
-       WARN_ON(link_id < 0);
-
-       ieee80211_set_active_links_async(vif,
-                                        vif->active_links & BIT(link_id));
+       new_active_links = BIT(iwl_mvm_get_primary_link(vif));
+       ieee80211_set_active_links_async(vif, new_active_links);
 }
 
 bool iwl_mvm_esr_allowed_on_vif(struct iwl_mvm *mvm,
                                  unsigned long desired_links)
 {
        struct iwl_mvm_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS];
-       u8 n_data;
+       u8 best_link, n_data;
 
        if (!iwl_mvm_esr_allowed_on_vif(mvm, vif))
                return false;
 
-       n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links);
+       n_data = iwl_mvm_set_link_selection_data(vif, data, desired_links,
+                                                &best_link);
 
        if (n_data != 2)
                return false;
 
  * @esr_active: indicates eSR mode is active
  * @esr_disable_reason: a bitmap of enum iwl_mvm_esr_disable_reason
  * @pm_enabled: indicates powersave is enabled
+ * @link_selection_res: bitmap of active links as it was decided in the last
+ *     link selection. Valid only for a MLO vif after assoc. 0 if there wasn't
+ *     any link selection yet.
+ * @link_selection_primary: primary link selected by link selection
+ * @primary_link: primary link in eSR. Valid only for an associated MLD vif,
+ *     and in eSR mode. Valid only for a STA.
  */
 struct iwl_mvm_vif {
        struct iwl_mvm *mvm;
                struct ieee80211_key_conf __rcu *keys[2];
        } bcn_prot;
 
+       u16 link_selection_res;
+       u8 link_selection_primary;
+       u8 primary_link;
        struct iwl_mvm_vif_link_info deflink;
        struct iwl_mvm_vif_link_info *link[IEEE80211_MLD_MAX_NUM_LINKS];
 };
 int iwl_mvm_disable_link(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
                         struct ieee80211_bss_conf *link_conf);
 
-void iwl_mvm_mld_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif,
-                             bool valid_links_changed);
-int iwl_mvm_mld_get_primary_link(struct iwl_mvm *mvm,
-                                struct ieee80211_vif *vif,
-                                unsigned long usable_links);
+void iwl_mvm_select_links(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
+u8 iwl_mvm_get_primary_link(struct ieee80211_vif *vif);
+
+#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS)
+unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
+#endif
+
 struct iwl_mvm_link_sel_data {
        u8 link_id;
        enum nl80211_band band;
-       enum nl80211_chan_width width;
-       bool active;
+       u16 grade;
 };
 
 u8 iwl_mvm_set_link_selection_data(struct ieee80211_vif *vif,
                                   struct iwl_mvm_link_sel_data *data,
-                                  unsigned long usable_links);
+                                  unsigned long usable_links,
+                                  u8 *best_link_idx);
 bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif,
-                                struct iwl_mvm_link_sel_data *a,
-                                struct iwl_mvm_link_sel_data *b);
+                                const struct iwl_mvm_link_sel_data *a,
+                                const struct iwl_mvm_link_sel_data *b);
+
 /* AP and IBSS */
 bool iwl_mvm_start_ap_ibss_common(struct ieee80211_hw *hw,
                                  struct ieee80211_vif *vif, int *ret);
                          struct ieee80211_vif *vif,
                          struct ieee80211_sta *sta,
                          struct ieee80211_key_conf *keyconf);
-unsigned int iwl_mvm_get_link_grade(struct ieee80211_bss_conf *link_conf);
 
 bool iwl_rfi_supported(struct iwl_mvm *mvm);
 int iwl_rfi_send_config_cmd(struct iwl_mvm *mvm,
 
                           iwl_mvm_rx_scan_match_found,
                           RX_HANDLER_SYNC),
        RX_HANDLER(SCAN_COMPLETE_UMAC, iwl_mvm_rx_umac_scan_complete_notif,
-                  RX_HANDLER_ASYNC_LOCKED, struct iwl_umac_scan_complete),
+                  RX_HANDLER_ASYNC_LOCKED_WIPHY,
+                  struct iwl_umac_scan_complete),
        RX_HANDLER(SCAN_ITERATION_COMPLETE_UMAC,
                   iwl_mvm_rx_umac_scan_iter_complete_notif, RX_HANDLER_SYNC,
                   struct iwl_umac_scan_iter_complete_notif),
 
 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
 /*
- * Copyright (C) 2012-2014, 2018-2023 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2024 Intel Corporation
  * Copyright (C) 2013-2015 Intel Mobile Communications GmbH
  * Copyright (C) 2016-2017 Intel Deutschland GmbH
  */
        return ret;
 }
 
+static void iwl_mvm_find_link_selection_vif(void *_data, u8 *mac,
+                                           struct ieee80211_vif *vif)
+{
+       struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
+
+       if (ieee80211_vif_is_mld(vif) && mvmvif->authorized)
+               iwl_mvm_select_links(mvmvif->mvm, vif);
+}
+
+static void iwl_mvm_post_scan_link_selection(struct iwl_mvm *mvm)
+{
+       ieee80211_iterate_active_interfaces(mvm->hw,
+                                           IEEE80211_IFACE_ITER_NORMAL,
+                                           iwl_mvm_find_link_selection_vif,
+                                           NULL);
+}
+
 void iwl_mvm_rx_umac_scan_complete_notif(struct iwl_mvm *mvm,
                                         struct iwl_rx_cmd_buffer *rxb)
 {
                mvm->last_ebs_successful = false;
 
        mvm->scan_uid_status[uid] = 0;
+
+       if (notif->status == IWL_SCAN_OFFLOAD_COMPLETED)
+               iwl_mvm_post_scan_link_selection(mvm);
 }
 
 void iwl_mvm_rx_umac_scan_iter_complete_notif(struct iwl_mvm *mvm,
 
 };
 
 kunit_test_suite(link_grading);
+
+static const struct valid_link_pair_case {
+       const char *desc;
+       u32 esr_disable_reason;
+       enum nl80211_band band_a;
+       enum nl80211_band band_b;
+       bool valid;
+} valid_link_pair_cases[] = {
+       {
+               .desc = "HB + UHB, valid.",
+               .band_a = NL80211_BAND_5GHZ,
+               .band_b = NL80211_BAND_6GHZ,
+               .valid = true,
+       },
+       {
+               .desc = "LB + HB, no BT.",
+               .band_a = NL80211_BAND_2GHZ,
+               .band_b = NL80211_BAND_5GHZ,
+               .valid = true,
+       },
+       {
+               .desc = "LB + HB, with BT.",
+               .esr_disable_reason = 0x1,
+               .band_a = NL80211_BAND_2GHZ,
+               .band_b = NL80211_BAND_5GHZ,
+               .valid = false,
+       },
+       {
+               .desc = "Same band",
+               .band_a = NL80211_BAND_2GHZ,
+               .band_b = NL80211_BAND_2GHZ,
+               .valid = false,
+       },
+};
+
+KUNIT_ARRAY_PARAM_DESC(valid_link_pair, valid_link_pair_cases, desc)
+
+static void test_valid_link_pair(struct kunit *test)
+{
+       const struct valid_link_pair_case *params = test->param_value;
+       size_t vif_size = sizeof(struct ieee80211_vif) +
+               sizeof(struct iwl_mvm_vif);
+       struct ieee80211_vif *vif = kunit_kzalloc(test, vif_size, GFP_KERNEL);
+       struct iwl_mvm_link_sel_data link_a = {
+               .band = params->band_a,
+       };
+       struct iwl_mvm_link_sel_data link_b = {
+               .band = params->band_b,
+       };
+       bool result;
+
+       KUNIT_ASSERT_NOT_NULL(test, vif);
+
+       iwl_mvm_vif_from_mac80211(vif)->esr_disable_reason =
+               params->esr_disable_reason;
+
+       result = iwl_mvm_mld_valid_link_pair(vif, &link_a, &link_b);
+
+       KUNIT_EXPECT_EQ(test, result, params->valid);
+
+       kunit_kfree(test, vif);
+}
+
+static struct kunit_case valid_link_pair_test_cases[] = {
+       KUNIT_CASE_PARAM(test_valid_link_pair, valid_link_pair_gen_params),
+       {},
+};
+
+static struct kunit_suite valid_link_pair = {
+       .name = "iwlmvm-valid-link-pair",
+       .test_cases = valid_link_pair_test_cases,
+};
+
+kunit_test_suite(valid_link_pair);