diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2e2be4fd2bb6..496a9d4956de 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -6142,7 +6142,11 @@ void wiphy_delayed_work_flush(struct wiphy *wiphy,
  *	unprotected beacon report
  * @links: array of %IEEE80211_MLD_MAX_NUM_LINKS elements containing @addr
  *	@ap and @client for each link
+ * @links.ap.bpcc: Bss param change count value for each link
+ * @links.ap.switch_count: CSA/BCCA count for each link
+ * @links.ap.critical_flag: Critical update flag for each link
  * @valid_links: bitmap describing what elements of @links are valid
+ * @critical_update: critical params updated on any wdev link
  */
 struct wireless_dev {
 	struct wiphy *wiphy;
@@ -6247,6 +6251,9 @@ struct wireless_dev {
 		u8 addr[ETH_ALEN] __aligned(2);
 		union {
 			struct {
+				u8 bpcc;
+				u8 switch_count;
+				bool critical_flag;
 				unsigned int beacon_interval;
 				struct cfg80211_chan_def chandef;
 			} ap;
@@ -6256,6 +6263,7 @@ struct wireless_dev {
 		};
 	} links[IEEE80211_MLD_MAX_NUM_LINKS];
 	u16 valid_links;
+	bool critical_update;
 };
 
 static inline const u8 *wdev_address(struct wireless_dev *wdev)
@@ -8340,6 +8348,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
  * @flags: flags, as defined in &enum nl80211_rxmgmt_flags
  * @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
  * @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @critical_update: critical params updated for the received frame
  */
 struct cfg80211_rx_info {
 	int freq;
@@ -8351,6 +8360,7 @@ struct cfg80211_rx_info {
 	u32 flags;
 	u64 rx_tstamp;
 	u64 ack_tstamp;
+	bool critical_update;
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index f23ecbdd84a2..02948c29fc27 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2856,6 +2856,15 @@ enum nl80211_commands {
  *	%NL80211_CMD_ASSOCIATE indicating the SPP A-MSDUs
  *	are used on this connection
  *
+ * @NL80211_ATTR_RXMGMT_CRITICAL_UPDATE: This is a nested attribute for driver
+ *	supporting critical update feature for AP MLD. When used with
+ *	%NL80211_CMD_FRAME it contains attribute defined in &enum nl80211_cu_attrs
+ *	to send critical update params for list of MLDs. Driver adds this attribute
+ *	only for probe, assoc and reassoc request frame. User-space can use these
+ *	params to update CU fields on corresponding response frame. This attribute
+ *	is needed only on beacon offload case and it is not needed on beacon
+ *	non-offload case since user space itself has these data.
+ *
  * @NUM_NL80211_ATTR: total number of nl80211_attrs available
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3401,6 +3410,7 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_ASSOC_SPP_AMSDU,
 
+	NL80211_ATTR_RXMGMT_CRITICAL_UPDATE,
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -6542,6 +6552,11 @@ enum nl80211_feature_flags {
  *	(signaling and payload protected) A-MSDUs and this shall be advertised
  *	in the RSNXE.
  *
+ * @NL80211_EXT_FEATURE_CRITICAL_UPDATE_OFFLOAD: User space sets critical
+ *	information only on impacted link through @NL80211_CMD_SET_BEACON.
+ *	Driver/Device handles synchronization among all the links and update
+ *	critical information on partner link beacon in case of MLD.
+ *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
  */
@@ -6617,6 +6632,7 @@ enum nl80211_ext_feature_index {
 	NL80211_EXT_FEATURE_OWE_OFFLOAD_AP,
 	NL80211_EXT_FEATURE_DFS_CONCURRENT,
 	NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT,
+	NL80211_EXT_FEATURE_CRITICAL_UPDATE_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -7987,4 +8003,92 @@ enum nl80211_ap_settings_flags {
 	NL80211_AP_SETTINGS_SA_QUERY_OFFLOAD_SUPPORT	= 1 << 1,
 };
 
+/*
+ * Critical update attribute length for a MLD list with two nested
+ * attributes. Each nla_nest_start() reserves four bytes.
+ */
+#define NL80211_CU_ATTR_MLDS_LEN	8
+
+/*
+ * Critical update attribute length for a particular MLD with two nested attributes.
+ * Each nla_nest_start() reserves four bytes and
+ * nla_put_u32(NL80211_CU_MLD_ATTR_IFINDEX) reserves eight bytes.
+ */
+
+#define NL80211_CU_ATTR_MLD_LEN		16
+
+/*
+ * Critical update attribute length for a particular link.
+ * Each nla_nest_start() reserves four bytes,
+ * nla_put_u8(NL80211_CU_MLD_LINK_ATTR_ID) reserves eight bytes,
+ * nla_put_flag(NL80211_CU_MLD_LINK_ATTR_CRITICAL_FLAG) reserves four bytes,
+ * nla_put_u8(NL80211_CU_MLD_LINK_ATTR_BPCC) reserves eight bytes and
+ * nla_put_u8(NL80211_CU_MLD_LINK_ATTR_SWITCH_COUNT) reserves eight bytes,
+ */
+#define NL80211_CU_ATTR_LINK_LEN	32
+
+/**
+ * nl80211_cu_attrs - critical update attributes
+ *
+ *
+ * @__NL80211_CU_ATTR_INVALID: invalid
+ * @NL80211_CU_ATTR_MLDS: nested attribute specifying list of MLDs,
+ * see &enum nl80211_cu_mld_attrs
+ * @__NL80211_CU_ATTR_LAST: internal use
+ * @NL80211_CU_ATTR_MAX: maximum critical update attribute
+ */
+enum nl80211_cu_attrs {
+	__NL80211_CU_ATTR_INVALID,
+
+	NL80211_CU_ATTR_MLDS,
+
+	/* keep last */
+	__NL80211_CU_ATTR_LAST,
+	NL80211_CU_ATTR_MAX = __NL80211_CU_ATTR_LAST - 1
+};
+
+/**
+ * nl80211_cu_mld_attrs - per mld critical update attributes
+ *
+ * @__NL80211_CU_MLD_ATTR_INVALID: invalid
+ * @NL80211_CU_MLD_ATTR_IFINDEX: network interface index (u32)
+ * @NL80211_CU_MLD_ATTR_LINKS: nested attribute specifying list of links
+ * on each mld, see &enum nl80211_cu_mld_link_attrs
+ * @__NL80211_CU_MLD_ATTR_LAST: internal use
+ * @NL80211_CU_MLD_ATTR_MAX: maximum per mld critical update attribute
+ */
+enum nl80211_cu_mld_attrs {
+	__NL80211_CU_MLD_ATTR_INVALID,
+
+	NL80211_CU_MLD_ATTR_IFINDEX,
+	NL80211_CU_MLD_ATTR_LINKS,
+
+	/* keep last */
+	__NL80211_CU_MLD_ATTR_LAST,
+	NL80211_CU_MLD_ATTR_MAX = __NL80211_CU_MLD_ATTR_LAST - 1
+};
+
+/**
+ * nl80211_cu_mld_link_attrs - per link critical update attributes
+ *
+ * @__NL80211_CU_MLD_LINK_ATTR_INVALID: invalid
+ * @NL80211_CU_MLD_LINK_ATTR_ID: link ID (u8) for the AP MLD
+ * @NL80211_CU_MLD_LINK_ATTR_CRITICAL_FLAG: critical update flag for the link
+ * @NL80211_CU_MLD_LINK_ATTR_BPCC: BSS parameter change count (u8) for the link
+ * @NL80211_CU_MLD_LINK_ATTR_SWITCH_COUNT: CSA / BCCA switch count (u8) for the link
+ * @__NL80211_CU_MLD_LINK_ATTR_LAST: internal use
+ * @NL80211_CU_MLD_LINK ATTR_MAX: maximum per link critical update attribute
+ */
+enum nl80211_cu_mld_link_attrs {
+	__NL80211_CU_MLD_LINK_ATTR_INVALID,
+
+	NL80211_CU_MLD_LINK_ATTR_ID,
+	NL80211_CU_MLD_LINK_ATTR_CRITICAL_FLAG,
+	NL80211_CU_MLD_LINK_ATTR_BPCC,
+	NL80211_CU_MLD_LINK_ATTR_SWITCH_COUNT,
+
+	/* keep last */
+	__NL80211_CU_MLD_LINK_ATTR_LAST,
+	NL80211_CU_MLD_LINK_ATTR_MAX = __NL80211_CU_MLD_LINK_ATTR_LAST - 1
+};
 #endif /* __LINUX_NL80211_H */
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index b4edba6b0b7b..b2c6376705b3 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -468,6 +468,25 @@ static const struct netlink_range_validation nl80211_punct_bitmap_range = {
 	.max = 0xffff,
 };
 
+static const struct nla_policy
+link_policy[NL80211_CU_MLD_LINK_ATTR_MAX + 1] = {
+	[NL80211_CU_MLD_LINK_ATTR_ID] = { .type = NLA_U8 },
+	[NL80211_CU_MLD_LINK_ATTR_CRITICAL_FLAG] = { .type = NLA_FLAG },
+	[NL80211_CU_MLD_LINK_ATTR_BPCC] = { .type = NLA_U8 },
+	[NL80211_CU_MLD_LINK_ATTR_SWITCH_COUNT] = { .type = NLA_U8 },
+};
+
+static const struct nla_policy
+mld_policy[NL80211_CU_MLD_ATTR_MAX + 1] = {
+	[NL80211_CU_MLD_ATTR_IFINDEX] = { .type = NLA_U32 },
+	[NL80211_CU_MLD_ATTR_LINKS] = NLA_POLICY_NESTED(link_policy),
+};
+
+static const struct nla_policy
+cu_policy[NL80211_CU_ATTR_MAX + 1] = {
+	[NL80211_CU_ATTR_MLDS] = NLA_POLICY_NESTED(mld_policy),
+};
+
 static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[0] = { .strict_start_type = NL80211_ATTR_HE_OBSS_PD },
 	[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
@@ -826,6 +845,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
 	[NL80211_ATTR_MLO_TTLM_DLINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
 	[NL80211_ATTR_MLO_TTLM_ULINK] = NLA_POLICY_EXACT_LEN(sizeof(u16) * 8),
 	[NL80211_ATTR_ASSOC_SPP_AMSDU] = { .type = NLA_FLAG },
+	[NL80211_ATTR_RXMGMT_CRITICAL_UPDATE] = NLA_POLICY_NESTED(cu_policy),
 };
 
 /* policy for the key attributes */
@@ -18843,6 +18863,91 @@ bool cfg80211_rx_unexpected_4addr_frame(struct net_device *dev,
 }
 EXPORT_SYMBOL(cfg80211_rx_unexpected_4addr_frame);
 
+static int nl80211_send_mgmt_critical_update_len(struct wireless_dev *wdev)
+{
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct wireless_dev *tmp_wdev;
+	int link_id;
+	int cu_len = 0;
+
+	cu_len += NL80211_CU_ATTR_MLDS_LEN;
+	list_for_each_entry(tmp_wdev, &rdev->wiphy.wdev_list, list) {
+		cu_len += NL80211_CU_ATTR_MLD_LEN;
+		for_each_valid_link(tmp_wdev, link_id) {
+			cu_len += NL80211_CU_ATTR_LINK_LEN;
+		}
+	}
+	return cu_len;
+}
+
+/* Add critical update attribute when sending management frame
+ * to user space.
+ */
+static int nl80211_send_mgmt_critical_update(struct sk_buff *msg,
+					     struct wireless_dev *wdev)
+{
+	struct wiphy *wiphy = wdev->wiphy;
+	struct wireless_dev *tmp_wdev;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+	struct nlattr *critical_update;
+	struct nlattr *mld_list, *mld;
+	struct nlattr *link_list, *link;
+	struct net_device *tmp_netdev;
+	int link_id;
+	int i = 0, j = 0;
+
+	critical_update = nla_nest_start(msg, NL80211_ATTR_RXMGMT_CRITICAL_UPDATE);
+	if (!critical_update)
+		return -ENOBUFS;
+
+	mld_list = nla_nest_start(msg, NL80211_CU_ATTR_MLDS);
+	if (!mld_list)
+		return -ENOBUFS;
+
+	list_for_each_entry(tmp_wdev, &rdev->wiphy.wdev_list, list) {
+		if (!tmp_wdev->valid_links)
+			continue;
+		if (!tmp_wdev->critical_update)
+			continue;
+		mld = nla_nest_start(msg, i + 1);
+		if (!mld)
+			return -ENOBUFS;
+		tmp_netdev = tmp_wdev->netdev;
+		if (tmp_netdev &&
+		    nla_put_u32(msg, NL80211_CU_MLD_ATTR_IFINDEX, tmp_netdev->ifindex))
+			return -ENOBUFS;
+		link_list = nla_nest_start(msg, NL80211_CU_MLD_ATTR_LINKS);
+		if (!link_list)
+			return -ENOBUFS;
+
+		for_each_valid_link(tmp_wdev, link_id) {
+			link = nla_nest_start(msg, j + 1);
+			if (!link)
+				return -ENOBUFS;
+			if (nla_put_u8(msg, NL80211_CU_MLD_LINK_ATTR_ID, link_id))
+				return -ENOBUFS;
+			if (tmp_wdev->links[link_id].ap.critical_flag &&
+			    nla_put_flag(msg, NL80211_CU_MLD_LINK_ATTR_CRITICAL_FLAG))
+				return -ENOBUFS;
+			if (nla_put_u8(msg, NL80211_CU_MLD_LINK_ATTR_BPCC,
+				       tmp_wdev->links[link_id].ap.bpcc))
+				return -ENOBUFS;
+			if (nla_put_u8(msg, NL80211_CU_MLD_LINK_ATTR_SWITCH_COUNT,
+				       tmp_wdev->links[link_id].ap.switch_count))
+				return -ENOBUFS;
+			nla_nest_end(msg, link);
+			j++;
+		}
+		nla_nest_end(msg, link_list);
+		nla_nest_end(msg, mld);
+		i++;
+	}
+	nla_nest_end(msg, mld_list);
+	nla_nest_end(msg, critical_update);
+	return 0;
+}
+
 int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 		      struct wireless_dev *wdev, u32 nlportid,
 		      struct cfg80211_rx_info *info, gfp_t gfp)
@@ -18850,8 +18955,15 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 	struct net_device *netdev = wdev->netdev;
 	struct sk_buff *msg;
 	void *hdr;
+	int cu_len = 0;
 
-	msg = nlmsg_new(100 + info->len, gfp);
+	if (wiphy_ext_feature_isset(
+		    wdev->wiphy,
+		    NL80211_EXT_FEATURE_CRITICAL_UPDATE_OFFLOAD) &&
+	    info->critical_update)
+		cu_len = nl80211_send_mgmt_critical_update_len(wdev);
+
+	msg = nlmsg_new(100 + info->len + cu_len, gfp);
 	if (!msg)
 		return -ENOMEM;
 
@@ -18885,6 +18997,15 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 						   NL80211_ATTR_PAD)))
 		goto nla_put_failure;
 
+	if (wiphy_ext_feature_isset(
+		    wdev->wiphy,
+		    NL80211_EXT_FEATURE_CRITICAL_UPDATE_OFFLOAD) &&
+	    info->critical_update) {
+		if (nl80211_send_mgmt_critical_update(msg, wdev))
+			goto nla_put_failure;
+		/* Reset the flag after adding the critical update attribute*/
+		wdev->critical_update = false;
+	}
 	genlmsg_end(msg, hdr);
 
 	return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlportid);
