]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
wifi: iwlwifi: mld: add NAN link management
authorJohannes Berg <johannes.berg@intel.com>
Sun, 10 May 2026 20:48:30 +0000 (23:48 +0300)
committerMiri Korenblit <miriam.rachel.korenblit@intel.com>
Tue, 26 May 2026 12:17:09 +0000 (15:17 +0300)
The firmware requires links for NAN which mac80211 doesn't use,
so introduce a new NAN link data structure that the driver has
for itself only, and handle the link command sending code for
NAN using this data structure, most of the bss_conf data isn't
used for NAN anyway, so those structures aren't useful.

With that, add, activate, deactivate or remove links depending
on the local NAN schedule updates.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Co-developed-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20260510234534.39ee3533ef30.I81ff6dc02e912396040f922e21888fd94b3c26e6@changeid
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
drivers/net/wireless/intel/iwlwifi/mld/iface.c
drivers/net/wireless/intel/iwlwifi/mld/iface.h
drivers/net/wireless/intel/iwlwifi/mld/link.c
drivers/net/wireless/intel/iwlwifi/mld/link.h
drivers/net/wireless/intel/iwlwifi/mld/mld.h
drivers/net/wireless/intel/iwlwifi/mld/nan.c
drivers/net/wireless/intel/iwlwifi/mld/nan.h
drivers/net/wireless/intel/iwlwifi/mld/rx.c
drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c

index 150ad095e0ae2dda40f427cc353d20227f5f3fc0..5fc3f67294553c8dca55bf9014a559799993c6ed 100644 (file)
@@ -5,6 +5,7 @@
 #include <net/cfg80211.h>
 
 #include "iface.h"
+#include "nan.h"
 #include "hcmd.h"
 #include "key.h"
 #include "mlo.h"
@@ -55,8 +56,12 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
 
        ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
 
-       if (vif->type == NL80211_IFTYPE_NAN)
+       if (vif->type == NL80211_IFTYPE_NAN) {
                mld_vif->nan.mac_added = false;
+               /* Clean up NAN links */
+               for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++)
+                       iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]);
+       }
 
        CLEANUP_STRUCT(mld_vif);
 }
@@ -515,6 +520,14 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
                wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk,
                                        iwl_mld_mlo_scan_start_wk);
        }
+
+       if (vif->type == NL80211_IFTYPE_NAN) {
+               for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++) {
+                       memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i]));
+                       mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID;
+               }
+       }
+
        iwl_mld_init_internal_sta(&mld_vif->aux_sta);
 }
 
index 1ac14996985c00828c264667122822e9c855c504..ce4f8ca885cfae1d5aab6cf7fc95c35b36e1edaa 100644 (file)
@@ -8,6 +8,7 @@
 #include <net/mac80211.h>
 
 #include "link.h"
+#include "nan.h"
 #include "session-protect.h"
 #include "d3.h"
 #include "fw/api/time-event.h"
@@ -153,6 +154,7 @@ struct iwl_mld_emlsr {
  * @aux_sta: station used for remain on channel. Used in P2P device.
  * @mlo_scan_start_wk: worker to start a deferred MLO scan
  * @nan: NAN parameters
+ * @nan.links: NAN links for FW (indexed by FW link ID)
  * @nan.mac_added: track whether or not the MAC was added to FW
  */
 struct iwl_mld_vif {
@@ -179,6 +181,7 @@ struct iwl_mld_vif {
 
        struct {
                /* use only with wiphy protection */
+               struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS];
                bool mac_added;
        } nan;
 
@@ -242,6 +245,12 @@ static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
             link_id++)                                                 \
                if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id)))
 
+#define for_each_mld_nan_valid_link(mld_vif, nan_link)                 \
+       for (nan_link = &(mld_vif)->nan.links[0];                       \
+            nan_link < &(mld_vif)->nan.links[ARRAY_SIZE((mld_vif)->nan.links)]; \
+            nan_link++)                                                \
+               if (nan_link->fw_id != FW_CTXT_ID_INVALID)
+
 /* Retrieve pointer to mld link from mac80211 structures */
 static inline struct iwl_mld_link *
 iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf)
index b66e84d2365fb00a48d52fd39d2d7f5d2ad6adc9..9e40b334ee1fab544da1bf32a399fc6f04d6868b 100644 (file)
 #include "fw/api/context.h"
 #include "fw/dbg.h"
 
-static int iwl_mld_send_link_cmd(struct iwl_mld *mld,
-                                struct iwl_link_config_cmd *cmd,
-                                enum iwl_ctxt_action action)
+int iwl_mld_send_link_cmd(struct iwl_mld *mld,
+                         struct iwl_link_config_cmd *cmd,
+                         enum iwl_ctxt_action action)
 {
        int ret;
 
        lockdep_assert_wiphy(mld->wiphy);
 
+       if (WARN_ON_ONCE(cmd->link_id == cpu_to_le32(FW_CTXT_ID_INVALID)))
+               return -EINVAL;
+
        cmd->action = cpu_to_le32(action);
        ret = iwl_mld_send_cmd_pdu(mld,
                                   WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
@@ -437,7 +440,8 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
        iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
 }
 
-static IWL_MLD_ALLOC_FN(link, bss_conf)
+IWL_MLD_ALLOC_FN(link, bss_conf)
+EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_link_fw_id);
 
 /* Constructor function for struct iwl_mld_link */
 static int
index ca691259fc5e6283571e9f9eeee7e35c7b7c9e52..84d9a24134a82295e3a0da46c64547a641f589e0 100644 (file)
@@ -99,6 +99,13 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link)
 /* Convert a percentage from [0,100] to [0,255] */
 #define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100)
 
+int iwl_mld_allocate_link_fw_id(struct iwl_mld *mld, u8 *fw_id,
+                               struct ieee80211_bss_conf *mac80211_ptr);
+
+int iwl_mld_send_link_cmd(struct iwl_mld *mld,
+                         struct iwl_link_config_cmd *cmd,
+                         enum iwl_ctxt_action action);
+
 int iwl_mld_add_link(struct iwl_mld *mld,
                     struct ieee80211_bss_conf *bss_conf);
 void iwl_mld_remove_link(struct iwl_mld *mld,
index 606cb64f8ea4fd63748623081007fb7d9f9fc32b..69da3c346394b6fb44f5eca5b73def30bf7a270f 100644 (file)
@@ -558,12 +558,18 @@ iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld,                                     \
 static inline struct ieee80211_bss_conf *
 iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
 {
+       struct ieee80211_bss_conf *link;
+
        if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links,
                         "Invalid fw_link_id: %d\n", fw_link_id))
                return NULL;
 
-       return wiphy_dereference(mld->wiphy,
+       link = wiphy_dereference(mld->wiphy,
                                 mld->fw_id_to_bss_conf[fw_link_id]);
+       if (IS_ERR(link))
+               return NULL;
+
+       return link;
 }
 
 #define MSEC_TO_TU(_msec)      ((_msec) * 1000 / 1024)
index 96e79ba5234a4dc8dd4517d4e999a057442cacdb..6ea11b66a5453242c66ad40e32a165a2276b5877 100644 (file)
@@ -5,8 +5,11 @@
 
 #include "mld.h"
 #include "iface.h"
+#include "link.h"
 #include "mlo.h"
 #include "fw/api/mac-cfg.h"
+#include "fw/api/mac.h"
+#include "fw/api/rs.h"
 
 #define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
 #define IWL_NAN_RSSI_CLOSE 55
@@ -297,24 +300,302 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
        cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
 }
 
+static void iwl_mld_nan_fill_rates(struct iwl_link_config_cmd *cmd)
+{
+       u32 ofdm = 0;
+
+       /* All OFDM rates - NAN uses OFDM only, no CCK */
+       ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(9) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(18) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(36) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(48) >> IWL_FIRST_OFDM_RATE;
+       ofdm |= IWL_RATE_BIT_MSK(54) >> IWL_FIRST_OFDM_RATE;
+
+       cmd->ofdm_rates = cpu_to_le32(ofdm);
+       cmd->short_slot = cpu_to_le32(1);
+}
+
+static void iwl_mld_nan_fill_qos(struct iwl_ac_qos *ac, __le32 *qos_flags)
+{
+       /* AC_BK: CWmin=15, CWmax=1023, AIFSN=7, TXOP=0 */
+       ac[AC_BK].cw_min = cpu_to_le16(15);
+       ac[AC_BK].cw_max = cpu_to_le16(1023);
+       ac[AC_BK].aifsn = 7;
+       ac[AC_BK].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BK);
+       ac[AC_BK].edca_txop = 0;
+
+       /* AC_BE: CWmin=15, CWmax=1023, AIFSN=3, TXOP=0 */
+       ac[AC_BE].cw_min = cpu_to_le16(15);
+       ac[AC_BE].cw_max = cpu_to_le16(1023);
+       ac[AC_BE].aifsn = 3;
+       ac[AC_BE].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BE);
+       ac[AC_BE].edca_txop = 0;
+
+       /* AC_VI: CWmin=7, CWmax=15, AIFSN=2, TXOP=3008us */
+       ac[AC_VI].cw_min = cpu_to_le16(7);
+       ac[AC_VI].cw_max = cpu_to_le16(15);
+       ac[AC_VI].aifsn = 2;
+       ac[AC_VI].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VI);
+       ac[AC_VI].edca_txop = cpu_to_le16(3008);
+
+       /* AC_VO: CWmin=3, CWmax=7, AIFSN=2, TXOP=1504us */
+       ac[AC_VO].cw_min = cpu_to_le16(3);
+       ac[AC_VO].cw_max = cpu_to_le16(7);
+       ac[AC_VO].aifsn = 2;
+       ac[AC_VO].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VO);
+       ac[AC_VO].edca_txop = cpu_to_le16(1504);
+
+       *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
+}
+
+static void
+iwl_mld_nan_link_prep_cmd(struct iwl_mld *mld,
+                         struct iwl_mld_nan_link *nan_link,
+                         struct iwl_link_config_cmd *cmd,
+                         u32 modify_flags)
+{
+       struct ieee80211_vif *vif = mld->nan_device_vif;
+       struct iwl_mld_vif *mld_vif;
+
+       if (WARN_ON_ONCE(!vif))
+               return;
+
+       mld_vif = iwl_mld_vif_from_mac80211(vif);
+
+       memset(cmd, 0, sizeof(*cmd));
+
+       if (!nan_link->chanctx) {
+               cmd->phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
+       } else {
+               struct iwl_mld_phy *mld_phy;
+
+               mld_phy = iwl_mld_phy_from_mac80211(nan_link->chanctx);
+               cmd->phy_id = cpu_to_le32(mld_phy->fw_id);
+       }
+
+       if (modify_flags & LINK_CONTEXT_MODIFY_RATES_INFO)
+               iwl_mld_nan_fill_rates(cmd);
+
+       if (modify_flags & LINK_CONTEXT_MODIFY_QOS_PARAMS)
+               iwl_mld_nan_fill_qos(cmd->ac, &cmd->qos_flags);
+
+       cmd->link_id = cpu_to_le32(nan_link->fw_id);
+       cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
+       cmd->active = cpu_to_le32(nan_link->active);
+
+       ether_addr_copy(cmd->local_link_addr, vif->addr);
+
+       cmd->modify_mask = cpu_to_le32(modify_flags);
+}
+
+static struct iwl_mld_nan_link *
+iwl_mld_nan_link_add(struct iwl_mld *mld,
+                    struct iwl_mld_vif *mld_vif,
+                    struct ieee80211_chanctx_conf *chanctx)
+{
+       struct iwl_mld_nan_link *nan_link;
+       struct iwl_link_config_cmd cmd;
+       u8 fw_id;
+       int ret;
+
+       ret = iwl_mld_allocate_link_fw_id(mld, &fw_id, ERR_PTR(-ENODEV));
+       if (ret < 0)
+               return NULL;
+
+       nan_link = &mld_vif->nan.links[fw_id];
+
+       if (WARN_ON_ONCE(nan_link->fw_id != FW_CTXT_ID_INVALID))
+               goto err;
+
+       nan_link->fw_id = fw_id;
+       nan_link->chanctx = chanctx;
+
+       iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
+                                 LINK_CONTEXT_MODIFY_RATES_INFO |
+                                 LINK_CONTEXT_MODIFY_QOS_PARAMS);
+
+       ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
+       if (ret) {
+               nan_link->fw_id = FW_CTXT_ID_INVALID;
+               nan_link->chanctx = NULL;
+               goto err;
+       }
+
+       return nan_link;
+err:
+       RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
+       return NULL;
+}
+
+static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
+                                      struct iwl_mld_nan_link *nan_link,
+                                      bool active)
+{
+       struct iwl_link_config_cmd cmd;
+       int ret;
+
+       if (nan_link->active == active)
+               return 0;
+
+       nan_link->active = active;
+
+       iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
+                                 LINK_CONTEXT_MODIFY_ACTIVE);
+
+       ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
+       if (ret) {
+               nan_link->active = !nan_link->active;
+               return ret;
+       }
+
+       if (!active)
+               nan_link->chanctx = NULL;
+
+       return 0;
+}
+
+static void iwl_mld_nan_link_remove(struct iwl_mld *mld,
+                                   struct iwl_mld_nan_link *nan_link)
+{
+       struct iwl_link_config_cmd cmd = {
+               .link_id = cpu_to_le32(nan_link->fw_id),
+               .phy_id = cpu_to_le32(FW_CTXT_ID_INVALID),
+       };
+
+       if (WARN_ON_ONCE(nan_link->fw_id == FW_CTXT_ID_INVALID))
+               return;
+
+       iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
+
+       RCU_INIT_POINTER(mld->fw_id_to_bss_conf[nan_link->fw_id], NULL);
+       nan_link->fw_id = FW_CTXT_ID_INVALID;
+       nan_link->active = false;
+       nan_link->chanctx = NULL;
+}
+
+static bool iwl_mld_nan_have_links(struct iwl_mld_vif *mld_vif)
+{
+       struct iwl_mld_nan_link *nan_link;
+
+       for_each_mld_nan_valid_link(mld_vif, nan_link)
+               return true;
+
+       return false;
+}
+
+static struct iwl_mld_nan_link *
+iwl_mld_nan_find_link(struct iwl_mld_vif *mld_vif,
+                     struct ieee80211_chanctx_conf *chanctx)
+{
+       struct iwl_mld_nan_link *nan_link;
+
+       for_each_mld_nan_valid_link(mld_vif, nan_link) {
+               if (nan_link->chanctx == chanctx)
+                       return nan_link;
+       }
+
+       return NULL;
+}
+
 void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
                                 struct ieee80211_vif *vif,
                                 u64 changes)
 {
-       struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
        struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
-       bool has_sched = memchr_inv(sched_cfg->schedule, 0,
-                                   sizeof(sched_cfg->schedule));
-
-       lockdep_assert_wiphy(mld->wiphy);
+       bool previously_empty_schedule = !iwl_mld_nan_have_links(mld_vif);
+       struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
+       struct iwl_mld_nan_link *links[ARRAY_SIZE(sched_cfg->channels)] = {};
+       bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {};
+       struct iwl_mld_nan_link *nan_link;
+       bool empty_schedule = true;
+       int ret;
 
        if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED))
                return;
 
-       if (has_sched && !mld_vif->nan.mac_added) {
-               if (iwl_mld_add_nan_vif(mld, vif))
-                       IWL_ERR(mld, "Failed to add NAN vif\n");
-       } else if (!has_sched && mld_vif->nan.mac_added) {
-               iwl_mld_rm_vif(mld, vif);
+       for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+               if (!sched_cfg->channels[i].chanreq.oper.chan)
+                       continue;
+               empty_schedule = false;
+               break;
+       }
+
+       /* add the MAC if needed (before adding links) */
+       if (!empty_schedule && previously_empty_schedule) {
+               WARN_ON(mld_vif->nan.mac_added);
+               ret = iwl_mld_add_nan_vif(mld, vif);
+
+               if (ret) {
+                       IWL_ERR(mld, "NAN: failed to add MAC (%d)\n", ret);
+                       return;
+               }
+       }
+
+       if (!mld_vif->nan.mac_added) {
+               /* nothing to do */
+               return;
+       }
+
+       /* find links we can keep (same chanctx/PHY) */
+       for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+               struct ieee80211_chanctx_conf *chanctx;
+               struct iwl_mld_nan_link *link;
+
+               chanctx = sched_cfg->channels[i].chanctx_conf;
+               /* ULW */
+               if (!chanctx)
+                       continue;
+
+               link = iwl_mld_nan_find_link(mld_vif, chanctx);
+               links[i] = link;
+               if (link)
+                       link_used[link->fw_id] = true;
+       }
+
+       /* add/reassign links for new channels */
+       for (int i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
+               struct ieee80211_chanctx_conf *chanctx;
+
+               /* already have an existing active link */
+               if (links[i])
+                       continue;
+
+               chanctx = sched_cfg->channels[i].chanctx_conf;
+               /* ULW or unused slot */
+               if (!chanctx)
+                       continue;
+
+               /*
+                * if this fails we still update the schedule, but
+                * without a valid link we'll always ULW it
+                */
+               links[i] = iwl_mld_nan_link_add(mld, mld_vif, chanctx);
+
+               /* we have a link, activate it */
+               if (links[i]) {
+                       link_used[links[i]->fw_id] = true;
+                       iwl_mld_nan_link_set_active(mld, links[i], true);
+               }
+       }
+
+       /* delete unused links */
+       for_each_mld_nan_valid_link(mld_vif, nan_link) {
+               if (!link_used[nan_link->fw_id]) {
+                       iwl_mld_nan_link_set_active(mld, nan_link, false);
+                       iwl_mld_nan_link_remove(mld, nan_link);
+               }
+       }
+
+       /* remove MAC if needed */
+       if (!previously_empty_schedule && empty_schedule) {
+               /* must have been added */
+               WARN_ON_ONCE(!mld_vif->nan.mac_added);
+
+               /* mac80211 should reconfigure same state */
+               if (!WARN_ON_ONCE(mld->fw_status.in_hw_restart))
+                       iwl_mld_rm_vif(mld, vif);
        }
 }
index 9487155cf6b38706e723906ef950bb6dc444746a..933e16c3c2742de679b89952f6ccb1e8270c3550 100644 (file)
@@ -7,6 +7,25 @@
 #include <net/cfg80211.h>
 #include <linux/etherdevice.h>
 
+/**
+ * struct iwl_mld_nan_link - struct representing a NAN link
+ * @chanctx: the channel context
+ * @active: indicates the NAN link is currently active
+ * @fw_id: FW link ID
+ */
+struct iwl_mld_nan_link {
+       struct ieee80211_chanctx_conf *chanctx;
+       bool active;
+       u8 fw_id;
+};
+
+/* Cleanup function for struct iwl_mld_nan_link, will be called in restart */
+static inline void iwl_mld_cleanup_nan_link(struct iwl_mld_nan_link *nan_link)
+{
+       memset(nan_link, 0, sizeof(*nan_link));
+       nan_link->fw_id = FW_CTXT_ID_INVALID;
+}
+
 bool iwl_mld_nan_supported(struct iwl_mld *mld);
 int iwl_mld_start_nan(struct ieee80211_hw *hw,
                      struct ieee80211_vif *vif,
index a2e586c6ea673b4b8c9ed12b8a3eb8d239625190..b270cf87824d914347052aa00e8ca9ad327f33e5 100644 (file)
@@ -158,7 +158,7 @@ static bool iwl_mld_used_average_energy(struct iwl_mld *mld, int link_id,
        guard(rcu)();
 
        link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]);
-       if (!link_conf)
+       if (IS_ERR_OR_NULL(link_conf))
                return false;
 
        mld_link = iwl_mld_link_from_mac80211(link_conf);
index dce7472701672a63b82a3f33dcef52608aa20e33..0cdbbb86dbd945dcd7d154b08873248607bf515c 100644 (file)
@@ -68,8 +68,6 @@ int iwlmld_kunit_test_init(struct kunit *test)
        return 0;
 }
 
-static IWL_MLD_ALLOC_FN(link, bss_conf)
-
 static void iwlmld_kunit_init_link(struct ieee80211_vif *vif,
                                   struct ieee80211_bss_conf *link,
                                   struct iwl_mld_link *mld_link, int link_id)