[PATCH RFC 1/2] wifi: mac80211: Add the capability to offload MLO link reconfiguration countdown in the NIC fw

Lorenzo Bianconi lorenzo at kernel.org
Wed Feb 11 15:38:11 PST 2026


Introduce the capability to offload the countdown for MLO link
reconfiguration IE if supported by the underlay driver/firmware.
MLO link reconfiguration IE is added to beacons/probe replies sent by
the MLD AP if the specified link is going to be removed.
The driver is supposed to set the WIPHY_FLAG_MLO_RECONF_ADV_OFFLOAD flag
to notify mac80211/hostapd it supports this capability in hw/fw.
Moreover, the driver is supposed to generate an event to notify
mac80211/hostapd when the countdown is completed calling
ieee80211_mlo_reconf_complete_notify() utility routine.
mac80211/cfg80211 will generate a
NL80211_CMD_MLO_RECONF_ADV_OFFLOAD_EVENT event for hostapd.

Signed-off-by: Lorenzo Bianconi <lorenzo at kernel.org>
---
 include/net/cfg80211.h       | 13 ++++++++++
 include/net/mac80211.h       | 13 ++++++++++
 include/uapi/linux/nl80211.h | 12 +++++++++
 net/mac80211/cfg.c           | 16 ++++++++++++
 net/mac80211/ieee80211_i.h   |  5 ++++
 net/mac80211/iface.c         | 14 ++++++++++
 net/wireless/nl80211.c       | 61 ++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 134 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index fc01de19c7981a906a5303899e7000d8193c60a8..b3ce45674e3004b592b95c4dc08ba53929f27908 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -5329,6 +5329,8 @@ struct cfg80211_ops {
  * @WIPHY_FLAG_SUPPORTS_NSTR_NONPRIMARY: support connection to non-primary link
  *	of an NSTR mobile AP MLD.
  * @WIPHY_FLAG_DISABLE_WEXT: disable wireless extensions for this device
+ * @WIPHY_FLAG_MLO_RECONF_ADV_OFFLOAD: The underly driver supports link
+ *	reconfiguration countdown in hw.
  */
 enum wiphy_flags {
 	WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK		= BIT(0),
@@ -5357,6 +5359,7 @@ enum wiphy_flags {
 	WIPHY_FLAG_HAS_CHANNEL_SWITCH		= BIT(23),
 	WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER	= BIT(24),
 	WIPHY_FLAG_CHANNEL_CHANGE_ON_BEACON     = BIT(25),
+	WIPHY_FLAG_MLO_RECONF_ADV_OFFLOAD	= BIT(26),
 };
 
 /**
@@ -10314,6 +10317,16 @@ struct cfg80211_mlo_reconf_done_data {
 void cfg80211_mlo_reconf_add_done(struct net_device *dev,
 				  struct cfg80211_mlo_reconf_done_data *data);
 
+/**
+ * cfg80211_mlo_reconf_complete_notify - Notify about MLO reconfiguration
+ * countdown completion.
+ * @dev: network device.
+ * @link_bitmap: bitmap representing the links the driver was announcing in
+ * the Reconfiguration Multi-Link element.
+ */
+void cfg80211_mlo_reconf_complete_notify(struct net_device *dev,
+					 u16 link_bitmap);
+
 /**
  * cfg80211_schedule_channels_check - schedule regulatory check if needed
  * @wdev: the wireless device to check
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7f9d96939a4ea715eea288a83aeb9a0aa863c8df..ced70e42c4a4db65c86bd8a43c8b42428663adf0 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7801,6 +7801,19 @@ void
 ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
 				      u64 color_bitmap, u8 link_id);
 
+
+/**
+ * ieee80211_mlo_reconf_complete_notify - notify userland about MLO
+ * link reconfiguration announcement completion.
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @link_bitmap: bitmap representing the links the driver was announcing in
+ * the Reconfiguration Multi-Link element.
+ *
+ */
+void ieee80211_mlo_reconf_complete_notify(struct ieee80211_vif *vif,
+					  u16 link_bitmap);
+
 /**
  * ieee80211_is_tx_data - check if frame is a data frame
  *
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index b63f718509060dd4b7b59e2d6cc2aad280acaa22..5c7c80d5f1e5addb78e40514db8b30bbf6041fd4 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1361,6 +1361,10 @@
  *	user space that the NAN new cluster has been joined. The cluster ID is
  *	indicated by %NL80211_ATTR_MAC.
  *
+ * @NL80211_CMD_MLO_RECONF_ADV_OFFLOAD_EVENT: This command is used to notify
+ *	user space the underlay driver has completed the MLO link
+ *	reconfiguration announcement countdown.
+ *
  * @NL80211_CMD_MAX: highest used command number
  * @__NL80211_CMD_AFTER_LAST: internal use
  */
@@ -1624,6 +1628,8 @@ enum nl80211_commands {
 	NL80211_CMD_NAN_NEXT_DW_NOTIFICATION,
 	NL80211_CMD_NAN_CLUSTER_JOINED,
 
+	NL80211_CMD_MLO_RECONF_ADV_OFFLOAD_EVENT,
+
 	/* add new commands above here */
 
 	/* used to define NL80211_CMD_MAX below */
@@ -2984,6 +2990,10 @@ enum nl80211_commands {
  *	this feature during association. This is a flag attribute.
  *	Currently only supported in mac80211 drivers.
  *
+ * @NL80211_ATTR_MLO_RECONF_ADV_OFFLOAD: Flag attribute to indicate user space
+ *	the driver supports link reconfiguration countdown in hw when a link is
+ *	removed.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3557,6 +3567,8 @@ enum nl80211_attrs {
 	NL80211_ATTR_UHR_CAPABILITY,
 	NL80211_ATTR_DISABLE_UHR,
 
+	NL80211_ATTR_MLO_RECONF_ADV_OFFLOAD,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 5d04d7d550b0b0af2d84148317aa152ad5647986..b20086160fd8741450e0f863d93cc2cd6964470d 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -5318,6 +5318,22 @@ ieee80211_obss_color_collision_notify(struct ieee80211_vif *vif,
 }
 EXPORT_SYMBOL_GPL(ieee80211_obss_color_collision_notify);
 
+void ieee80211_mlo_reconf_complete_notify(struct ieee80211_vif *vif,
+					  u16 link_bitmap)
+{
+	struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+	if (vif->type != NL80211_IFTYPE_AP)
+		return;
+
+	if (test_and_set_bit(IEEE80211_IF_AP_RECONF_LINKS, &sdata->u.ap.flags))
+		return;
+
+	sdata->u.ap.reconf_links = link_bitmap;
+	wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
+}
+EXPORT_SYMBOL_GPL(ieee80211_mlo_reconf_complete_notify);
+
 static int
 ieee80211_color_change(struct wiphy *wiphy, struct net_device *dev,
 		       struct cfg80211_color_change_settings *params)
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e60b814dd89e03a5b6dd7537375b49246e5b78a5..74bc6311e430a2374afe790a7aa5686f5a62c351 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -325,6 +325,8 @@ struct ps_data {
 	int sb_count; /* num short beacons til next long beacon */
 };
 
+#define IEEE80211_IF_AP_RECONF_LINKS	BIT(0)
+
 struct ieee80211_if_ap {
 	struct list_head vlans; /* write-protected with RTNL and local->mtx */
 
@@ -333,6 +335,9 @@ struct ieee80211_if_ap {
 
 	bool multicast_to_unicast;
 	bool active;
+
+	unsigned long flags;
+	u16 reconf_links;
 };
 
 struct ieee80211_if_vlan {
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 676b2a43c9f2f2de03f4dd3993bf5c0ba2de0d36..6cfee360b2e7bb08cf15be84a35a0825538725e3 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1776,6 +1776,17 @@ static void ieee80211_iface_process_status(struct ieee80211_sub_if_data *sdata,
 	}
 }
 
+static void ieee80211_ap_work(struct ieee80211_sub_if_data *sdata)
+{
+	if (!test_bit(IEEE80211_IF_AP_RECONF_LINKS, &sdata->u.ap.flags))
+		return;
+
+	cfg80211_mlo_reconf_complete_notify(sdata->dev,
+					    sdata->u.ap.reconf_links);
+	sdata->u.ap.reconf_links = 0;
+	clear_bit(IEEE80211_IF_AP_RECONF_LINKS, &sdata->u.ap.flags);
+}
+
 static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work)
 {
 	struct ieee80211_sub_if_data *sdata =
@@ -1817,6 +1828,9 @@ static void ieee80211_iface_work(struct wiphy *wiphy, struct wiphy_work *work)
 
 	/* then other type-dependent work */
 	switch (sdata->vif.type) {
+	case NL80211_IFTYPE_AP:
+		ieee80211_ap_work(sdata);
+		break;
 	case NL80211_IFTYPE_STATION:
 		ieee80211_sta_work(sdata);
 		break;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6e58b238a1f89323585607b05b25a954abc44ffe..fd670bfd61034a99f0537b2a529984661fb505e7 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -946,6 +946,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_UHR_CAPABILITY] =
 		NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255),
 	[NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
+	[NL80211_ATTR_MLO_RECONF_ADV_OFFLOAD] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -3338,6 +3339,9 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *rdev,
 				rdev->wiphy.hw_timestamp_max_peers))
 			goto nla_put_failure;
 
+		if (rdev->wiphy.flags & WIPHY_FLAG_MLO_RECONF_ADV_OFFLOAD)
+			nla_put_flag(msg, NL80211_ATTR_MLO_RECONF_ADV_OFFLOAD);
+
 		state->split_start++;
 		break;
 	case 17:
@@ -20066,6 +20070,63 @@ void cfg80211_links_removed(struct net_device *dev, u16 link_mask)
 }
 EXPORT_SYMBOL(cfg80211_links_removed);
 
+void cfg80211_mlo_reconf_complete_notify(struct net_device *dev,
+					 u16 link_bitmap)
+{
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct nlattr *links;
+	struct sk_buff *msg;
+	void *hdr;
+
+	if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP))
+		return;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0,
+			     NL80211_CMD_MLO_RECONF_ADV_OFFLOAD_EVENT);
+	if (!hdr)
+		goto nla_put_failure;
+
+	if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
+	    nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
+		goto nla_put_failure;
+
+	links = nla_nest_start(msg, NL80211_ATTR_MLO_LINKS);
+	if (!links)
+		goto nla_put_failure;
+
+	while (link_bitmap) {
+		int link_id = __ffs(link_bitmap);
+		struct nlattr *link;
+
+		link = nla_nest_start(msg, link_id + 1);
+		if (!link)
+			goto nla_put_failure;
+
+		if (nla_put_u8(msg, NL80211_ATTR_MLO_LINK_ID, link_id))
+			goto nla_put_failure;
+
+		nla_nest_end(msg, link);
+		link_bitmap &= ~(1 << link_id);
+	}
+
+	nla_nest_end(msg, links);
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+				NL80211_MCGRP_MLME, GFP_KERNEL);
+	return;
+
+nla_put_failure:
+	nlmsg_free(msg);
+}
+EXPORT_SYMBOL_GPL(cfg80211_mlo_reconf_complete_notify);
+
 void nl80211_mlo_reconf_add_done(struct net_device *dev,
 				 struct cfg80211_mlo_reconf_done_data *data)
 {

-- 
2.53.0




More information about the linux-arm-kernel mailing list