Linux wireless drivers development
 help / color / mirror / Atom feed
* [RFC PATCH v2 4/8] wifi: mac80211: rework RX packet handling
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>

From: Benjamin Berg <benjamin.berg@intel.com>

The code has grown over time and it was not correctly handling all
cases. Examples of issues are that for management frames we would match
the received address against the MLD address even though we should not
resolve the station in that case. Another issue was that for data frames
we would not correctly fall back to use link addresses when the driver
does not provide the pubsta already.

The new code still uses the same approach as before. For data frames,
assume that we a valid station exists and interfaces do not need to be
iterated. On the other hand, for non-data frames (or if a data frame
without a station is received) all interfaces must be iterated.

Note that this rework makes mac80211 slightly more strict. For example,
previously mac80211 would have incorrectly accepted a data frame that
was transmitted on the wrong link.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 net/mac80211/rx.c | 282 +++++++++++++++++++++++++++-------------------
 1 file changed, 169 insertions(+), 113 deletions(-)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 1b8ec391991f..d76e8ee1a607 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4284,9 +4284,33 @@ static bool
 ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
 {
 	if (sta->mlo)
-		return !!(sta->valid_links & BIT(link_id));
-	else
-		return sta->deflink.link_id == link_id;
+		return sta->valid_links & BIT(link_id);
+
+	return sta->deflink.link_id == link_id;
+}
+
+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,
+					   struct link_sta_info *link_sta)
+{
+	struct sta_info *sta;
+
+	if (WARN_ON_ONCE(!link_sta) ||
+	    !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta,
+					       link_sta->link_id))
+		return false;
+
+	sta = link_sta->sta;
+
+	rx->sta = sta;
+	rx->local = sta->sdata->local;
+	rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]);
+	if (!rx->link)
+		return false;
+
+	rx->link_id = rx->link->link_id;
+	rx->link_sta = link_sta;
+
+	return true;
 }
 
 static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -5192,53 +5216,18 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
 	dev_kfree_skb(skb);
 }
 
-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
-				       struct sk_buff *skb, bool consume)
+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
 {
-	struct link_sta_info *link_sta;
-	struct ieee80211_hdr *hdr = (void *)skb->data;
-	struct sta_info *sta;
-	int link_id = -1;
+	struct ieee80211_chanctx_conf *conf;
 
-	/*
-	 * FIXME: Here we can assume that link addresses have not
-	 * been translated.
-	 *
-	 * Look up link station first, in case there's a
-	 * chance that they might have a link address that
-	 * is identical to the MLD address, that way we'll
-	 * have the link information if needed.
-	 */
-	link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
-	if (link_sta) {
-		sta = link_sta->sta;
-		link_id = link_sta->link_id;
-	} else {
-		struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
-		sta = sta_info_get_bss(rx->sdata, hdr->addr2);
-		if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
-		    status->freq) {
-			struct ieee80211_link_data *link;
-			struct ieee80211_chanctx_conf *conf;
-
-			for_each_link_data_rcu(rx->sdata, link) {
-				conf = rcu_dereference(link->conf->chanctx_conf);
-				if (!conf || !conf->def.chan)
-					continue;
-
-				if (status->freq == conf->def.chan->center_freq) {
-					link_id = link->link_id;
-					break;
-				}
-			}
-		}
-	}
+	if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN)
+		return true;
 
-	if (!ieee80211_rx_data_set_sta(rx, sta, link_id))
+	conf = rcu_dereference(link->conf->chanctx_conf);
+	if (!conf || !conf->def.chan)
 		return false;
 
-	return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+	return freq == conf->def.chan->center_freq;
 }
 
 /*
@@ -5252,11 +5241,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 {
 	struct ieee80211_local *local = hw_to_local(hw);
 	struct ieee80211_sub_if_data *sdata;
+	struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
 	struct ieee80211_hdr *hdr;
+	struct link_sta_info *link_sta;
+	struct sta_info *sta;
 	__le16 fc;
 	struct ieee80211_rx_data rx;
-	struct ieee80211_sub_if_data *prev;
 	struct rhlist_head *tmp;
+	bool rx_data_pending;
 	int err = 0;
 
 	fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -5292,92 +5284,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		     ieee80211_is_s1g_beacon(hdr->frame_control)))
 		ieee80211_scan_rx(local, skb);
 
+	/*
+	 * RX of a data frame should only happen to existing stations.
+	 * It is therefore more efficient to use the one provided by the driver
+	 * or search based on the station's address.
+	 *
+	 * Note we will fall through and handle a spurious data frame in the
+	 * same way as a management frame.
+	 */
 	if (ieee80211_is_data(fc)) {
-		struct sta_info *sta, *prev_sta;
-
 		if (link_pubsta) {
-			sta = container_of(link_pubsta->sta,
-					   struct sta_info, sta);
-			if (!ieee80211_rx_data_set_sta(&rx, sta,
-						       link_pubsta->link_id))
-				goto out;
+			sta = container_of(link_pubsta->sta, struct sta_info,
+					   sta);
+			link_sta = rcu_dereference(sta->link[link_pubsta->link_id]);
 
-			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+			rx.sdata = sta->sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta) &&
+			    ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
 
-		prev_sta = NULL;
+		rx_data_pending = false;
 
+		/* Search for stations on non-MLD interfaces */
 		for_each_sta_info(local, hdr->addr2, sta, tmp) {
-			int link_id;
+			struct ieee80211_link_data *link;
 
-			if (!prev_sta) {
-				prev_sta = sta;
+			if (ieee80211_vif_is_mld(&sta->sdata->vif))
 				continue;
-			}
-
-			rx.sdata = prev_sta->sdata;
-
-			/*
-			 * FIXME: This is not correct as the addr2 cannot be a
-			 * link address if the loop itself is iterated.
-			 */
-			if (prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
 
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					continue;
+			link = &sta->sdata->deflink;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
-			} else {
-				link_id = sta->deflink.link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
-
-			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink))
+				continue;
 
-			prev_sta = sta;
+			rx_data_pending = true;
 		}
 
-		if (prev_sta) {
-			int link_id;
+		/* And search stations on MLD interfaces */
+		for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) {
+			struct ieee80211_link_data *link;
 
-			rx.sdata = prev_sta->sdata;
+			sta = link_sta->sta;
+			sdata =	sta->sdata;
+			link = rcu_dereference(sdata->link[link_sta->link_id]);
 
-			/*
-			 * FIXME: This is not correct as the addr2 cannot be a
-			 * link address if the loop itself is iterated.
-			 */
-			if (prev_sta->sta.mlo) {
-				struct link_sta_info *link_sta;
-
-				link_sta = link_sta_info_get_bss(rx.sdata,
-								 hdr->addr2);
-				if (!link_sta)
-					goto out;
+			if (!ieee80211_rx_valid_freq(status->freq, link))
+				continue;
 
-				link_id = link_sta->link_id;
-			} else {
-				link_id = sta->deflink.link_id;
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb,
+								false);
+				rx_data_pending = false;
 			}
 
-			if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
-				goto out;
+			rx.sdata = sta->sdata;
+			if (!ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				continue;
+
+			rx_data_pending = true;
+		}
 
+		if (rx_data_pending) {
 			if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
 				return;
+
 			goto out;
 		}
+
+		/* fall through, e.g. for spurious frame notification */
 	}
 
-	prev = NULL;
+	/*
+	 * This is a management frame (or a data frame without a station) and
+	 * it will be delivered independent of whether a station exists,
+	 * so iterate the interfaces.
+	 */
+	rx_data_pending = false;
+
+	/* We expect the driver to provide frequency information */
+	if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq))
+		goto out;
 
 	list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+		struct ieee80211_link_data *link = NULL;
+
 		if (!ieee80211_sdata_running(sdata))
 			continue;
 
@@ -5385,30 +5387,84 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
 		    sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
 			continue;
 
+		/* Try to resolve the station */
+		if (!ieee80211_vif_is_mld(&sdata->vif)) {
+			sta = sta_info_get_bss(sdata, hdr->addr2);
+
+			if (sta) {
+				link_sta = &sta->deflink;
+				link = &sdata->deflink;
+			}
+		} else {
+			link_sta = link_sta_info_get_bss(sdata, hdr->addr2);
+
+			if (link_sta) {
+				sta = link_sta->sta;
+				link = rcu_dereference(sdata->link[link_sta->link_id]);
+			}
+		}
+
 		/*
-		 * frame is destined for this interface, but if it's
-		 * not also for the previous one we handle that after
-		 * the loop to avoid copying the SKB once too much
+		 * If we found a STA and it has a valid link information, then
+		 * RX using the station.
 		 */
+		if (link_sta && link &&
+		    ieee80211_rx_valid_freq(status->freq, link)) {
+			if (rx_data_pending) {
+				ieee80211_prepare_and_rx_handle(&rx, skb, false);
+				rx_data_pending = false;
+			}
+
+			/* No valid_links check as we need to RX beacons */
+
+			rx.sdata = sdata;
+			if (ieee80211_rx_data_set_link_sta(&rx, link_sta))
+				rx_data_pending = true;
 
-		if (!prev) {
-			prev = sdata;
 			continue;
 		}
 
-		rx.sdata = prev;
-		ieee80211_rx_for_interface(&rx, skb, false);
+		/* No station, try to resolve the link and RX */
+		if (ieee80211_vif_is_mld(&sdata->vif)) {
+			struct ieee80211_chanctx_conf *conf;
+			bool found = false;
 
-		prev = sdata;
-	}
+			for_each_link_data_rcu(sdata, link) {
+				conf = rcu_dereference(link->conf->chanctx_conf);
+				if (!conf || !conf->def.chan)
+					continue;
+
+				if (status->freq == conf->def.chan->center_freq) {
+					found = true;
+					break;
+				}
+			}
 
-	if (prev) {
-		rx.sdata = prev;
+			if (!found)
+				link = &sdata->deflink;
+		} else {
+			link = &sdata->deflink;
+		}
 
-		if (ieee80211_rx_for_interface(&rx, skb, true))
-			return;
+		if (rx_data_pending) {
+			ieee80211_prepare_and_rx_handle(&rx, skb, false);
+			rx_data_pending = false;
+		}
+
+		rx.sdata = sdata;
+		rx.local = sdata->local;
+		rx.link = link;
+		rx.link_id = link->link_id;
+		rx.sta = NULL;
+		rx.link_sta = NULL;
+
+		rx_data_pending = true;
 	}
 
+	if (rx_data_pending &&
+	    ieee80211_prepare_and_rx_handle(&rx, skb, true))
+		return;
+
  out:
 	dev_kfree_skb(skb);
 }
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 5/8] wifi: cfg80211: add attribute for TX/RX denoting there is no station
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>

From: Benjamin Berg <benjamin.berg@intel.com>

For MLD stations, userspace may need to explicitly transmit a frame to
a specific link address. In that case, it needs to ensure that no
address translation happens.

In the reverse case of an RX, userspace may need to know the link
address for a frame. By passing the information whether a STA is known
for the frame, userspace knows whether link translation happened and
can do the reverse lookup when needed.

This is important for flows where a STA is still registered but the
connection has been lost and it is returning again.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 include/net/cfg80211.h       | 4 ++++
 include/uapi/linux/nl80211.h | 7 +++++++
 net/wireless/nl80211.c       | 8 +++++++-
 3 files changed, 18 insertions(+), 1 deletion(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index fc01de19c798..5063911cba56 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3919,6 +3919,7 @@ struct cfg80211_update_ft_ies_params {
  * @link_id: for MLO, the link ID to transmit on, -1 if not given; note
  *	that the link ID isn't validated (much), it's in range but the
  *	link might not exist (or be used by the receiver STA)
+ * @no_sta: set if the frame should not be transmitted using an existing STA
  */
 struct cfg80211_mgmt_tx_params {
 	struct ieee80211_channel *chan;
@@ -3931,6 +3932,7 @@ struct cfg80211_mgmt_tx_params {
 	int n_csa_offsets;
 	const u16 *csa_offsets;
 	int link_id;
+	bool no_sta;
 };
 
 /**
@@ -9025,6 +9027,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
+ * @no_sta: set if no station is known for the frame (relevant for MLD)
  */
 struct cfg80211_rx_info {
 	int freq;
@@ -9036,6 +9039,7 @@ struct cfg80211_rx_info {
 	u32 flags;
 	u64 rx_tstamp;
 	u64 ack_tstamp;
+	bool no_sta;
 };
 
 /**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index b63f71850906..1466c043974c 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2984,6 +2984,11 @@ enum nl80211_commands {
  *	this feature during association. This is a flag attribute.
  *	Currently only supported in mac80211 drivers.
  *
+ * @NL80211_ATTR_FRAME_CMD_NO_STA: Valid for NL80211_CMD_FRAME to denote that
+ *	the kernel had no station for a received frame or should not use a
+ *	known station to transmit a frame. This is relevant to know whether
+ *	MLD address translation happened or to disable it when sending a frame.
+ *
  * @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 +3562,8 @@ enum nl80211_attrs {
 	NL80211_ATTR_UHR_CAPABILITY,
 	NL80211_ATTR_DISABLE_UHR,
 
+	NL80211_ATTR_FRAME_CMD_NO_STA,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6e58b238a1f8..56a9c63ddd76 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_FRAME_CMD_NO_STA] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -14020,6 +14021,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 	    !(wdev->valid_links & BIT(params.link_id)))
 		return -EINVAL;
 
+	params.no_sta =
+		nla_get_flag(info->attrs[NL80211_ATTR_FRAME_CMD_NO_STA]);
+
 	params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
 	params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
 
@@ -20581,7 +20585,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 	    (info->ack_tstamp && nla_put_u64_64bit(msg,
 						   NL80211_ATTR_TX_HW_TIMESTAMP,
 						   info->ack_tstamp,
-						   NL80211_ATTR_PAD)))
+						   NL80211_ATTR_PAD)) ||
+	    (info->no_sta &&
+	     nla_put_flag(msg, NL80211_ATTR_FRAME_CMD_NO_STA)))
 		goto nla_put_failure;
 
 	genlmsg_end(msg, hdr);
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 6/8] wifi: mac80211: report to cfg80211 when no STA is known for a frame
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>

From: Benjamin Berg <benjamin.berg@intel.com>

This is relevant for hostapd to know whether address translation was
done on a received management frame.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 net/mac80211/rx.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d76e8ee1a607..ec718b7f3e16 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3948,6 +3948,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
 		.len = rx->skb->len,
 		.link_id = rx->link_id,
 		.have_link_id = rx->link_id >= 0,
+		.no_sta = !rx->sta,
 	};
 
 	/* skip known-bad action frames and return them in the next handler */
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 7/8] wifi: mac80211: pass station to ieee80211_tx_skb_tid
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>

From: Benjamin Berg <benjamin.berg@intel.com>

The station may be relevant for queuing and will also generally be
resolved in some cases. However, we want to be able to prevent looking
up the station based on the address.

Add a station parameter, which can be set to the correct station, to an
error value to prevent station lookup or to NULL to get the old
behaviour where the address is used to find the appropriate station.

Also disable the station lookup for ieee80211_tx_skb_tid_band already as
it does not make any sense to find a station when doing an off-channel
transmit.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 net/mac80211/agg-tx.c      |  6 +++---
 net/mac80211/ht.c          |  4 ++--
 net/mac80211/ieee80211_i.h | 14 ++++++++------
 net/mac80211/offchannel.c  |  2 +-
 net/mac80211/rx.c          |  2 +-
 net/mac80211/tdls.c        |  4 ++--
 net/mac80211/tx.c          |  8 +++++---
 7 files changed, 22 insertions(+), 18 deletions(-)

diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d981b0fc57bf..6a5754351f08 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
  * Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2024, 2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -97,7 +97,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
 	if (sta->sta.deflink.he_cap.has_he)
 		ieee80211_add_addbaext(skb, 0, agg_size);
 
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 
 void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -126,7 +126,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
 
 	IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
 					IEEE80211_TX_CTL_REQ_TX_STATUS;
-	ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
 }
 EXPORT_SYMBOL(ieee80211_send_bar);
 
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 1c82a28b03de..f98f5a9a2ebe 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -9,7 +9,7 @@
  * Copyright 2007, Michael Wu <flamingice@sourmilk.net>
  * Copyright 2007-2010, Intel Corporation
  * Copyright 2017	Intel Deutschland GmbH
- * Copyright(c) 2020-2025 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -571,7 +571,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
 	info->status_data = IEEE80211_STATUS_TYPE_SMPS |
 			    u16_encode_bits(status_link_id << 2 | smps,
 					    IEEE80211_STATUS_SUBDATA_MASK);
-	ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 
 	return 0;
 }
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e60b814dd89e..793331c1d748 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2393,7 +2393,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
 		    struct sta_info *sta, struct sk_buff *skb);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band);
 
 static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
@@ -2411,22 +2412,23 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
 
 static inline void
 ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid,
-			  enum nl80211_band band)
+			  struct sk_buff *skb, int tid, enum nl80211_band band)
 {
 	rcu_read_lock();
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+				    tid, -1, band);
 	rcu_read_unlock();
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id);
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id);
 
 static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
 				    struct sk_buff *skb)
 {
 	/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
-	ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+	ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
 }
 
 /**
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index ae82533e3c02..0a8b4c5e8c12 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -1026,7 +1026,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index ec718b7f3e16..5109791546a7 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4072,7 +4072,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
 					local->hw.offchannel_tx_hw_queue;
 		}
 
-		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+		__ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
 					    status->band);
 	}
 
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842f..39a880ab7edb 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -6,7 +6,7 @@
  * Copyright 2014, Intel Corporation
  * Copyright 2014  Intel Mobile Communications GmbH
  * Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2019, 2021-2026 Intel Corporation
  */
 
 #include <linux/ieee80211.h>
@@ -1067,7 +1067,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
 	}
 
 	if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
-		ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
 		return 0;
 	}
 
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 007f5a368d41..c788d48ef365 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -6235,7 +6235,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
 EXPORT_SYMBOL(ieee80211_unreserve_tid);
 
 void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
-				 struct sk_buff *skb, int tid, int link_id,
+				 struct sk_buff *skb, struct sta_info *sta,
+				 int tid, int link_id,
 				 enum nl80211_band band)
 {
 	const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6292,7 +6293,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
 }
 
 void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
-			  struct sk_buff *skb, int tid, int link_id)
+			  struct sk_buff *skb, struct sta_info *sta,
+			  int tid, int link_id)
 {
 	struct ieee80211_chanctx_conf *chanctx_conf;
 	enum nl80211_band band;
@@ -6317,7 +6319,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
 		band = 0;
 	}
 
-	__ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+	__ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
 	rcu_read_unlock();
 }
 
-- 
2.53.0


^ permalink raw reply related

* [RFC PATCH v2 8/8] wifi: mac80211: pass error station if non-STA transmit was requested
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
  To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>

From: Benjamin Berg <benjamin.berg@intel.com>

When cfg80211 requested a transmit without a station, pass an error
station to ieee80211_tx_skb_tid instead of the correct one.

Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
 net/mac80211/offchannel.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 0a8b4c5e8c12..24a55186b87f 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -857,8 +857,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 			need_offchan = true;
 
 		rcu_read_lock();
-		sta = sta_info_get_bss(sdata, mgmt->da);
-		mlo_sta = sta && sta->sta.mlo;
+		if (!params->no_sta) {
+			sta = sta_info_get_bss(sdata, mgmt->da);
+			mlo_sta = sta && sta->sta.mlo;
+		}
 
 		if (!ieee80211_is_action(mgmt->frame_control) ||
 		    mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -887,7 +889,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 		     local->ops->remain_on_channel &&
 		     memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
 			need_offchan = true;
-		} else if (sdata->u.mgd.associated &&
+		} else if (!params->no_sta &&
+			   sdata->u.mgd.associated &&
 			   ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
 			sta = sta_info_get_bss(sdata, mgmt->da);
 			mlo_sta = sta && sta->sta.mlo;
@@ -1026,7 +1029,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
 	}
 
 	if (!need_offchan) {
-		ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
+		ieee80211_tx_skb_tid(sdata, skb,
+				     sta ? sta : ERR_PTR(-ENOENT),
+				     7, link_id);
 		ret = 0;
 		goto out_unlock;
 	}
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH wireless] wifi: mac80211: Fix ADDBA update when HW supports reordering
From: Pablo MARTIN-GOMEZ @ 2026-02-23 13:25 UTC (permalink / raw)
  To: Johannes Berg, Remi Pommarel; +Cc: linux-wireless, linux-kernel
In-Reply-To: <0f0703e2749185f9a334b3435ffe247b42e6923b.camel@sipsolutions.net>

Hello,

On 23/02/2026 12:50, Johannes Berg wrote:
> On Sun, 2026-02-22 at 17:06 +0100, Remi Pommarel wrote:
>>>> That does make sense. However, if I understand correctly, it means that
>>>> even if we end up storing the timeout for drivers that support
>>>> reordering, a new IEEE80211_AMPDU_RX_UPDATE should still be introduced,
>>>> right?
>>>
>>> I guess in order to do a no-op update that doesn't change the timeout,
>>> we could? But not sure it's all worth it?
>>
>> I was going to say it would not be a no-op for a buf_size update but,
>> if I understand correctly, even when SUPPORTS_REORDERING_BUFFER is not
>> set the buf_size update is ignored completely and the reorder_buf is
>> not resized yet a successful addba response is sent. Don't we need to
>> check for buf_size change as well as timeout also?
> 
> I was going to say that I thought buf_size is not allowed to change, but
> (re)reading the spec doesn't seem to bear that out.
For once, the standard (802.11-2024) is really clear on this (10.25.2):

A block ack agreement may be modified by the originator by sending an
ADDBA Request frame (see 11.5.2, except that MLME-ADDBA primitives are
not used by the originator). All parameters of the agreement may be
modified except for the TID. If the request is not successful, the
existing agreement is not modified. If the request is successful, the
behavior is as if a DELBA frame for the block ack agreement had been
transmitted by the originator and received by the recipient immediately
prior to the ADDBA Request frame.
> 
> I guess we could just unconditionally reject any updates. I'm not sure
> now why we ever added the update in the first place. Perhaps some
> implementation was doing no-op updates and failing on negative
> responses?
> 
If the originator is compliant and trying to update, unconditionally
rejecing any update shouldn't have any side-effects. But if the
originator doesn't receive the ADDBA response, it is going to resend an
ADDBA request; for the originator, it is just a retry, but for the
recipient it's an update, so the originator is going to receive a non
success. And unless the originator sends a DELBA "by default" at some
point, it won't be able to create a new BA (for a given TID) with the
recipient.
[...]
> johannes
Pablo MG

^ permalink raw reply

* [PATCH ath-next v4] wifi: ath12k: add basic hwmon temperature reporting
From: Maharaja Kennadyrajan @ 2026-02-23 13:26 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan, Aishwarya R

Add initial thermal support by wiring up a per-radio (pdev) hwmon temperature
sensor backed by the existing WMI pdev temperature command and event.
When userspace reads the sysfs file temp1_input, the driver sends
WMI_PDEV_GET_TEMPERATURE_CMDID (tag WMI_TAG_PDEV_GET_TEMPERATURE_CMD) and waits
for the corresponding WMI_PDEV_TEMPERATURE_EVENTID
(tag WMI_TAG_PDEV_TEMPERATURE_EVENT) to get the temperature and pdev_id.

Export the reported value in millidegrees Celsius as required by hwmon.
The temperature reported is per-radio (pdev). In a multi-radio wiphy under a
single phy, a separate hwmon device is created for each radio.

Sample command and output:
$ cat /sys/devices/pci0000:00/.../ieee80211/phyX/hwmonY/temp1_input
$ 50000

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202602160145.YQdvbqYY-lkp@intel.com/
Co-developed-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---

v4: Addressed Nicolas comment on extra mutex_lock removal.

v3: Addressed the Pablo's comment on looping the ab->pdevs in the function
    ath12k_thermal_register() while handling the failure case.
    Renamed mutex_lock to generic name from thermal.read_lock to
    thermal.lock.

v2: Fixed the kernel test robot reported build test error.

 drivers/net/wireless/ath/ath12k/Makefile  |   1 +
 drivers/net/wireless/ath/ath12k/core.c    |  13 +++
 drivers/net/wireless/ath/ath12k/core.h    |   3 +
 drivers/net/wireless/ath/ath12k/mac.c     |   4 +
 drivers/net/wireless/ath/ath12k/thermal.c | 124 ++++++++++++++++++++++
 drivers/net/wireless/ath/ath12k/thermal.h |  40 +++++++
 drivers/net/wireless/ath/ath12k/wmi.c     |  57 +++++-----
 7 files changed, 211 insertions(+), 31 deletions(-)
 create mode 100644 drivers/net/wireless/ath/ath12k/thermal.c
 create mode 100644 drivers/net/wireless/ath/ath12k/thermal.h

diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile
index 3ba1236956cc..3b39b2c33307 100644
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -32,6 +32,7 @@ ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
 ath12k-$(CONFIG_PM) += wow.o
 ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o
 ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ath12k-$(CONFIG_THERMAL) += thermal.o
 
 # for tracing framework to find trace.h
 CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 9d6c50a94e64..9dca1a0af73e 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -863,11 +863,22 @@ static int ath12k_core_pdev_create(struct ath12k_base *ab)
 		return ret;
 	}
 
+	ret = ath12k_thermal_register(ab);
+	if (ret) {
+		ath12k_err(ab, "could not register thermal device: %d\n", ret);
+		goto err_dp_pdev_free;
+	}
+
 	return 0;
+
+err_dp_pdev_free:
+	ath12k_dp_pdev_free(ab);
+	return ret;
 }
 
 static void ath12k_core_pdev_destroy(struct ath12k_base *ab)
 {
+	ath12k_thermal_unregister(ab);
 	ath12k_dp_pdev_free(ab);
 }
 
@@ -1361,6 +1372,7 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab)
 
 	mutex_lock(&ab->core_lock);
 	ath12k_link_sta_rhash_tbl_destroy(ab);
+	ath12k_thermal_unregister(ab);
 	ath12k_dp_pdev_free(ab);
 	ath12k_ce_cleanup_pipes(ab);
 	ath12k_wmi_detach(ab);
@@ -1502,6 +1514,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
 			complete(&ar->vdev_delete_done);
 			complete(&ar->bss_survey_done);
 			complete_all(&ar->regd_update_completed);
+			complete_all(&ar->thermal.wmi_sync);
 
 			wake_up(&ar->dp.tx_empty_waitq);
 			idr_for_each(&ar->txmgmt_idr,
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 990934ec92fc..760c76d6f0f4 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -36,6 +36,7 @@
 #include "coredump.h"
 #include "cmn_defs.h"
 #include "dp_cmn.h"
+#include "thermal.h"
 
 #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
 
@@ -757,6 +758,8 @@ struct ath12k {
 
 	s8 max_allowed_tx_power;
 	struct ath12k_pdev_rssi_offsets rssi_info;
+
+	struct ath12k_thermal thermal;
 };
 
 struct ath12k_hw {
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 68431a0e128e..b9091f3f723f 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -14792,6 +14792,10 @@ static void ath12k_mac_setup(struct ath12k *ar)
 	init_completion(&ar->mlo_setup_done);
 	init_completion(&ar->completed_11d_scan);
 	init_completion(&ar->regd_update_completed);
+	init_completion(&ar->thermal.wmi_sync);
+
+	ar->thermal.temperature = 0;
+	ar->thermal.hwmon_dev = NULL;
 
 	INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
 	wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
new file mode 100644
index 000000000000..a764d2112a3c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include "core.h"
+#include "debug.h"
+
+static ssize_t ath12k_thermal_temp_show(struct device *dev,
+					struct device_attribute *attr,
+					char *buf)
+{
+	struct ath12k *ar = dev_get_drvdata(dev);
+	unsigned long time_left;
+	int ret, temperature;
+
+	guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+	if (ar->ah->state != ATH12K_HW_STATE_ON)
+		return -ENETDOWN;
+
+	reinit_completion(&ar->thermal.wmi_sync);
+	ret = ath12k_wmi_send_pdev_temperature_cmd(ar);
+	if (ret) {
+		ath12k_warn(ar->ab, "failed to read temperature %d\n", ret);
+		return ret;
+	}
+
+	if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
+		return -ESHUTDOWN;
+
+	time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
+						ATH12K_THERMAL_SYNC_TIMEOUT_HZ);
+	if (!time_left) {
+		ath12k_warn(ar->ab, "failed to synchronize thermal read\n");
+		return -ETIMEDOUT;
+	}
+
+	spin_lock_bh(&ar->data_lock);
+	temperature = ar->thermal.temperature;
+	spin_unlock_bh(&ar->data_lock);
+
+	/* display in millidegree celsius */
+	return sysfs_emit(buf, "%d\n", temperature * 1000);
+}
+
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature)
+{
+	spin_lock_bh(&ar->data_lock);
+	ar->thermal.temperature = temperature;
+	spin_unlock_bh(&ar->data_lock);
+	complete_all(&ar->thermal.wmi_sync);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, ath12k_thermal_temp, 0);
+
+static struct attribute *ath12k_hwmon_attrs[] = {
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	NULL,
+};
+ATTRIBUTE_GROUPS(ath12k_hwmon);
+
+int ath12k_thermal_register(struct ath12k_base *ab)
+{
+	struct ath12k *ar;
+	int i, j, ret;
+
+	if (!IS_REACHABLE(CONFIG_HWMON))
+		return 0;
+
+	for (i = 0; i < ab->num_radios; i++) {
+		ar = ab->pdevs[i].ar;
+		if (!ar)
+			continue;
+
+		ar->thermal.hwmon_dev =
+			hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
+							  "ath12k_hwmon", ar,
+							  ath12k_hwmon_groups);
+		if (IS_ERR(ar->thermal.hwmon_dev)) {
+			ret = PTR_ERR(ar->thermal.hwmon_dev);
+			ar->thermal.hwmon_dev = NULL;
+			ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
+				   ret);
+			for (j = i - 1; j >= 0; j--) {
+				ar = ab->pdevs[j].ar;
+				if (!ar)
+					continue;
+
+				hwmon_device_unregister(ar->thermal.hwmon_dev);
+				ar->thermal.hwmon_dev = NULL;
+			}
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+	struct ath12k *ar;
+	int i;
+
+	if (!IS_REACHABLE(CONFIG_HWMON))
+		return;
+
+	for (i = 0; i < ab->num_radios; i++) {
+		ar = ab->pdevs[i].ar;
+		if (!ar)
+			continue;
+
+		if (ar->thermal.hwmon_dev) {
+			hwmon_device_unregister(ar->thermal.hwmon_dev);
+			ar->thermal.hwmon_dev = NULL;
+		}
+	}
+}
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
new file mode 100644
index 000000000000..9d84056188e1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _ATH12K_THERMAL_
+#define _ATH12K_THERMAL_
+
+#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
+
+struct ath12k_thermal {
+	struct completion wmi_sync;
+
+	/* temperature value in Celsius degree protected by data_lock. */
+	int temperature;
+	struct device *hwmon_dev;
+};
+
+#if IS_REACHABLE(CONFIG_THERMAL)
+int ath12k_thermal_register(struct ath12k_base *ab);
+void ath12k_thermal_unregister(struct ath12k_base *ab);
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
+#else
+static inline int ath12k_thermal_register(struct ath12k_base *ab)
+{
+	return 0;
+}
+
+static inline void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
+						    int temperature)
+{
+}
+
+#endif
+#endif /* _ATH12K_THERMAL_ */
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 7617fc3a2479..69f2dbcfca63 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6778,31 +6778,6 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
 	return 0;
 }
 
-static int
-ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, struct sk_buff *skb,
-			 const struct wmi_pdev_temperature_event *ev)
-{
-	const void **tb;
-	int ret;
-
-	tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
-	if (IS_ERR(tb)) {
-		ret = PTR_ERR(tb);
-		ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
-		return ret;
-	}
-
-	ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
-	if (!ev) {
-		ath12k_warn(ab, "failed to fetch pdev temp ev");
-		kfree(tb);
-		return -EPROTO;
-	}
-
-	kfree(tb);
-	return 0;
-}
-
 static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab)
 {
 	/* try to send pending beacons first. they take priority */
@@ -8811,25 +8786,45 @@ static void
 ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
 				  struct sk_buff *skb)
 {
+	const struct wmi_pdev_temperature_event *ev;
 	struct ath12k *ar;
-	struct wmi_pdev_temperature_event ev = {};
+	const void **tb;
+	int temp;
+	u32 pdev_id;
+
+	tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+	if (IS_ERR(tb)) {
+		ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
+		return;
+	}
 
-	if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) {
-		ath12k_warn(ab, "failed to extract pdev temperature event");
+	ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
+	if (!ev) {
+		ath12k_warn(ab, "failed to fetch pdev temp ev\n");
+		kfree(tb);
 		return;
 	}
 
+	temp = a_sle32_to_cpu(ev->temp);
+	pdev_id = le32_to_cpu(ev->pdev_id);
+
+	kfree(tb);
+
 	ath12k_dbg(ab, ATH12K_DBG_WMI,
-		   "pdev temperature ev temp %d pdev_id %d\n", ev.temp, ev.pdev_id);
+		   "pdev temperature ev temp %d pdev_id %u\n",
+		   temp, pdev_id);
 
 	rcu_read_lock();
 
-	ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev.pdev_id));
+	ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id);
 	if (!ar) {
-		ath12k_warn(ab, "invalid pdev id in pdev temperature ev %d", ev.pdev_id);
+		ath12k_warn(ab, "invalid pdev id %u in pdev temperature ev\n",
+			    pdev_id);
 		goto exit;
 	}
 
+	ath12k_thermal_event_temperature(ar, temp);
+
 exit:
 	rcu_read_unlock();
 }

base-commit: 37a93dd5c49b5fda807fd204edf2547c3493319c
-- 
2.17.1


^ permalink raw reply related

* Re: [PATCH ath-next] wifi: ath9k: simplify eeprom format strings
From: Toke Høiland-Jørgensen @ 2026-02-23 15:05 UTC (permalink / raw)
  To: Rosen Penev, linux-wireless
  Cc: Andreas Färber, Manivannan Sadhasivam, open list,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE
In-Reply-To: <20250821033908.638871-1-rosenp@gmail.com>

Rosen Penev <rosenp@gmail.com> writes:

> devm is already used here so might as well simplify the whole function
> with devm_kasprintf.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>

(sorry for not getting around to replying to this before)

> ---
>  drivers/net/wireless/ath/ath9k/ath9k.h                |  2 +-
>  drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c | 11 +----------
>  drivers/net/wireless/ath/ath9k/init.c                 |  7 +++----
>  drivers/net/wireless/ath/ath9k/rng.c                  |  4 ++--
>  4 files changed, 7 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 6e38aa7351e3..60aac2c50409 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -1076,7 +1076,7 @@ struct ath_softc {
>  #ifdef CONFIG_ATH9K_HWRNG
>  	struct hwrng rng_ops;
>  	u32 rng_last;
> -	char rng_name[sizeof("ath9k_65535")];
> +	const char *rng_name;

Changing this fixed buffer to a devm-managed pointer makes no sense: it
doesn't help with any lifetime issues, and just adds overhead.

>  #endif
>  };
>  
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> index fe1013a3a588..c4f8d1f98369 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> +++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> @@ -140,19 +140,10 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
>  static const char *owl_get_eeprom_name(struct pci_dev *pdev)
>  {
>  	struct device *dev = &pdev->dev;
> -	char *eeprom_name;
>  
>  	dev_dbg(dev, "using auto-generated eeprom filename\n");
>  
> -	eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
> -	if (!eeprom_name)
> -		return NULL;
> -
> -	/* this should match the pattern used in ath9k/init.c */
> -	scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
> -		  dev_name(dev));
> -
> -	return eeprom_name;
> +	return devm_kasprintf(dev, GFP_KERNEL, "ath9k-eeprom-pci-%s.bin", dev_name(dev));

This change sort of makes sense, given that devm is already used. But
really, it shouldn't be; the name is only used to pass it to
request_firmware_nowait(), so the devm management is totally
superfluous. Better to change it to an on-stack buffer like the other
call instead (maybe just by getting rid of the owl_get_eeprom_name()
helper entirely, and moving the scnprintf() into the caller).

>  }
>  
>  static void owl_nvmem_work(struct work_struct *work)
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index ee951493e993..04903b5c611f 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -625,7 +625,7 @@ static int ath9k_of_init(struct ath_softc *sc)
>  	struct ath_hw *ah = sc->sc_ah;
>  	struct ath_common *common = ath9k_hw_common(ah);
>  	enum ath_bus_type bus_type = common->bus_ops->ath_bus_type;
> -	char eeprom_name[100];
> +	const char *eeprom_name;

Changing an on-stack buffer to a devm-managed one also doesn't make any
sense.

-Toke

^ permalink raw reply

* Re: [PATCH v1] wifi: ath9k: Fix typo
From: Toke Høiland-Jørgensen @ 2026-02-23 15:17 UTC (permalink / raw)
  To: Alejandro Colomar, linux-wireless
  Cc: Alejandro Colomar, Rajkumar Manoharan, John W. Linville
In-Reply-To: <6ab107cf786f9d05dc4d84ea4e2d1b219ce108c0.1766355822.git.alx@kernel.org>

Alejandro Colomar <alx@kernel.org> writes:

> This only worked by chance, because all callers of this macro used the
> same identifiers that were expected by the macro.
>
> 	$ grep -rn ath_for_each_chanctx
> 	drivers/net/wireless/ath/ath9k/main.c:1576:	ath_for_each_chanctx(sc, ctx)
> 	drivers/net/wireless/ath/ath9k/main.c:2554:	ath_for_each_chanctx(sc, ctx) {
> 	drivers/net/wireless/ath/ath9k/channel.c:165:	ath_for_each_chanctx(sc, ctx) {
> 	drivers/net/wireless/ath/ath9k/channel.c:291:	ath_for_each_chanctx(sc, ctx) {
> 	drivers/net/wireless/ath/ath9k/channel.c:861:	ath_for_each_chanctx(sc, ctx) {
> 	drivers/net/wireless/ath/ath9k/debug.c:717:	ath_for_each_chanctx(sc, ctx) {
> 	drivers/net/wireless/ath/ath9k/ath9k.h:446:#define ath_for_each_chanctx(_sc, _ctx)                               \
>
> Fixes: c4dc0d040e35 (2014-06-19; "ath9k: Fetch appropriate operating channel context")
> Cc: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
> Cc: John W. Linville <linville@tuxdriver.com>
> Cc: Toke Høiland-Jørgensen <toke@toke.dk>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>

Yeah, looks reasonable - thanks!

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath9k: use non devm for nvmem_cell_get
From: Toke Høiland-Jørgensen @ 2026-02-23 15:21 UTC (permalink / raw)
  To: Rosen Penev, linux-wireless; +Cc: open list
In-Reply-To: <20260223025021.19008-1-rosenp@gmail.com>

Rosen Penev <rosenp@gmail.com> writes:

> There's absolutely no need to extend the lifetime of cell to post
> removal of the driver. It's only used in this function.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>

Right, makes sense.

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

^ permalink raw reply

* [BUG] iwlwifi oops in iwl_pcie_rx_handle on resume
From: Chris Bainbridge @ 2026-02-23 18:43 UTC (permalink / raw)
  To: miriam.rachel.korenblit
  Cc: johannes.berg, emmanuel.grumbach, linux-intel-wifi,
	linux-wireless, linux-kernel

Hi,

I just got an oops with v7.0-rc1 shortly after resume:

<6>[ 9761.194306] ACPI: EC: interrupt unblocked
<6>[ 9761.336806] amdgpu 0000:03:00.0: [drm] PCIE GART of 1024M enabled.
<6>[ 9761.336816] amdgpu 0000:03:00.0: [drm] PTB located at 0x000000F41FC00000
<6>[ 9761.337043] amdgpu 0000:03:00.0: SMU is resuming...
<6>[ 9761.338126] amdgpu 0000:03:00.0: dpm has been disabled
<6>[ 9761.338943] amdgpu 0000:03:00.0: SMU is resumed successfully!
<6>[ 9761.439301] [drm] DM_MST: Differing MST start on aconnector: 000000001b6f89b3 [id: 116]
<6>[ 9761.441525] amdgpu 0000:03:00.0: ring gfx uses VM inv eng 0 on hub 0
<6>[ 9761.441530] amdgpu 0000:03:00.0: ring comp_1.0.0 uses VM inv eng 1 on hub 0
<6>[ 9761.441532] amdgpu 0000:03:00.0: ring comp_1.1.0 uses VM inv eng 4 on hub 0
<6>[ 9761.441534] amdgpu 0000:03:00.0: ring comp_1.2.0 uses VM inv eng 5 on hub 0
<6>[ 9761.441536] amdgpu 0000:03:00.0: ring comp_1.3.0 uses VM inv eng 6 on hub 0
Oops#1 Part6
<6>[ 9761.441538] amdgpu 0000:03:00.0: ring comp_1.0.1 uses VM inv eng 7 on hub 0
<6>[ 9761.441540] amdgpu 0000:03:00.0: ring comp_1.1.1 uses VM inv eng 8 on hub 0
<6>[ 9761.441542] amdgpu 0000:03:00.0: ring comp_1.2.1 uses VM inv eng 9 on hub 0
<6>[ 9761.441544] amdgpu 0000:03:00.0: ring comp_1.3.1 uses VM inv eng 10 on hub 0
<6>[ 9761.441546] amdgpu 0000:03:00.0: ring kiq_0.2.1.0 uses VM inv eng 11 on hub 0
<6>[ 9761.441548] amdgpu 0000:03:00.0: ring sdma0 uses VM inv eng 0 on hub 8
<6>[ 9761.441550] amdgpu 0000:03:00.0: ring vcn_dec uses VM inv eng 1 on hub 8
<6>[ 9761.441552] amdgpu 0000:03:00.0: ring vcn_enc0 uses VM inv eng 4 on hub 8
<6>[ 9761.441554] amdgpu 0000:03:00.0: ring vcn_enc1 uses VM inv eng 5 on hub 8
<6>[ 9761.441556] amdgpu 0000:03:00.0: ring jpeg_dec uses VM inv eng 6 on hub 8
<6>[ 9761.568907] usb 1-1.3.2.4: reset high-speed USB device number 12 using xhci_hcd
<6>[ 9761.767481] nvme nvme0: 8/0/0 default/read/poll queues
<6>[ 9761.922505] iwlwifi 0000:01:00.0: WFPM_UMAC_PD_NOTIFICATION: 0x20
<6>[ 9761.922592] iwlwifi 0000:01:00.0: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
<6>[ 9761.922715] iwlwifi 0000:01:00.0: WFPM_AUTH_KEY_0: 0x90
<6>[ 9761.922787] iwlwifi 0000:01:00.0: CNVI_SCU_SEQ_DATA_DW9: 0x0
<6>[ 9762.052938] OOM killer enabled.
<6>[ 9762.052942] Restarting tasks: Starting
<6>[ 9762.054800] Restarting tasks: Done
<6>[ 9762.054976] efivarfs: resyncing variable state
<6>[ 9762.066314] efivarfs: finished resyncing variable state
<5>[ 9762.066477] random: crng reseeded on system resumption
<6>[ 9762.160714] PM: suspend exit
<6>[ 9762.739294] iwlwifi 0000:01:00.0: WFPM_UMAC_PD_NOTIFICATION: 0x20
Oops#1 Part5
<6>[ 9762.739367] iwlwifi 0000:01:00.0: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
<6>[ 9762.739435] iwlwifi 0000:01:00.0: WFPM_AUTH_KEY_0: 0x90
<6>[ 9762.739451] iwlwifi 0000:01:00.0: CNVI_SCU_SEQ_DATA_DW9: 0x0
<6>[ 9766.455338] wlp1s0: authenticate with d6:92:5e:eb:ee:15 (local address=c8:15:4e:63:1d:e8)
<6>[ 9766.456638] wlp1s0: send auth to d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.518885] wlp1s0: authenticate with d6:92:5e:eb:ee:15 (local address=c8:15:4e:63:1d:e8)
<6>[ 9766.518900] wlp1s0: send auth to d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.522553] wlp1s0: authenticated
<6>[ 9766.524038] wlp1s0: associate with d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.532772] wlp1s0: RX AssocResp from d6:92:5e:eb:ee:15 (capab=0x1011 status=0 aid=21)
<6>[ 9766.549780] wlp1s0: associated
<7>[ 9766.618488] wlp1s0: Limiting TX power to 23 (23 - 0) dBm as advertised by d6:92:5e:eb:ee:15
<4>[ 9809.544787] Oops: general protection fault, probably for non-canonical address 0xe0000d1060000000: 0000 [#1] SMP KASAN
<1>[ 9809.544818] KASAN: maybe wild-memory-access in range [0x0000888300000000-0x0000888300000007]
<4>[ 9809.544825] CPU: 3 UID: 0 PID: 996 Comm: irq/75-iwlwifi: Not tainted 7.0.0-rc1 #444 PREEMPT(lazy)
<4>[ 9809.544832] Hardware name: HP HP Pavilion Aero Laptop 13-be0xxx/8916, BIOS F.17 12/18/2024
<4>[ 9809.544835] RIP: 0010:detach_if_pending (./include/linux/list.h:992 (discriminator 2) kernel/time/timer.c:891 (discriminator 2) kernel/time/timer.c:910 (discriminator 2))
<4>[ 9809.544845] Code: df 4c 89 ea 48 8b 2b 48 c1 ea 03 80 3c 02 00 0f 85 40 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 6b 08 4c 89 ea 48 c1 ea 03 <80> 3c 02 00 0f 85 14 02 00 00 49 89 6d 00 48 85 ed 74 23 48 b8 00
All code
========
   0:	df 4c 89 ea          	fisttps -0x16(%rcx,%rcx,4)
   4:	48 8b 2b             	mov    (%rbx),%rbp
   7:	48 c1 ea 03          	shr    $0x3,%rdx
   b:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)
   f:	0f 85 40 02 00 00    	jne    0x255
  15:	48 b8 00 00 00 00 00 	movabs $0xdffffc0000000000,%rax
  1c:	fc ff df 
  1f:	4c 8b 6b 08          	mov    0x8(%rbx),%r13
  23:	4c 89 ea             	mov    %r13,%rdx
  26:	48 c1 ea 03          	shr    $0x3,%rdx
  2a:*	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)		<-- trapping instruction
  2e:	0f 85 14 02 00 00    	jne    0x248
  34:	49 89 6d 00          	mov    %rbp,0x0(%r13)
  38:	48 85 ed             	test   %rbp,%rbp
  3b:	74 23                	je     0x60
  3d:	48                   	rex.W
  3e:	b8                   	.byte 0xb8
	...

Code starting with the faulting instruction
===========================================
   0:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)
   4:	0f 85 14 02 00 00    	jne    0x21e
   a:	49 89 6d 00          	mov    %rbp,0x0(%r13)
   e:	48 85 ed             	test   %rbp,%rbp
  11:	74 23                	je     0x36
  13:	48                   	rex.W
  14:	b8                   	.byte 0xb8
	...
<4>[ 9809.544849] RSP: 0018:ffffc90000708850 EFLAGS: 00010016
Oops#1 Part4
<4>[ 9809.544855] RAX: dffffc0000000000 RBX: ffff888107e710e0 RCX: ffffffff86373dde
<4>[ 9809.544859] RDX: 0000111060000000 RSI: ffffffff888b8ec0 RDI: ffffffff88f25138
<4>[ 9809.544862] RBP: dead000000000122 R08: 0000000000000000 R09: fffffbfff139f90c
<4>[ 9809.544865] R10: ffffffff89cfc867 R11: 0000000000000001 R12: 0000000000000000
<4>[ 9809.544868] R13: 0000888300000000 R14: ffff888107e710e8 R15: ffff88838d6af9c0
<4>[ 9809.544872] FS:  0000000000000000(0000) GS:ffff888403005000(0000) knlGS:0000000000000000
<4>[ 9809.544875] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[ 9809.544878] CR2: 00007fad9435741c CR3: 0000000076a91000 CR4: 0000000000750ef0
<4>[ 9809.544882] PKRU: 55555554
<4>[ 9809.544885] Call Trace:
<4>[ 9809.544888]  <IRQ>
<4>[ 9809.544893]  __mod_timer (kernel/time/timer.c:1097)
<4>[ 9809.544899]  ? __get_next_timer_interrupt (kernel/time/timer.c:1019)
<4>[ 9809.544904]  ? _raw_spin_unlock_irqrestore (./arch/x86/include/asm/paravirt.h:529 ./arch/x86/include/asm/irqflags.h:159 ./include/linux/spinlock_api_smp.h:178 kernel/locking/spinlock.c:194)
<4>[ 9809.544909]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4472 (discriminator 4))
<4>[ 9809.544914]  ? iommu_dma_free_iova (drivers/iommu/dma-iommu.c:245 drivers/iommu/dma-iommu.c:804)
<4>[ 9809.544921]  __iommu_dma_unmap (drivers/iommu/dma-iommu.c:814)
<4>[ 9809.544927]  ? iommu_get_msi_cookie (drivers/iommu/dma-iommu.c:814)
<4>[ 9809.544932]  ? __lock_acquire (kernel/locking/lockdep.c:4674 (discriminator 1) kernel/locking/lockdep.c:5191 (discriminator 1))
<4>[ 9809.544941]  iommu_dma_unmap_phys (./include/linux/swiotlb.h:145 ./include/linux/swiotlb.h:252 drivers/iommu/dma-iommu.c:1250 drivers/iommu/dma-iommu.c:1231)
<4>[ 9809.544947]  ? do_raw_spin_lock (./arch/x86/include/asm/atomic.h:107 (discriminator 1) ./include/linux/atomic/atomic-arch-fallback.h:2170 (discriminator 1) ./include/linux/atomic/atomic-instrumented.h:1302 (discriminator 1) ./include/asm-generic/qspinlock.h:111 (discriminator 1) kernel/locking/spinlock_debug.c:116 (discriminator 1))
<4>[ 9809.544952]  dma_unmap_phys (kernel/dma/mapping.c:212)
<4>[ 9809.544960] iwl_pcie_rx_handle (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1311 drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1565) iwlwifi
<4>[ 9809.544980]  ? kasan_save_stack (mm/kasan/common.c:59)
<4>[ 9809.544985]  ? kasan_save_stack (mm/kasan/common.c:58)
<4>[ 9809.544990]  ? __kasan_slab_free (mm/kasan/common.c:287)
<4>[ 9809.544994]  ? kmem_cache_free (mm/slub.c:6124 (discriminator 3) mm/slub.c:6254 (discriminator 3))
<4>[ 9809.544999]  ? handle_softirqs (./arch/x86/include/asm/jump_label.h:37 ./include/trace/events/irq.h:142 kernel/softirq.c:623)
<4>[ 9809.545003]  ? __irq_exit_rcu (kernel/softirq.c:657 kernel/softirq.c:496 kernel/softirq.c:723)
Oops#1 Part3
<4>[ 9809.545007]  ? irq_exit_rcu (kernel/softirq.c:741)
<4>[ 9809.545012]  ? cpu_startup_entry (kernel/sched/idle.c:429)
<4>[ 9809.545017]  ? start_secondary (arch/x86/kernel/smpboot.c:200 (discriminator 10) arch/x86/kernel/smpboot.c:280 (discriminator 10))
<4>[ 9809.545025]  ? iwl_pcie_rxq_alloc_rbs (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1499) iwlwifi
<4>[ 9809.545043] iwl_pcie_napi_poll_msix (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1042) iwlwifi
<4>[ 9809.545058]  __napi_poll.constprop.0 (net/core/dev.c:7684)
<4>[ 9809.545065]  net_rx_action (net/core/dev.c:7749 net/core/dev.c:7899)
<4>[ 9809.545072]  ? run_backlog_napi (net/core/dev.c:7861)
<4>[ 9809.545079]  ? rcu_momentary_eqs (kernel/rcu/tree.c:2541)
<4>[ 9809.545086]  ? mark_held_locks (kernel/locking/lockdep.c:4325 (discriminator 1))
<4>[ 9809.545091]  ? handle_softirqs (./arch/x86/include/asm/paravirt.h:529 kernel/softirq.c:606)
<4>[ 9809.545096]  handle_softirqs (./arch/x86/include/asm/jump_label.h:37 ./include/trace/events/irq.h:142 kernel/softirq.c:623)
<4>[ 9809.545103]  ? tasklet_unlock_wait (kernel/softirq.c:580)
<4>[ 9809.545108]  ? tasklet_unlock_wait (kernel/softirq.c:580)
<4>[ 9809.545112]  ? tick_nohz_stop_idle (./include/linux/seqlock.h:453 ./include/linux/seqlock.h:525 kernel/time/tick-sched.c:771)
<4>[ 9809.545119]  ? iwl_pcie_irq_rx_msix_handler (./include/linux/bottom_half.h:33 (discriminator 1) drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1666 (discriminator 1)) iwlwifi
<4>[ 9809.545134]  ? iwl_pcie_irq_rx_msix_handler (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1660 (discriminator 1)) iwlwifi
<4>[ 9809.545148]  do_softirq.part.0 (kernel/softirq.c:523 (discriminator 20))
<4>[ 9809.545153]  </IRQ>
<4>[ 9809.545156]  <TASK>
<4>[ 9809.545158]  __local_bh_enable_ip (kernel/softirq.c:515 (discriminator 1) kernel/softirq.c:450 (discriminator 1))
<4>[ 9809.545163]  ? iwl_pcie_irq_rx_msix_handler (./include/linux/bottom_half.h:33 (discriminator 1) drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1666 (discriminator 1)) iwlwifi
<4>[ 9809.545177] iwl_pcie_irq_rx_msix_handler (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1668 (discriminator 1)) iwlwifi
<4>[ 9809.545192]  ? iwl_pcie_rx_free (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1640) iwlwifi
<4>[ 9809.545206]  ? irq_thread (kernel/irq/manage.c:1058 (discriminator 3) kernel/irq/manage.c:1268 (discriminator 3))
<4>[ 9809.545211]  irq_thread_fn (kernel/irq/manage.c:1143)
<4>[ 9809.545216]  irq_thread (kernel/irq/manage.c:1272)
<4>[ 9809.545220]  ? find_held_lock (kernel/locking/lockdep.c:5350 (discriminator 1))
<4>[ 9809.545225]  ? __kthread_parkme (./include/linux/instrumented.h:82 ./include/asm-generic/bitops/instrumented-non-atomic.h:141 kernel/kthread.c:290)
<4>[ 9809.545230]  ? irq_copy_pending.isra.0 (kernel/irq/manage.c:1142)
<4>[ 9809.545235]  ? irq_forced_thread_fn (kernel/irq/manage.c:1245)
<4>[ 9809.545239]  ? _raw_spin_unlock_irqrestore (./arch/x86/include/asm/paravirt.h:529 ./arch/x86/include/asm/irqflags.h:159 ./include/linux/spinlock_api_smp.h:178 kernel/locking/spinlock.c:194)
Oops#1 Part2
<4>[ 9809.545243]  ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4472 (discriminator 4))
<4>[ 9809.545247]  ? irq_has_action (kernel/irq/manage.c:1179)
<4>[ 9809.545252]  ? __kthread_parkme (./arch/x86/include/asm/bitops.h:202 ./arch/x86/include/asm/bitops.h:232 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/kthread.c:290)
<4>[ 9809.545263]  ? irq_forced_thread_fn (kernel/irq/manage.c:1245)
<4>[ 9809.545270]  kthread (kernel/kthread.c:467)
<4>[ 9809.545277]  ? _raw_spin_unlock_irq (./arch/x86/include/asm/paravirt.h:529 ./include/linux/spinlock_api_smp.h:187 kernel/locking/spinlock.c:202)
<4>[ 9809.545284]  ? kthread_affine_node (kernel/kthread.c:412)
<4>[ 9809.545292]  ret_from_fork (arch/x86/kernel/process.c:164)
<4>[ 9809.545302]  ? exit_thread (arch/x86/kernel/process.c:153)
<4>[ 9809.545312]  ? __switch_to (./arch/x86/include/asm/cpufeature.h:101 arch/x86/kernel/process_64.c:377 arch/x86/kernel/process_64.c:665)
<4>[ 9809.545320]  ? kthread_affine_node (kernel/kthread.c:412)
<4>[ 9809.545330]  ret_from_fork_asm (arch/x86/entry/entry_64.S:255)
<4>[ 9809.545351]  </TASK>
<4>[ 9809.545357] Modules linked in: snd_seq_dummy snd_hrtimer snd_seq xt_conntrack nft_chain_nat xt_MASQUERADE nf_nat nf_conntrack_netlink nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 xfrm_user xfrm_algo xt_addrtype nft_compat x_tables nf_tables br_netfilter bridge stp llc ccm overlay qrtr rfcomm cmac algif_hash algif_skcipher af_alg bnep binfmt_misc ext4 mbcache jbd2 nls_ascii nls_cp437 vfat fat snd_acp3x_rn snd_acp3x_pdm_dma snd_soc_dmic intel_rapl_msr snd_soc_core snd_compress snd_hda_codec_generic amd_atl intel_rapl_common iwlmvm uvcvideo videobuf2_vmalloc btusb mac80211 snd_usb_audio snd_hda_codec_hdmi videobuf2_memops kvm_amd libarc4 btrtl uvc snd_usbmidi_lib snd_pci_acp6x btintel videobuf2_v4l2 snd_rawmidi btbcm videodev kvm snd_seq_device btmtk snd_hda_intel irqbypass snd_pci_acp5x videobuf2_common snd_hda_codec bluetooth snd_intel_dspcfg rapl mc snd_hwdep ecdh_generic ecc wmi_bmof snd_hda_core snd_rn_pci_acp3x iwlwifi snd_pcm snd_acp_config pcspkr ee1004 k10temp snd_soc_acpi snd_timer ac cfg80211 battery snd
Oops#1 Part1
<4>[ 9809.545588]  snd_pci_acp3x rfkill soundcore ccp joydev button amd_pmc sg acpi_tad evdev msr parport_pc ppdev lp parport efi_pstore nvme_fabrics fuse configfs nfnetlink efivarfs autofs4 btrfs xor libblake2b raid6_pq dm_crypt dm_mod r8153_ecm cdc_ether usbnet sd_mod hid_microsoft ff_memless hid_cmedia uas r8152 usb_storage mii scsi_mod libphy mdio_bus usbhid scsi_common amdgpu drm_client_lib i2c_algo_bit drm_ttm_helper ttm drm_exec drm_suballoc_helper drm_buddy drm_panel_backlight_quirks gpu_sched amdxcp hid_multitouch drm_display_helper sp5100_tco ucsi_acpi hid_generic drm_kms_helper xhci_pci watchdog video typec_ucsi cec i2c_hid_acpi roles i2c_piix4 xhci_hcd ghash_clmulni_intel nvme rc_core serio_raw i2c_hid amd_sfh typec i2c_smbus usbcore crc16 nvme_core hid thunderbolt fan usb_common wmi drm aesni_intel
<4>[ 9809.545854] ---[ end trace 0000000000000000 ]---
<4>[ 9809.764012] RIP: 0010:detach_if_pending (./include/linux/list.h:992 (discriminator 2) kernel/time/timer.c:891 (discriminator 2) kernel/time/timer.c:910 (discriminator 2))
<4>[ 9809.764021] Code: df 4c 89 ea 48 8b 2b 48 c1 ea 03 80 3c 02 00 0f 85 40 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 6b 08 4c 89 ea 48 c1 ea 03 <80> 3c 02 00 0f 85 14 02 00 00 49 89 6d 00 48 85 ed 74 23 48 b8 00
All code
========
   0:	df 4c 89 ea          	fisttps -0x16(%rcx,%rcx,4)
   4:	48 8b 2b             	mov    (%rbx),%rbp
   7:	48 c1 ea 03          	shr    $0x3,%rdx
   b:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)
   f:	0f 85 40 02 00 00    	jne    0x255
  15:	48 b8 00 00 00 00 00 	movabs $0xdffffc0000000000,%rax
  1c:	fc ff df 
  1f:	4c 8b 6b 08          	mov    0x8(%rbx),%r13
  23:	4c 89 ea             	mov    %r13,%rdx
  26:	48 c1 ea 03          	shr    $0x3,%rdx
  2a:*	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)		<-- trapping instruction
  2e:	0f 85 14 02 00 00    	jne    0x248
  34:	49 89 6d 00          	mov    %rbp,0x0(%r13)
  38:	48 85 ed             	test   %rbp,%rbp
  3b:	74 23                	je     0x60
  3d:	48                   	rex.W
  3e:	b8                   	.byte 0xb8
	...

Code starting with the faulting instruction
===========================================
   0:	80 3c 02 00          	cmpb   $0x0,(%rdx,%rax,1)
   4:	0f 85 14 02 00 00    	jne    0x21e
   a:	49 89 6d 00          	mov    %rbp,0x0(%r13)
   e:	48 85 ed             	test   %rbp,%rbp
  11:	74 23                	je     0x36
  13:	48                   	rex.W
  14:	b8                   	.byte 0xb8
	...
<4>[ 9809.764025] RSP: 0018:ffffc90000708850 EFLAGS: 00010016
<4>[ 9809.764031] RAX: dffffc0000000000 RBX: ffff888107e710e0 RCX: ffffffff86373dde
<4>[ 9809.764033] RDX: 0000111060000000 RSI: ffffffff888b8ec0 RDI: ffffffff88f25138
<4>[ 9809.764036] RBP: dead000000000122 R08: 0000000000000000 R09: fffffbfff139f90c
<4>[ 9809.764038] R10: ffffffff89cfc867 R11: 0000000000000001 R12: 0000000000000000
<4>[ 9809.764040] R13: 0000888300000000 R14: ffff888107e710e8 R15: ffff88838d6af9c0
<4>[ 9809.764043] FS:  0000000000000000(0000) GS:ffff888403005000(0000) knlGS:0000000000000000
<4>[ 9809.764046] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[ 9809.764048] CR2: 00007fad9435741c CR3: 0000000076a91000 CR4: 0000000000750ef0
<4>[ 9809.764051] PKRU: 55555554
<0>[ 9809.764054] Kernel panic - not syncing: Fatal exception in interrupt
<0>[ 9811.126715] Shutting down cpus with NMI
<0>[ 9811.126752] Kernel Offset: 0x4c00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)


This could possibly be related to a similar oops I saw last week, in
that case the stack trace didn't point to iwlwifi, but the oops appeared
to be the result of memory corruption:
https://lkml.org/lkml/2026/2/18/486

^ permalink raw reply

* Re: [PATCH 1/5] wifi: mt76: mt7921: refactor regulatory domain handling to regd.[ch]
From: Sean Wang @ 2026-02-23 20:38 UTC (permalink / raw)
  To: JB Tsai
  Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
	Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-1-jb.tsai@mediatek.com>

Hi JB,

Thanks for the patch  just a small nit:

On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Move regd logic to regd.c and regd.h files
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
>  .../wireless/mediatek/mt76/mt7921/Makefile    |   2 +-
>  .../net/wireless/mediatek/mt76/mt7921/init.c  |  98 +----------------
>  .../net/wireless/mediatek/mt76/mt7921/main.c  |   1 +
>  .../wireless/mediatek/mt76/mt7921/mt7921.h    |   1 -
>  .../net/wireless/mediatek/mt76/mt7921/pci.c   |   1 +
>  .../net/wireless/mediatek/mt76/mt7921/regd.c  | 104 ++++++++++++++++++
>  .../net/wireless/mediatek/mt76/mt7921/regd.h  |  13 +++
>  7 files changed, 121 insertions(+), 99 deletions(-)
>  create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.c
>  create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.h
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> index 2ad3c1cc3779..3ef7c9c45386 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> @@ -5,7 +5,7 @@ obj-$(CONFIG_MT7921E) += mt7921e.o
>  obj-$(CONFIG_MT7921S) += mt7921s.o
>  obj-$(CONFIG_MT7921U) += mt7921u.o
>
> -mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o
> +mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
>  mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
>  mt7921e-y := pci.o pci_mac.o pci_mcu.o
>  mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> index 29732315af1c..1fe2f2bc3881 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> @@ -9,6 +9,7 @@
>  #include "mt7921.h"
>  #include "../mt76_connac2_mac.h"
>  #include "mcu.h"
> +#include "regd.h"
>
>  static ssize_t mt7921_thermal_temp_show(struct device *dev,
>                                         struct device_attribute *attr,
> @@ -60,103 +61,6 @@ static int mt7921_thermal_init(struct mt792x_phy *phy)
>         return PTR_ERR_OR_ZERO(hwmon);
>  }
>
> -static void
> -mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> -{
> -#define IS_UNII_INVALID(idx, sfreq, efreq) \
> -       (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
> -       struct ieee80211_supported_band *sband;
> -       struct mt76_dev *mdev = &dev->mt76;
> -       struct device_node *np, *band_np;
> -       struct ieee80211_channel *ch;
> -       int i, cfreq;
> -
> -       np = mt76_find_power_limits_node(mdev);
> -
> -       sband = wiphy->bands[NL80211_BAND_5GHZ];
> -       band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
> -       for (i = 0; i < sband->n_channels; i++) {
> -               ch = &sband->channels[i];
> -               cfreq = ch->center_freq;
> -
> -               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> -                       ch->flags |= IEEE80211_CHAN_DISABLED;
> -                       continue;
> -               }
> -
> -               /* UNII-4 */
> -               if (IS_UNII_INVALID(0, 5845, 5925))
> -                       ch->flags |= IEEE80211_CHAN_DISABLED;
> -       }
> -
> -       sband = wiphy->bands[NL80211_BAND_6GHZ];
> -       if (!sband)
> -               return;
> -
> -       band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
> -       for (i = 0; i < sband->n_channels; i++) {
> -               ch = &sband->channels[i];
> -               cfreq = ch->center_freq;
> -
> -               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> -                       ch->flags |= IEEE80211_CHAN_DISABLED;
> -                       continue;
> -               }
> -
> -               /* UNII-5/6/7/8 */
> -               if (IS_UNII_INVALID(1, 5925, 6425) ||
> -                   IS_UNII_INVALID(2, 6425, 6525) ||
> -                   IS_UNII_INVALID(3, 6525, 6875) ||
> -                   IS_UNII_INVALID(4, 6875, 7125))
> -                       ch->flags |= IEEE80211_CHAN_DISABLED;
> -       }
> -}
> -
> -void mt7921_regd_update(struct mt792x_dev *dev)
> -{
> -       struct mt76_dev *mdev = &dev->mt76;
> -       struct ieee80211_hw *hw = mdev->hw;
> -       struct wiphy *wiphy = hw->wiphy;
> -
> -       mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
> -       mt7921_regd_channel_update(wiphy, dev);
> -       mt76_connac_mcu_set_channel_domain(hw->priv);
> -       mt7921_set_tx_sar_pwr(hw, NULL);
> -}
> -EXPORT_SYMBOL_GPL(mt7921_regd_update);
> -
> -static void
> -mt7921_regd_notifier(struct wiphy *wiphy,
> -                    struct regulatory_request *request)
> -{
> -       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> -       struct mt792x_dev *dev = mt792x_hw_dev(hw);
> -       struct mt76_connac_pm *pm = &dev->pm;
> -
> -       memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
> -       dev->mt76.region = request->dfs_region;
> -       dev->country_ie_env = request->country_ie_env;
> -
> -       if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
> -               if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
> -                       wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
> -               else
> -                       wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
> -       }
> -
> -       if (pm->suspended)
> -               return;
> -
> -       dev->regd_in_progress = true;
> -
> -       mt792x_mutex_acquire(dev);
> -       mt7921_regd_update(dev);
> -       mt792x_mutex_release(dev);
> -
> -       dev->regd_in_progress = false;
> -       wake_up(&dev->wait);
> -}
> -
>  int mt7921_mac_init(struct mt792x_dev *dev)
>  {
>         int i;
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 42b9514e04e7..00ca3d3f3ef0 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -7,6 +7,7 @@
>  #include <linux/module.h>
>  #include <net/ipv6.h>
>  #include "mt7921.h"
> +#include "regd.h"
>  #include "mcu.h"
>
>  static int
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> index ad92af98e314..5239ea970d24 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> @@ -246,7 +246,6 @@ mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val)
>  #define mt7921_l1_set(dev, addr, val)  mt7921_l1_rmw(dev, addr, 0, val)
>  #define mt7921_l1_clear(dev, addr, val)        mt7921_l1_rmw(dev, addr, val, 0)
>
> -void mt7921_regd_update(struct mt792x_dev *dev);
>  int mt7921_mac_init(struct mt792x_dev *dev);
>  bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
>  int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> index 65c7fe671137..a173a61f2b49 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> @@ -12,6 +12,7 @@
>  #include "../mt76_connac2_mac.h"
>  #include "../dma.h"
>  #include "mcu.h"
> +#include "regd.h"
>
>  static const struct pci_device_id mt7921_pci_device_table[] = {
>         { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961),
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> new file mode 100644
> index 000000000000..6e6c81189222
> --- /dev/null
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: ISC */

Please align the SPDX identifier in the new files with mt76
(BSD-3-Clause-Clear).

> +/* Copyright (C) 2025 MediaTek Inc. */
> +
> +#include <linux/of_net.h>
> +#include <linux/of.h>

linux/of.h should be sufficient here; we can drop linux/of_net.h.

> +#include "mt7921.h"
> +#include "regd.h"
> +#include "mcu.h"
> +
> +static void
> +mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> +{
> +#define IS_UNII_INVALID(idx, sfreq, efreq) \
> +       (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
> +       struct ieee80211_supported_band *sband;
> +       struct mt76_dev *mdev = &dev->mt76;
> +       struct device_node *np, *band_np;
> +       struct ieee80211_channel *ch;
> +       int i, cfreq;
> +
> +       np = mt76_find_power_limits_node(mdev);
> +
> +       sband = wiphy->bands[NL80211_BAND_5GHZ];
> +       band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
> +       for (i = 0; i < sband->n_channels; i++) {
> +               ch = &sband->channels[i];
> +               cfreq = ch->center_freq;
> +
> +               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> +                       ch->flags |= IEEE80211_CHAN_DISABLED;
> +                       continue;
> +               }
> +
> +               /* UNII-4 */
> +               if (IS_UNII_INVALID(0, 5845, 5925))
> +                       ch->flags |= IEEE80211_CHAN_DISABLED;
> +       }
> +
> +       sband = wiphy->bands[NL80211_BAND_6GHZ];
> +       if (!sband)
> +               return;
> +
> +       band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
> +       for (i = 0; i < sband->n_channels; i++) {
> +               ch = &sband->channels[i];
> +               cfreq = ch->center_freq;
> +
> +               if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> +                       ch->flags |= IEEE80211_CHAN_DISABLED;
> +                       continue;
> +               }
> +
> +               /* UNII-5/6/7/8 */
> +               if (IS_UNII_INVALID(1, 5925, 6425) ||
> +                   IS_UNII_INVALID(2, 6425, 6525) ||
> +                   IS_UNII_INVALID(3, 6525, 6875) ||
> +                   IS_UNII_INVALID(4, 6875, 7125))
> +                       ch->flags |= IEEE80211_CHAN_DISABLED;
> +       }
> +}
> +
> +void mt7921_regd_update(struct mt792x_dev *dev)
> +{
> +       struct mt76_dev *mdev = &dev->mt76;
> +       struct ieee80211_hw *hw = mdev->hw;
> +       struct wiphy *wiphy = hw->wiphy;
> +
> +       mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
> +       mt7921_regd_channel_update(wiphy, dev);
> +       mt76_connac_mcu_set_channel_domain(hw->priv);
> +       mt7921_set_tx_sar_pwr(hw, NULL);
> +}
> +EXPORT_SYMBOL_GPL(mt7921_regd_update);
> +
> +void mt7921_regd_notifier(struct wiphy *wiphy,
> +                         struct regulatory_request *request)
> +{
> +       struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> +       struct mt792x_dev *dev = mt792x_hw_dev(hw);
> +       struct mt76_connac_pm *pm = &dev->pm;
> +
> +       memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
> +       dev->mt76.region = request->dfs_region;
> +       dev->country_ie_env = request->country_ie_env;
> +
> +       if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
> +               if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
> +                       wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
> +               else
> +                       wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
> +       }
> +
> +       if (pm->suspended)
> +               return;
> +
> +       dev->regd_in_progress = true;
> +
> +       mt792x_mutex_acquire(dev);
> +       mt7921_regd_update(dev);
> +       mt792x_mutex_release(dev);
> +
> +       dev->regd_in_progress = false;
> +       wake_up(&dev->wait);
> +}

Looks good to me this is a straight move of the code with no extra
logic changes.

> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> new file mode 100644
> index 000000000000..0ba6161e1919
> --- /dev/null
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: ISC */

Please align the SPDX identifier in the new files with mt76
(BSD-3-Clause-Clear).

> +/* Copyright (C) 2025 MediaTek Inc. */
> +
> +#ifndef __MT7921_REGD_H
> +#define __MT7921_REGD_H
> +
> +#include "mt7921.h"
> +

I guess regd.h shouldn’t rely on indirect includes for struct
regulatory_request; please forward-declare it or include the proper
header.

> +void mt7921_regd_update(struct mt792x_dev *dev);
> +void mt7921_regd_notifier(struct wiphy *wiphy,
> +                         struct regulatory_request *request);
> +
> +#endif
> --
> 2.45.2
>
>

^ permalink raw reply

* Re: [PATCH ath-next] wifi: ath10k: use non devm for nvmem_cell_get
From: Loic Poulain @ 2026-02-23 20:52 UTC (permalink / raw)
  To: Rosen Penev
  Cc: linux-wireless, Jeff Johnson,
	open list:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER, open list
In-Reply-To: <20260223024854.18910-1-rosenp@gmail.com>

On Mon, Feb 23, 2026 at 3:49 AM Rosen Penev <rosenp@gmail.com> wrote:
>
> There's absolutely no need to extend the lifetime of cell to post
> removal of the driver. It's only used in this function.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>

Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>

> ---
>  drivers/net/wireless/ath/ath10k/core.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
> index 5c5bd5ef7175..38c5f5f95433 100644
> --- a/drivers/net/wireless/ath/ath10k/core.c
> +++ b/drivers/net/wireless/ath/ath10k/core.c
> @@ -1981,13 +1981,14 @@ static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name)
>         size_t len;
>         int ret;
>
> -       cell = devm_nvmem_cell_get(ar->dev, cell_name);
> +       cell = nvmem_cell_get(ar->dev, cell_name);
>         if (IS_ERR(cell)) {
>                 ret = PTR_ERR(cell);
>                 return ret;
>         }
>
>         buf = nvmem_cell_read(cell, &len);
> +       nvmem_cell_put(cell);
>         if (IS_ERR(buf))
>                 return PTR_ERR(buf);
>
> --
> 2.53.0
>
>

^ permalink raw reply

* Re: [PATCH 00/15] AES-CMAC library
From: Eric Biggers @ 2026-02-23 21:28 UTC (permalink / raw)
  To: linux-crypto
  Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
	linux-arm-kernel, linux-cifs, linux-wireless
In-Reply-To: <20260218213501.136844-1-ebiggers@kernel.org>

On Wed, Feb 18, 2026 at 01:34:46PM -0800, Eric Biggers wrote:
> This series can also be retrieved from:
> 
>     git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git aes-cmac-v1
> 
> This series adds support for AES CBC-based MACs to the crypto library,
> specifically AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC.  The
> implementation is fully optimized with the existing
> architecture-optimized AES code, either single-block AES en/decryption
> or arm64's neon_aes_mac_update() and ce_aes_mac_update().  As usual,
> optimizations are now enabled by default as well.
> 
> AES-CMAC support will be useful for at least the SMB client and server,
> and the bluetooth and mac80211 drivers.  Patches 8-15 convert these
> users to use the crypto library API instead of crypto_shash, though
> these patches will likely go in via subsystem trees later.  They result
> in some significant simplifications and performance improvements.
> 
> As usual, a KUnit test suite, FIPS self-test, and traditional crypto API
> wrapper algorithms are included as well.
> 
> Note that I'm also planning to add additional AES modes to the library.
> This is just an initial set of AES modes to get things started.
> Notably, with the SMB client and server already using the SHA* and MD5
> libraries, "cmac(aes)" was the only remaining use of crypto_shash there.
> So it makes sense to take care of that.
> 
> Eric Biggers (15):
>   lib/crypto: aes: Add support for CBC-based MACs
>   crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library
>   crypto: arm64/aes - Fix 32-bit aes_mac_update() arg treated as 64-bit
>   lib/crypto: arm64/aes: Move assembly code for AES modes into libaes
>   lib/crypto: arm64/aes: Migrate optimized CBC-based MACs into library
>   lib/crypto: tests: Add KUnit tests for CBC-based MACs
>   lib/crypto: aes: Add FIPS self-test for CMAC
>   smb: client: Use AES-CMAC library for SMB3 signature calculation
>   smb: client: Remove obsolete cmac(aes) allocation
>   smb: client: Make generate_key() return void
>   smb: client: Drop 'allocate_crypto' arg from smb*_calc_signature()
>   ksmbd: Use AES-CMAC library for SMB3 signature calculation
>   Bluetooth: SMP: Use AES-CMAC library API
>   wifi: mac80211: Use AES-CMAC library in ieee80211_aes_cmac()
>   wifi: mac80211: Use AES-CMAC library in aes_s2v()

Applied patches 1-7 and 14-15 to
https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/log/?h=libcrypto-next

Patches 8-13 (smb client, smb server, and bluetooth) can go in via
subsystem trees later.

I edited "lib/crypto: arm64/aes: Move assembly code for AES modes into
libaes" to update the file comments to remove the file paths.

- Eric

^ permalink raw reply

* [PATCHv2 wireless-next] wifi: rt2x00: use generic nvmem_cell_get
From: Rosen Penev @ 2026-02-23 21:40 UTC (permalink / raw)
  To: linux-wireless; +Cc: Stanislaw Gruszka, open list

The library doesn't necessarily depend on OF. This codepath is used by
both soc (OF only) and pci (no such requirement). After this, the only
of specific function is of_get_mac_address, which is needed for nvmem.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 65d0f805459c..93e4ce604171 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -10965,13 +10965,13 @@ EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);

 int rt2800_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
 {
-	struct device_node *np = rt2x00dev->dev->of_node;
+	struct device *dev = rt2x00dev->dev;
 	unsigned int len = rt2x00dev->ops->eeprom_size;
 	struct nvmem_cell *cell;
 	const void *data;
 	size_t retlen;

-	cell = of_nvmem_cell_get(np, "eeprom");
+	cell = nvmem_cell_get(dev, "eeprom");
 	if (IS_ERR(cell))
 		return PTR_ERR(cell);

--
2.53.0


^ permalink raw reply related

* [PATCH 24/62] net/cw1200: Fix locking in error paths
From: Bart Van Assche @ 2026-02-23 22:00 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Christoph Hellwig, Steven Rostedt, Nick Desaulniers,
	Nathan Chancellor, Kees Cook, Jann Horn, Bart Van Assche,
	Kalle Valo, Johannes Berg, linux-wireless
In-Reply-To: <20260223220102.2158611-1-bart.vanassche@linux.dev>

From: Bart Van Assche <bvanassche@acm.org>

cw1200_wow_suspend() must only return with priv->conf_mutex locked if it
returns zero. This mutex must be unlocked if an error is returned. Add
mutex_unlock() calls to the error paths from which that call is missing.
This has been detected by the Clang thread-safety analyzer.

Cc: Kalle Valo <kvalo@codeaurora.org>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-wireless@vger.kernel.org
Fixes: a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/net/wireless/st/cw1200/pm.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index 120f0379f81d..84eb15d729c7 100644
--- a/drivers/net/wireless/st/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
@@ -264,12 +264,14 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
 		wiphy_err(priv->hw->wiphy,
 			  "PM request failed: %d. WoW is disabled.\n", ret);
 		cw1200_wow_resume(hw);
+		mutex_unlock(&priv->conf_mutex);
 		return -EBUSY;
 	}
 
 	/* Force resume if event is coming from the device. */
 	if (atomic_read(&priv->bh_rx)) {
 		cw1200_wow_resume(hw);
+		mutex_unlock(&priv->conf_mutex);
 		return -EAGAIN;
 	}
 

^ permalink raw reply related

* [PATCH 25/62] wlcore: Fix a locking bug
From: Bart Van Assche @ 2026-02-23 22:00 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Christoph Hellwig, Steven Rostedt, Nick Desaulniers,
	Nathan Chancellor, Kees Cook, Jann Horn, Bart Van Assche,
	Johannes Berg, Eyal Reizer, Tony Lindgren, Kalle Valo,
	linux-wireless
In-Reply-To: <20260223220102.2158611-1-bart.vanassche@linux.dev>

From: Bart Van Assche <bvanassche@acm.org>

Make sure that wl->mutex is locked before it is unlocked. This has been
detected by the Clang thread-safety analyzer.

Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: Eyal Reizer <eyalr@ti.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Kalle Valo <kvalo@codeaurora.org>
Cc: linux-wireless@vger.kernel.org
Fixes: 45aa7f071b06 ("wlcore: Use generic runtime pm calls for wowlan elp configuration")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/net/wireless/ti/wlcore/main.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 17dd417756f2..1c340a4a0930 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1875,6 +1875,8 @@ static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
 		     wl->wow_enabled);
 	WARN_ON(!wl->wow_enabled);
 
+	mutex_lock(&wl->mutex);
+
 	ret = pm_runtime_force_resume(wl->dev);
 	if (ret < 0) {
 		wl1271_error("ELP wakeup failure!");
@@ -1891,8 +1893,6 @@ static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
 		run_irq_work = true;
 	spin_unlock_irqrestore(&wl->wl_lock, flags);
 
-	mutex_lock(&wl->mutex);
-
 	/* test the recovery flag before calling any SDIO functions */
 	pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
 				    &wl->flags);

^ permalink raw reply related

* 6.18.13 iwlwifi deadlock allocating cma while work-item is active.
From: Ben Greear @ 2026-02-23 22:36 UTC (permalink / raw)
  To: linux-wireless; +Cc: Korenblit, Miriam Rachel, linux-mm

Hello,

I hit a deadlock related to CMA mem allocation attempting to flush all work
while holding some wifi related mutex, and with a work-queue attempting to process a wifi regdomain
work item.  I really don't see any good way to fix this,
it would seem that any code that was holding a mutex that could block a work-queue
cannot safely allocate CMA memory?  Hopefully someone else has a better idea.

For whatever reason, my hacked up kernel will print out the sysrq process stack traces I need
to understand this, and my stable 6.18.13 will not.  But, the locks-held matches in both cases, so almost
certainly this is same problem.  I can reproduce the same problem on both un-modified stable
and my own.  The details below are from my modified 6.18.9+ kernel.

I only hit this (reliably?) with a KASAN enabled kernel, likely because it makes things slow enough to
hit the problem and/or causes CMA allocations in a different manner.

General way to reproduce is to have large amounts of intel be200 radios in a system, and bring them
admin up and down.


## From 6.18.13 (un-modified)

40479 Feb 23 14:13:31 ct523c-de7c kernel: 5 locks held by kworker/u32:11/34989:
40480 Feb 23 14:13:31 ct523c-de7c kernel:  #0: ffff888120161148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work+0xf7a/0x17b0
40481 Feb 23 14:13:31 ct523c-de7c kernel:  #1: ffff8881a561fd20 ((work_completion)(&rdev->wiphy_work)){+.+.}-{0:0}, at: process_one_work+0x7ca/0x17b0
40482 Feb 23 14:13:31 ct523c-de7c kernel:  #2: ffff88815e618788 (&rdev->wiphy.mtx){+.+.}-{4:4}, at: cfg80211_wiphy_work+0x5c/0x570 [cfg80211]
40483 Feb 23 14:13:31 ct523c-de7c kernel:  #3: ffffffff87232e60 (&cma->alloc_mutex){+.+.}-{4:4}, at: __cma_alloc+0x3c5/0xd20
40484 Feb 23 14:13:31 ct523c-de7c kernel:  #4: ffffffff8534f668 (lock#5){+.+.}-{4:4}, at: __lru_add_drain_all+0x5f/0x530

40488 Feb 23 14:13:31 ct523c-de7c kernel: 4 locks held by kworker/1:0/39480:
40489 Feb 23 14:13:31 ct523c-de7c kernel:  #0: ffff88812006b148 ((wq_completion)events){+.+.}-{0:0}, at: process_one_work+0xf7a/0x17b0
40490 Feb 23 14:13:31 ct523c-de7c kernel:  #1: ffff88814087fd20 (reg_work){+.+.}-{0:0}, at: process_one_work+0x7ca/0x17b0
40491 Feb 23 14:13:31 ct523c-de7c kernel:  #2: ffffffff85970028 (rtnl_mutex){+.+.}-{4:4}, at: reg_todo+0x18/0x770 [cfg80211]
40492 Feb 23 14:13:31 ct523c-de7c kernel:  #3: ffff88815e618788 (&rdev->wiphy.mtx){+.+.}-{4:4}, at: reg_process_self_managed_hints+0x70/0x190 [cfg80211]


## Rest of this is from my 6.18.9+ hacks kernel.

### thread trying to allocate cma is blocked here, trying to flush work.

Type "apropos word" to search for commands related to "word"...
Reading symbols from vmlinux...
(gdb) l *(alloc_contig_range_noprof+0x1de)
0xffffffff8162453e is in alloc_contig_range_noprof (/home2/greearb/git/linux-6.18.dev.y/mm/page_alloc.c:6798).
6793			.reason = MR_CONTIG_RANGE,
6794		};
6795	
6796		lru_cache_disable();
6797	
6798		while (pfn < end || !list_empty(&cc->migratepages)) {
6799			if (fatal_signal_pending(current)) {
6800				ret = -EINTR;
6801				break;
6802			}
(gdb) l *(__lru_add_drain_all+0x19b)
0xffffffff815ae44b is in __lru_add_drain_all (/home2/greearb/git/linux-6.18.dev.y/mm/swap.c:884).
879				queue_work_on(cpu, mm_percpu_wq, work);
880				__cpumask_set_cpu(cpu, &has_work);
881			}
882		}
883	
884		for_each_cpu(cpu, &has_work)
885			flush_work(&per_cpu(lru_add_drain_work, cpu));
886	
887	done:
888		mutex_unlock(&lock);
(gdb)


#### and other thread is trying to process a regdom request, and trying to use
# rcu and rtnl???

Type "apropos word" to search for commands related to "word"...
Reading symbols from net/wireless/cfg80211.ko...
(gdb) l *(reg_todo+0x18)
0xe238 is in reg_todo (/home2/greearb/git/linux-6.18.dev.y/net/wireless/reg.c:3107).
3102	 */
3103	static void reg_process_pending_hints(void)
3104	{
3105		struct regulatory_request *reg_request, *lr;
3106	
3107		lr = get_last_request();
3108	
3109		/* When last_request->processed becomes true this will be rescheduled */
3110		if (lr && !lr->processed) {
3111			pr_debug("Pending regulatory request, waiting for it to be processed...\n");
(gdb)

static struct regulatory_request *get_last_request(void)
{
	return rcu_dereference_rtnl(last_request);
}


task:kworker/6:0     state:D stack:0     pid:56    tgid:56    ppid:2      task_flags:0x4208060 flags:0x00080000
Workqueue: events reg_todo [cfg80211]
Call Trace:
  <TASK>
  __schedule+0x526/0x1290
  preempt_schedule_notrace+0x35/0x50
  preempt_schedule_notrace_thunk+0x16/0x30
  rcu_is_watching+0x2a/0x30
  lock_acquire+0x26d/0x2c0
  schedule+0xac/0x120
  ? schedule+0x8d/0x120
  schedule_preempt_disabled+0x11/0x20
  __mutex_lock+0x726/0x1070
  ? reg_todo+0x18/0x2b0 [cfg80211]
  ? reg_todo+0x18/0x2b0 [cfg80211]
  reg_todo+0x18/0x2b0 [cfg80211]
  process_one_work+0x221/0x6d0
  worker_thread+0x1e5/0x3b0
  ? rescuer_thread+0x450/0x450
  kthread+0x108/0x220
  ? kthreads_online_cpu+0x110/0x110
  ret_from_fork+0x1c6/0x220
  ? kthreads_online_cpu+0x110/0x110
  ret_from_fork_asm+0x11/0x20
  </TASK>

task:ip              state:D stack:0     pid:72857 tgid:72857 ppid:72843  task_flags:0x400100 flags:0x00080001
Call Trace:
  <TASK>
  __schedule+0x526/0x1290
  ? schedule+0x8d/0x120
  ? schedule+0xe2/0x120
  schedule+0x36/0x120
  schedule_timeout+0xf9/0x110
  ? mark_held_locks+0x40/0x70
  __wait_for_common+0xbe/0x1e0
  ? hrtimer_nanosleep_restart+0x120/0x120
  ? __flush_work+0x20b/0x530
  __flush_work+0x34e/0x530
  ? flush_workqueue_prep_pwqs+0x160/0x160
  ? bpf_prog_test_run_tracing+0x160/0x2d0
  __lru_add_drain_all+0x19b/0x220
  alloc_contig_range_noprof+0x1de/0x8a0
  __cma_alloc+0x1f1/0x6a0
  __dma_direct_alloc_pages.isra.0+0xcb/0x2f0
  dma_direct_alloc+0x7b/0x250
  dma_alloc_attrs+0xa1/0x2a0
  _iwl_pcie_ctxt_info_dma_alloc_coherent+0x31/0xb0 [iwlwifi]
  iwl_pcie_ctxt_info_alloc_dma+0x20/0x50 [iwlwifi]
  iwl_pcie_init_fw_sec+0x2fc/0x380 [iwlwifi]
  iwl_pcie_ctxt_info_v2_alloc+0x19e/0x530 [iwlwifi]
  iwl_trans_pcie_gen2_start_fw+0x2e2/0x820 [iwlwifi]
  ? lock_is_held_type+0x92/0x100
  iwl_trans_start_fw+0x77/0x90 [iwlwifi]
  iwl_mld_load_fw_wait_alive+0x97/0x2c0 [iwlmld]
  ? iwl_mld_mac80211_sta_state+0x780/0x780 [iwlmld]
  ? lock_is_held_type+0x92/0x100
  iwl_mld_load_fw+0x91/0x240 [iwlmld]
  ? ieee80211_open+0x3d/0xe0 [mac80211]
  ? lock_is_held_type+0x92/0x100
  iwl_mld_start_fw+0x44/0x470 [iwlmld]
  iwl_mld_mac80211_start+0x3d/0x1b0 [iwlmld]
  drv_start+0x6f/0x1d0 [mac80211]
  ieee80211_do_open+0x2d6/0x960 [mac80211]
  ieee80211_open+0x62/0xe0 [mac80211]
  __dev_open+0x11a/0x2e0
  __dev_change_flags+0x1f8/0x280
  netif_change_flags+0x22/0x60
  do_setlink.isra.0+0xe57/0x11a0
  ? __mutex_lock+0xb0/0x1070
  ? __mutex_lock+0x99e/0x1070
  ? __nla_validate_parse+0x5e/0xcd0
  ? rtnl_newlink+0x355/0xb50
  ? cap_capable+0x90/0x100
  ? security_capable+0x72/0x80
  rtnl_newlink+0x7e8/0xb50
  ? __lock_acquire+0x436/0x2190
  ? lock_acquire+0xc2/0x2c0
  ? rtnetlink_rcv_msg+0x97/0x660
  ? find_held_lock+0x2b/0x80
  ? do_setlink.isra.0+0x11a0/0x11a0
  ? rtnetlink_rcv_msg+0x3ea/0x660
  ? lock_release+0xcc/0x290
  ? do_setlink.isra.0+0x11a0/0x11a0
  rtnetlink_rcv_msg+0x409/0x660
  ? rtnl_fdb_dump+0x240/0x240
  netlink_rcv_skb+0x56/0x100
  netlink_unicast+0x1e1/0x2d0
  netlink_sendmsg+0x219/0x460
  __sock_sendmsg+0x38/0x70
  ____sys_sendmsg+0x214/0x280
  ? import_iovec+0x2c/0x30
  ? copy_msghdr_from_user+0x6c/0xa0
  ___sys_sendmsg+0x85/0xd0
  ? __lock_acquire+0x436/0x2190
  ? find_held_lock+0x2b/0x80
  ? lock_acquire+0xc2/0x2c0
  ? mntput_no_expire+0x43/0x460
  ? find_held_lock+0x2b/0x80
  ? mntput_no_expire+0x8c/0x460
  __sys_sendmsg+0x6b/0xc0
  do_syscall_64+0x6b/0x11b0
  entry_SYSCALL_64_after_hwframe+0x4b/0x53

Thanks,
Ben

-- 
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc  http://www.candelatech.com


^ permalink raw reply

* Re: [PATCH 2/5] wifi: mt76: mt7921: refactor CLC support check flow
From: Sean Wang @ 2026-02-23 22:41 UTC (permalink / raw)
  To: JB Tsai
  Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
	Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-2-jb.tsai@mediatek.com>

HI, JB

On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Move the disable_clc module parameter to regd.c and introduce
> mt7925_regd_clc_supported() to centralize CLC support checks.

typo, s/7925/7921/

>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt7921/mcu.c  | 14 ++++++++------
>  drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 13 +++++++++++++
>  drivers/net/wireless/mediatek/mt76/mt7921/regd.h |  1 +
>  3 files changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> index 8442dbd2ee23..1e2afa736cdf 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> @@ -4,6 +4,7 @@
>  #include <linux/fs.h>
>  #include <linux/firmware.h>
>  #include "mt7921.h"
> +#include "regd.h"
>  #include "mcu.h"
>  #include "../mt76_connac2_mac.h"
>  #include "../mt792x_trace.h"
> @@ -11,10 +12,6 @@
>  #define MT_STA_BFER                    BIT(0)
>  #define MT_STA_BFEE                    BIT(1)
>
> -static bool mt7921_disable_clc;
> -module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
> -MODULE_PARM_DESC(disable_clc, "disable CLC support");
> -
>  int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd,
>                               struct sk_buff *skb, int seq)
>  {
> @@ -422,8 +419,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
>         u8 *clc_base = NULL, hw_encap = 0;
>
>         dev->phy.clc_chan_conf = 0xff;
> -       if (mt7921_disable_clc ||
> -           mt76_is_usb(&dev->mt76))
> +       if (!mt7921_regd_clc_supported(dev))
>                 return 0;
>
>         if (mt76_is_mmio(&dev->mt76)) {
> @@ -470,6 +466,9 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
>         for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) {
>                 clc = (const struct mt7921_clc *)(clc_base + offset);
>
> +               if (clc->idx >= ARRAY_SIZE(phy->clc))
> +                       break;
> +

I’d keep the refactor logic-preserving. This bounds check is a
correctness fix (prevents OOB on phy->clc[clc->idx])
so please send it as a separate hardening patch and describe the failure and fix
Also, ARRAY_SIZE() is for compile-time arrays only if phy->clc were a
pointer, it would be invalid/misleading I guess.

>                 /* do not init buf again if chip reset triggered */
>                 if (phy->clc[clc->idx])
>                         continue;
> @@ -1403,6 +1402,9 @@ int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
>         struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy;
>         int i, ret;
>
> +       if (!ARRAY_SIZE(phy->clc))
> +               return -ESRCH;
> +

Ditto,  to keep the refactor logic-preserving, send it as a separate
hardening patch and describe the failure and fix and also
!ARRAY_SIZE(phy->clc) isn’t a meaningful runtime check I guess.

>         /* submit all clc config */
>         for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
>                 ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> index 6e6c81189222..70440ab8ba82 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -7,6 +7,19 @@
>  #include "regd.h"
>  #include "mcu.h"
>
> +static bool mt7921_disable_clc;
> +module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
> +MODULE_PARM_DESC(disable_clc, "disable CLC support");
> +

should we explicitly include <linux/moduleparam.h> ?

> +bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
> +{
> +       if (mt7921_disable_clc ||
> +           mt76_is_usb(&dev->mt76))
> +               return false;
> +
> +       return true;
> +}
> +
>  static void
>  mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
>  {
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> index 0ba6161e1919..74bc2fdd532c 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -9,5 +9,6 @@
>  void mt7921_regd_update(struct mt792x_dev *dev);
>  void mt7921_regd_notifier(struct wiphy *wiphy,
>                           struct regulatory_request *request);
> +bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
>
>  #endif
> --
> 2.45.2
>
>

^ permalink raw reply

* [PATCH ath-next] wifi: ath9k: owl: move name into owl_nvmem_probe
From: Rosen Penev @ 2026-02-23 22:42 UTC (permalink / raw)
  To: linux-wireless
  Cc: Toke Høiland-Jørgensen, Andreas Färber,
	Manivannan Sadhasivam,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
	moderated list:ARM/ACTIONS SEMI ARCHITECTURE, open list

There is no need for dynamic allocation for a simple string.
request_firmware_nowait copies the string internally anyway.

The error message on failure is also wrong. It's an allocation failure,
not a find failure.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 .../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 31 +++++--------------
 1 file changed, 7 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
index fe1013a3a588..b9ef34709202 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
+++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
@@ -137,24 +137,6 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
 	release_firmware(fw);
 }

-static const char *owl_get_eeprom_name(struct pci_dev *pdev)
-{
-	struct device *dev = &pdev->dev;
-	char *eeprom_name;
-
-	dev_dbg(dev, "using auto-generated eeprom filename\n");
-
-	eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
-	if (!eeprom_name)
-		return NULL;
-
-	/* this should match the pattern used in ath9k/init.c */
-	scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
-		  dev_name(dev));
-
-	return eeprom_name;
-}
-
 static void owl_nvmem_work(struct work_struct *work)
 {
 	struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
@@ -195,8 +177,9 @@ static int owl_nvmem_probe(struct owl_ctx *ctx)
 static int owl_probe(struct pci_dev *pdev,
 		     const struct pci_device_id *id)
 {
+	char eeprom_name[EEPROM_FILENAME_LEN];
+	struct device *dev = &pdev->dev;
 	struct owl_ctx *ctx;
-	const char *eeprom_name;
 	int err = 0;

 	if (pci_enable_device(pdev))
@@ -215,11 +198,11 @@ static int owl_probe(struct pci_dev *pdev,
 	if (err <= 0)
 		return err;

-	eeprom_name = owl_get_eeprom_name(pdev);
-	if (!eeprom_name) {
-		dev_err(&pdev->dev, "no eeprom filename found.\n");
-		return -ENODEV;
-	}
+	dev_dbg(dev, "using auto-generated eeprom filename\n");
+
+	/* this should match the pattern used in ath9k/init.c */
+	scnprintf(eeprom_name, sizeof(eeprom_name), "ath9k-eeprom-pci-%s.bin",
+		  dev_name(dev));

 	err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
 				      &pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
--
2.53.0


^ permalink raw reply related

* [PATCH ath-next] wifi: ath9k: use kmemdup and kcalloc
From: Rosen Penev @ 2026-02-23 22:44 UTC (permalink / raw)
  To: linux-wireless; +Cc: Toke Høiland-Jørgensen, open list

Simplifies the code slightly by removing temporary variables.
multiplication overflow is also gained for free.

Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
 drivers/net/wireless/ath/ath9k/ar9002_hw.c   | 6 +++---
 drivers/net/wireless/ath/ath9k/common-init.c | 8 ++------
 drivers/net/wireless/ath/ath9k/init.c        | 8 +++-----
 drivers/net/wireless/ath/ath9k/recv.c        | 4 +---
 4 files changed, 9 insertions(+), 17 deletions(-)

diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index b26224480041..0f24539b75ec 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -80,14 +80,14 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
 	/* iniAddac needs to be modified for these chips */
 	if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) {
 		struct ar5416IniArray *addac = &ah->iniAddac;
-		u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns;
+		u32 n = addac->ia_rows * addac->ia_columns;
 		u32 *data;

-		data = devm_kzalloc(ah->dev, size, GFP_KERNEL);
+		data = devm_kmemdup_array(ah->dev, addac->ia_array, n, sizeof(u32),
+			GFP_KERNEL);
 		if (!data)
 			return -ENOMEM;

-		memcpy(data, addac->ia_array, size);
 		addac->ia_array = data;

 		if (!AR_SREV_5416_22_OR_LATER(ah)) {
diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c
index da102c791712..52e02e0752d8 100644
--- a/drivers/net/wireless/ath/ath9k/common-init.c
+++ b/drivers/net/wireless/ath/ath9k/common-init.c
@@ -133,13 +133,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
 		     ATH9K_NUM_CHANNELS);

 	if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
-		channels = devm_kzalloc(ah->dev,
+		channels = devm_kmemdup(ah->dev, ath9k_2ghz_chantable,
 			sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
 		if (!channels)
 		    return -ENOMEM;

-		memcpy(channels, ath9k_2ghz_chantable,
-		       sizeof(ath9k_2ghz_chantable));
 		common->sbands[NL80211_BAND_2GHZ].channels = channels;
 		common->sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ;
 		common->sbands[NL80211_BAND_2GHZ].n_channels =
@@ -150,13 +148,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
 	}

 	if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
-		channels = devm_kzalloc(ah->dev,
+		channels = devm_kmemdup(ah->dev, ath9k_5ghz_chantable,
 			sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
 		if (!channels)
 			return -ENOMEM;

-		memcpy(channels, ath9k_5ghz_chantable,
-		       sizeof(ath9k_5ghz_chantable));
 		common->sbands[NL80211_BAND_5GHZ].channels = channels;
 		common->sbands[NL80211_BAND_5GHZ].band = NL80211_BAND_5GHZ;
 		common->sbands[NL80211_BAND_5GHZ].n_channels =
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index b52ddb237dcf..e52775dda6a7 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -297,7 +297,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 {
 	struct ath_common *common = ath9k_hw_common(sc->sc_ah);
 	u8 *ds;
-	int i, bsize, desc_len;
+	int i, desc_len;

 	ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
 		name, nbuf, ndesc);
@@ -351,8 +351,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 	if (is_tx) {
 		struct ath_buf *bf;

-		bsize = sizeof(struct ath_buf) * nbuf;
-		bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+		bf = devm_kcalloc(sc->dev, sizeof(*bf), nbuf, GFP_KERNEL);
 		if (!bf)
 			return -ENOMEM;

@@ -382,8 +381,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
 	} else {
 		struct ath_rxbuf *bf;

-		bsize = sizeof(struct ath_rxbuf) * nbuf;
-		bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+		bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbuf, GFP_KERNEL);
 		if (!bf)
 			return -ENOMEM;

diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 34c74ed99b7b..93b41a1bb2af 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -202,7 +202,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
 	struct sk_buff *skb;
 	struct ath_rxbuf *bf;
 	int error = 0, i;
-	u32 size;

 	ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
 				    ah->caps.rx_status_len);
@@ -212,8 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
 	ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
 			       ah->caps.rx_hp_qdepth);

-	size = sizeof(struct ath_rxbuf) * nbufs;
-	bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
+	bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbufs, GFP_KERNEL);
 	if (!bf)
 		return -ENOMEM;

--
2.53.0


^ permalink raw reply related

* Re: [PATCH 3/5] wifi: mt76: mt7921: refactor regulatory notifier flow
From: Sean Wang @ 2026-02-23 23:17 UTC (permalink / raw)
  To: JB Tsai
  Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
	Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-3-jb.tsai@mediatek.com>

Hi, JB

On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Rename mt7921_regd_update() to mt7921_mcu_regd_update() to centralize
> regd updates with error handling.
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
>  .../net/wireless/mediatek/mt76/mt7921/pci.c   |  2 +-
>  .../net/wireless/mediatek/mt76/mt7921/regd.c  | 45 +++++++++++++------
>  .../net/wireless/mediatek/mt76/mt7921/regd.h  |  3 +-
>  3 files changed, 35 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> index a173a61f2b49..3fdf55c056a6 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> @@ -545,7 +545,7 @@ static int mt7921_pci_resume(struct device *device)
>         if (err < 0)
>                 goto failed;
>
> -       mt7921_regd_update(dev);
> +       mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
>         err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED);
>  failed:
>         pm->suspended = false;
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> index 70440ab8ba82..f795ee2eb446 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -72,18 +72,43 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
>         }
>  }
>
> -void mt7921_regd_update(struct mt792x_dev *dev)
> +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
> +                          enum environment_cap country_ie_env)

To keep naming consistent, how about renaming mt7921_mcu_regd_update
to mt7921_regd_mcu_update, matching the mt7921_regd_* prefix?

>  {
>         struct mt76_dev *mdev = &dev->mt76;
>         struct ieee80211_hw *hw = mdev->hw;
>         struct wiphy *wiphy = hw->wiphy;
> +       int ret = 0;
> +
> +       dev->regd_in_progress = true;
> +
> +       mt792x_mutex_acquire(dev);
> +       if (!dev->regd_change)
> +               goto err;
> +
> +       ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
> +       if (ret < 0)
> +               goto err;
>

I’d prefer to keep the refactor logic-preserving. The new error
handling and flag sequencing  the dev->regd_change handling looks like
a functional change, so it would be clearer as a follow-up patch where
the behavior change can be explained. For example, the
dev->regd_change handling could be moved to the “wifi: mt76: mt7921:
add auto regdomain switch support” patch so the regd_change flow is
handled end-to-end there.

> -       mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
>         mt7921_regd_channel_update(wiphy, dev);
> -       mt76_connac_mcu_set_channel_domain(hw->priv);
> -       mt7921_set_tx_sar_pwr(hw, NULL);
> +
> +       ret = mt76_connac_mcu_set_channel_domain(hw->priv);
> +       if (ret < 0)
> +               goto err;
> +
> +       ret = mt7921_set_tx_sar_pwr(hw, NULL);
> +       if (ret < 0)
> +               goto err;
> +
> +err:
> +       mt792x_mutex_release(dev);
> +       dev->regd_change = false;
> +       dev->regd_in_progress = false;
> +       wake_up(&dev->wait);
> +
> +       return ret;
>  }
> -EXPORT_SYMBOL_GPL(mt7921_regd_update);
> +EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update);
>
>  void mt7921_regd_notifier(struct wiphy *wiphy,
>                           struct regulatory_request *request)
> @@ -106,12 +131,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
>         if (pm->suspended)
>                 return;
>
> -       dev->regd_in_progress = true;
> -
> -       mt792x_mutex_acquire(dev);
> -       mt7921_regd_update(dev);
> -       mt792x_mutex_release(dev);
> -
> -       dev->regd_in_progress = false;
> -       wake_up(&dev->wait);
> +       mt7921_mcu_regd_update(dev, request->alpha2,
> +                              request->country_ie_env);
>  }
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> index 74bc2fdd532c..da5bd4450312 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -6,7 +6,8 @@
>
>  #include "mt7921.h"
>
> -void mt7921_regd_update(struct mt792x_dev *dev);
> +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
> +                          enum environment_cap country_ie_env);
>  void mt7921_regd_notifier(struct wiphy *wiphy,
>                           struct regulatory_request *request);
>  bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
> --
> 2.45.2
>
>

^ permalink raw reply

* Re: [PATCH 4/5] wifi: mt76: mt7921: add auto regdomain switch support
From: Sean Wang @ 2026-02-23 23:45 UTC (permalink / raw)
  To: JB Tsai
  Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
	Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-4-jb.tsai@mediatek.com>

Hi, JB

On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Implement 802.11d-based automatic regulatory domain switching to
> dynamically determine the regulatory domain at runtime.
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
>  .../wireless/mediatek/mt76/mt76_connac_mcu.h  |  3 +-
>  .../net/wireless/mediatek/mt76/mt7921/mac.c   |  3 +
>  .../net/wireless/mediatek/mt76/mt7921/main.c  | 12 ++-
>  .../net/wireless/mediatek/mt76/mt7921/mcu.c   |  3 +-
>  .../net/wireless/mediatek/mt76/mt7921/regd.c  | 81 +++++++++++++++++--
>  .../net/wireless/mediatek/mt76/mt7921/regd.h  |  2 +
>  6 files changed, 93 insertions(+), 11 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
> index f44977f9093d..263778be4a34 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
> @@ -1586,7 +1586,7 @@ struct mt76_connac_hw_scan_done {
>         u8 pno_enabled;
>         u8 pad2[3];
>         u8 sparse_channel_valid_num;
> -       u8 pad3[3];
> +       u8 alpha2[3];
>         u8 channel_num[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
>         /* idle format for channel_idle_time
>          * 0: first bytes: idle time(ms) 2nd byte: dwell time(ms)
> @@ -1599,6 +1599,7 @@ struct mt76_connac_hw_scan_done {
>         u8 mdrdy_count[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
>         __le32 beacon_2g_num;
>         __le32 beacon_5g_num;
> +       __le16 channel_scan_time[MT76_CONNAC_SCAN_DONE_EVENT_MAX_CHANNEL_NUM];
>  } __packed;
>
>  struct mt76_connac_sched_scan_req {
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> index 03b4960db73f..bcca4b17e8f2 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mac.c
> @@ -7,6 +7,7 @@
>  #include "mt7921.h"
>  #include "../dma.h"
>  #include "../mt76_connac2_mac.h"
> +#include "regd.h"
>  #include "mcu.h"
>
>  #define MT_WTBL_TXRX_CAP_RATE_OFFSET   7
> @@ -697,6 +698,8 @@ void mt7921_mac_reset_work(struct work_struct *work)
>                                             IEEE80211_IFACE_ITER_RESUME_ALL,
>                                             mt7921_vif_connect_iter, NULL);
>         mt76_connac_power_save_sched(&dev->mt76.phy, pm);
> +
> +       mt7921_regd_change(&dev->phy, "00");
>  }
>
>  void mt7921_coredump_work(struct work_struct *work)
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 00ca3d3f3ef0..dfe8cbd7dfa5 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -1027,8 +1027,16 @@ void mt7921_scan_work(struct work_struct *work)
>                 rxd = (struct mt76_connac2_mcu_rxd *)skb->data;
>                 if (rxd->eid == MCU_EVENT_SCHED_SCAN_DONE) {
>                         ieee80211_sched_scan_results(phy->mt76->hw);
> -               } else if (test_and_clear_bit(MT76_HW_SCANNING,
> -                                             &phy->mt76->state)) {
> +               } else if (rxd->eid == MCU_EVENT_SCAN_DONE) {
> +                       struct mt76_connac_hw_scan_done *event = NULL;
> +
> +                       skb_pull(skb, sizeof(*rxd));
> +                       event = (struct mt76_connac_hw_scan_done *)skb->data;
> +                       mt7921_regd_change(phy, event->alpha2);

Because this changes the firmware/driver ABI, we need to consider
backward compatibility. Will the driver still behave correctly with
older firmware?
For example, on older firmware the scan-done event doesn’t populate
alpha2 (it would effectively be coming from the old pad3 field and may
contain undefined bytes).

> +               }
> +
> +               if (test_and_clear_bit(MT76_HW_SCANNING,
> +                                      &phy->mt76->state)) {
>                         struct cfg80211_scan_info info = {

^ permalink raw reply

* RE: [PATCH net] wifi: rtw88: properly drop usb interface reference on error
From: Ping-Ke Shih @ 2026-02-24  0:46 UTC (permalink / raw)
  To: Greg Kroah-Hartman, linux-wireless@vger.kernel.org
  Cc: linux-kernel@vger.kernel.org, stable
In-Reply-To: <2026022333-periscope-unusual-f0a0@gregkh>

Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> If an error happens in the usb probe path, in rtw_usb_intf_init(), the
> usb interface reference needs to be properly dropped, otherwise is is
> incorrectly increased when returning to the USB core.
> 
> Cc: Ping-Ke Shih <pkshih@realtek.com>
> Cc: stable <stable@kernel.org>
> Assisted-by: gkh_clanker_2000
> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> ---
>  drivers/net/wireless/realtek/rtw88/usb.c | 8 ++++++--
>  1 file changed, 6 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
> index 433b06c8d8a6..36ac20039ce2 100644
> --- a/drivers/net/wireless/realtek/rtw88/usb.c
> +++ b/drivers/net/wireless/realtek/rtw88/usb.c
> @@ -1046,13 +1046,17 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
> 
>         rtwusb->udev = udev;
>         ret = rtw_usb_parse(rtwdev, intf);
> -       if (ret)
> +       if (ret) {
> +               usb_put_dev(udev);
>                 return ret;
> +       }
> 
>         rtwusb->usb_data = kcalloc(RTW_USB_MAX_RXTX_COUNT, sizeof(u32),
>                                    GFP_KERNEL);
> -       if (!rtwusb->usb_data)
> +       if (!rtwusb->usb_data) {
> +               usb_put_dev(udev);
>                 return -ENOMEM;
> +       }
> 
>         usb_set_intfdata(intf, rtwdev->hw);
> 

Since rtwusb->udev isn't used right after assignment in this function.
Would it be simpler that moving usb_get_dev() downward like below?

diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index db60e142268d..6e5c9c6f3e00 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -1041,10 +1041,8 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
                             struct usb_interface *intf)
 {
        struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
-       struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf));
        int ret;

-       rtwusb->udev = udev;
        ret = rtw_usb_parse(rtwdev, intf);
        if (ret)
                return ret;
@@ -1054,6 +1052,8 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
        if (!rtwusb->usb_data)
                return -ENOMEM;

+       rtwusb->udev = usb_get_dev(interface_to_usbdev(intf));
+
        usb_set_intfdata(intf, rtwdev->hw);

        SET_IEEE80211_DEV(rtwdev->hw, &intf->dev);




^ permalink raw reply related

* Re: [PATCH net] wifi: rtw88: properly drop usb interface reference on error
From: Greg Kroah-Hartman @ 2026-02-24  1:09 UTC (permalink / raw)
  To: Ping-Ke Shih
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable
In-Reply-To: <0a1b75853588468d87725e4d6aad8f22@realtek.com>

On Tue, Feb 24, 2026 at 12:46:02AM +0000, Ping-Ke Shih wrote:
> Greg Kroah-Hartman <gregkh@linuxfoundation.org> wrote:
> > If an error happens in the usb probe path, in rtw_usb_intf_init(), the
> > usb interface reference needs to be properly dropped, otherwise is is
> > incorrectly increased when returning to the USB core.
> > 
> > Cc: Ping-Ke Shih <pkshih@realtek.com>
> > Cc: stable <stable@kernel.org>
> > Assisted-by: gkh_clanker_2000
> > Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
> > ---
> >  drivers/net/wireless/realtek/rtw88/usb.c | 8 ++++++--
> >  1 file changed, 6 insertions(+), 2 deletions(-)
> > 
> > diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
> > index 433b06c8d8a6..36ac20039ce2 100644
> > --- a/drivers/net/wireless/realtek/rtw88/usb.c
> > +++ b/drivers/net/wireless/realtek/rtw88/usb.c
> > @@ -1046,13 +1046,17 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
> > 
> >         rtwusb->udev = udev;
> >         ret = rtw_usb_parse(rtwdev, intf);
> > -       if (ret)
> > +       if (ret) {
> > +               usb_put_dev(udev);
> >                 return ret;
> > +       }
> > 
> >         rtwusb->usb_data = kcalloc(RTW_USB_MAX_RXTX_COUNT, sizeof(u32),
> >                                    GFP_KERNEL);
> > -       if (!rtwusb->usb_data)
> > +       if (!rtwusb->usb_data) {
> > +               usb_put_dev(udev);
> >                 return -ENOMEM;
> > +       }
> > 
> >         usb_set_intfdata(intf, rtwdev->hw);
> > 
> 
> Since rtwusb->udev isn't used right after assignment in this function.
> Would it be simpler that moving usb_get_dev() downward like below?

What is even simpler, and easier, is to never call usb_get_dev() at all
anyway as it's not needed :)

I created that pattern a few decades ago when we thought that it was
going to be required, but as long as the usb interface is bound to the
driver, that pointer is going to be valid so there's no real need to
increment the reference count, except to feel good about doing it.

I'll gladly do that fix instead, if you want me to, I was just trying to
follow the style of the existing code and fix up the current bug.

> diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
> index db60e142268d..6e5c9c6f3e00 100644
> --- a/drivers/net/wireless/realtek/rtw88/usb.c
> +++ b/drivers/net/wireless/realtek/rtw88/usb.c
> @@ -1041,10 +1041,8 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
>                              struct usb_interface *intf)
>  {
>         struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev);
> -       struct usb_device *udev = usb_get_dev(interface_to_usbdev(intf));
>         int ret;
> 
> -       rtwusb->udev = udev;
>         ret = rtw_usb_parse(rtwdev, intf);
>         if (ret)
>                 return ret;
> @@ -1054,6 +1052,8 @@ static int rtw_usb_intf_init(struct rtw_dev *rtwdev,
>         if (!rtwusb->usb_data)
>                 return -ENOMEM;
> 
> +       rtwusb->udev = usb_get_dev(interface_to_usbdev(intf));

That too works, or again, just drop the usb_get_dev() and usb_put_dev()
calls entirely.

thanks,

greg k-h

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox