Linux wireless drivers development
 help / color / mirror / Atom feed
* [PATCH v2] wifi: mt76: mt7921: add regulatory wiphy self manager support
From: JB Tsai @ 2026-06-09  6:50 UTC (permalink / raw)
  To: nbd, lorenzo
  Cc: linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang, Quan.Zhou,
	Ryder.Lee, Leon.Yen, litien.chang, Charlie-cy.Wu, jb.tsai

From: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>

Introduce regulatory wiphy self-managed mode support for MT7921,
allowing the driver to manage its own regulatory domain independently
from the kernel's regulatory framework.

Signed-off-by: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>
---
v2: fix regd.c build warning
---
 .../wireless/mediatek/mt76/mt76_connac_mcu.h  |   1 +
 .../net/wireless/mediatek/mt76/mt7921/mcu.c   |   3 +
 .../net/wireless/mediatek/mt76/mt7921/regd.c  | 209 ++++++++++++++++--
 .../net/wireless/mediatek/mt76/mt7921/regd.h  |  55 ++++-
 4 files changed, 245 insertions(+), 23 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
index ed5c441748d8..c10a2c4e7ee2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
+++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h
@@ -1363,6 +1363,7 @@ enum {
 	MCU_CE_CMD_FWLOG_2_HOST = 0xc5,
 	MCU_CE_CMD_GET_WTBL = 0xcd,
 	MCU_CE_CMD_GET_TXPWR = 0xd0,
+	MCU_CE_CMD_SET_REGD_CH = 0xd1,
 };
 
 enum {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
index 25b9437250f7..2e0769d18f87 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
@@ -1403,6 +1403,9 @@ int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
 
 	/* submit all clc config */
 	for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
+		if (i == MT792x_CLC_REGD)
+			continue;
+
 		ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap,
 					   phy->clc[i], i);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
index f122e418d825..d29b3b0113f2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
@@ -10,6 +10,15 @@ static bool mt7921_disable_clc;
 module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
 MODULE_PARM_DESC(disable_clc, "disable CLC support");
 
+static struct ieee80211_regdomain mt7921_regd_ww = {
+	.n_reg_rules = 1,
+	.alpha2 =  "00",
+	.reg_rules = {
+		/* IEEE 802.11b/g, channels 1..11 */
+		REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0),
+	}
+};
+
 bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
 {
 	if (mt7921_disable_clc ||
@@ -33,6 +42,9 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
 	np = mt76_find_power_limits_node(mdev);
 
 	sband = wiphy->bands[NL80211_BAND_5GHZ];
+	if (!sband)
+		return;
+
 	band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
 	for (i = 0; i < sband->n_channels; i++) {
 		ch = &sband->channels[i];
@@ -71,35 +83,36 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
 	}
 }
 
-int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
-			   enum environment_cap country_ie_env)
+static int mt7921_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
+				  enum environment_cap env)
 {
-	struct mt76_dev *mdev = &dev->mt76;
-	struct ieee80211_hw *hw = mdev->hw;
+	struct ieee80211_hw *hw = mt76_hw(dev);
 	struct wiphy *wiphy = hw->wiphy;
-	int ret = 0;
-
-	dev->regd_in_progress = true;
-
-	mt792x_mutex_acquire(dev);
-	if (!dev->regd_change)
-		goto err;
+	int ret;
 
-	ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
+	ret = mt7921_mcu_set_clc(dev, alpha2, env);
 	if (ret < 0)
-		goto err;
+		return ret;
 
 	mt7921_regd_channel_update(wiphy, dev);
 
 	ret = mt76_connac_mcu_set_channel_domain(hw->priv);
 	if (ret < 0)
-		goto err;
+		return ret;
 
-	ret = mt7921_set_tx_sar_pwr(hw, NULL);
-	if (ret < 0)
-		goto err;
+	return mt7921_set_tx_sar_pwr(hw, NULL);
+}
 
-err:
+int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
+			   enum environment_cap country_ie_env)
+{
+	int ret = 0;
+
+	dev->regd_in_progress = true;
+
+	mt792x_mutex_acquire(dev);
+	if (dev->regd_change)
+		ret = mt7921_mcu_apply_regd(dev, alpha2, country_ie_env);
 	mt792x_mutex_release(dev);
 	dev->regd_change = false;
 	dev->regd_in_progress = false;
@@ -142,10 +155,160 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
 	if (pm->suspended)
 		return;
 
+	if (MT7921_REGD_SUPPORTED(&dev->phy)) {
+		mt7921_regd_update(&dev->phy, req->alpha2);
+
+		return;
+	}
+
 	mt7921_mcu_regd_update(dev, req->alpha2,
 			       req->country_ie_env);
 }
 
+static struct sk_buff *
+mt7921_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)
+{
+	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt7921_clc *clc = phy->clc[MT792x_CLC_REGD];
+	struct mt7921_regd_query_req *req;
+	struct mt7921_regd_cc *regd_cc;
+	struct sk_buff *ret_skb = NULL;
+	u8 *pos, *last_pos;
+	int ret = 0;
+
+	if (!clc)
+		return NULL;
+
+	pos = clc->data;
+	last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7921_clc);
+	while (pos < last_pos) {
+		u32 req_len = 0;
+		u32 rules_len = 0;
+		u32 sign_len = 4;
+
+		if (pos + sizeof(*regd_cc) > last_pos)
+			break;
+
+		regd_cc = (struct mt7921_regd_cc *)pos;
+		rules_len = sizeof(struct mt7921_regd_rule_header) +
+			    sizeof(struct mt7921_regd_rule) *
+			    le32_to_cpu(regd_cc->n_reg_rules);
+
+		if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos)
+			break;
+
+		pos += sizeof(*regd_cc) + rules_len + sign_len;
+		if (memcmp(regd_cc->alpha2, alpha2, 2))
+			continue;
+
+		req_len = sizeof(*req) + rules_len + sign_len;
+		req = devm_kmalloc(dev->mt76.dev, req_len, GFP_KERNEL);
+
+		if (!req)
+			return NULL;
+
+		req->ver = regd_cc->ver;
+		req->sign_type = regd_cc->sign_type;
+		req->size = cpu_to_le32(rules_len + sign_len);
+		req->n_reg_rules = regd_cc->n_reg_rules;
+
+		memcpy(req->alpha2, regd_cc->alpha2, 2);
+		memcpy(req->data, regd_cc->data, rules_len + sign_len);
+
+		ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+						MCU_CE_CMD(SET_REGD_CH),
+						req, req_len, true, &ret_skb);
+
+		devm_kfree(dev->mt76.dev, req);
+
+		return ret < 0 ? NULL : ret_skb;
+	}
+
+	return NULL;
+}
+
+int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2)
+{
+	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt7921_regd_rule *mt7921_rule;
+	struct mt76_dev *mdev = &dev->mt76;
+	struct ieee80211_regdomain *regd;
+	struct ieee80211_reg_rule *rule;
+	struct mt7921_regd_rule_ev *ev;
+	int i, num_of_rules = 0;
+	struct sk_buff *skb;
+	int ret = 0;
+
+	if (dev->hw_full_reset)
+		return 0;
+
+	if (!MT7921_REGD_SUPPORTED(phy))
+		return -EOPNOTSUPP;
+
+	mt792x_mutex_acquire(dev);
+	skb = mt7921_regd_query_regdb(phy, alpha2);
+	mt792x_mutex_release(dev);
+
+	if (!skb)
+		return -EINVAL;
+
+	ev = (struct mt7921_regd_rule_ev *)(skb->data + 4);
+	num_of_rules = le32_to_cpu(ev->n_reg_rules);
+
+	if (!num_of_rules ||
+		WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL);
+	if (!regd) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < num_of_rules; i++) {
+		mt7921_rule = &ev->reg_rule[i];
+		rule = &regd->reg_rules[i];
+
+		rule->freq_range.start_freq_khz =
+					MHZ_TO_KHZ(mt7921_rule->start_freq);
+		rule->freq_range.end_freq_khz =
+					MHZ_TO_KHZ(mt7921_rule->end_freq);
+		rule->freq_range.max_bandwidth_khz =
+					MHZ_TO_KHZ(mt7921_rule->max_bw);
+		/* not used by fw */
+		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+		rule->power_rule.max_eirp = DBM_TO_MBM(22);
+		rule->flags = mt7921_rule->flags;
+	}
+
+	regd->n_reg_rules = num_of_rules;
+	regd->dfs_region = ev->dfs_region;
+
+	memcpy(regd->alpha2, alpha2, 2);
+	memcpy(mdev->alpha2, alpha2, 2);
+
+	dev->regd_change = true;
+	mt7921_mcu_regd_update(dev, alpha2, ENVIRON_ANY);
+
+	ret = regulatory_set_wiphy_regd(wiphy, regd);
+
+	kfree(regd);
+err:
+	dev_kfree_skb(skb);
+
+	if (ret < 0)
+		return regulatory_set_wiphy_regd(wiphy, &mt7921_regd_ww);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mt7921_regd_update);
+
 static bool
 mt7921_regd_is_valid_alpha2(const char *alpha2)
 {
@@ -183,7 +346,9 @@ int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2)
 	if (!memcmp(alpha2, mdev->alpha2, 2))
 		return 0;
 
-	if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+	if (MT7921_REGD_SUPPORTED(phy))
+		return mt7921_regd_update(phy, alpha2);
+	else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
 		return regulatory_hint(wiphy, alpha2);
 	else
 		return mt7921_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
@@ -197,7 +362,11 @@ int mt7921_regd_init(struct mt792x_phy *phy)
 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
 	struct mt76_dev *mdev = &dev->mt76;
 
-	if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
+	if (MT7921_REGD_SUPPORTED(phy)) {
+		wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED |
+					   REGULATORY_DISABLE_BEACON_HINTS;
+		return mt7921_regd_update(phy, "00");
+	} else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN)
 		wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
 					   REGULATORY_DISABLE_BEACON_HINTS;
 	else
diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
index 571f31629e9e..c1e94cd4c958 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
@@ -4,9 +4,57 @@
 #ifndef __MT7921_REGD_H
 #define __MT7921_REGD_H
 
-struct mt792x_dev;
-struct wiphy;
-struct regulatory_request;
+#include "mt7921.h"
+
+struct mt7921_regd_rule_header {
+	u8 alpha2[2];
+	u8 dfs_region;
+	u8 rsv[13];
+};
+
+struct mt7921_regd_rule {
+	u32 start_freq;
+	u32 end_freq;
+	u32 max_bw;
+	u32 eirp;
+	u32 flags;
+	u8 rsv[12];
+};
+
+struct mt7921_regd_cc {
+	u8 alpha2[2];
+	u8 ver;
+	u8 rsv;
+	__le32 n_reg_rules;
+	u8 sign_type;
+	u8 rsv1[7];
+	u8 data[];
+};
+
+struct mt7921_regd_rule_ev {
+	__le16 tag;
+	__le16 len;
+	__le32 n_reg_rules;
+	u8 dfs_region;
+	u8 rsv[15];
+	struct mt7921_regd_rule reg_rule[];
+};
+
+struct mt7921_regd_query_req {
+	u8 ver;
+	u8 sign_type;
+	u8 rsv1[2];
+	__le32 size;
+	u8 alpha2[2];
+	u8 rsv2[2];
+	__le32 n_reg_rules;
+	u8 rsv3[64];
+	u8 data[];
+};
+
+#define MT7921_REGD_SUPPORTED(phy) \
+	(((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \
+	(phy)->clc[MT792x_CLC_REGD])
 
 int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
 			   enum environment_cap country_ie_env);
@@ -15,5 +63,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
 bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
 int mt7921_regd_change(struct mt792x_phy *phy, char *alpha2);
 int mt7921_regd_init(struct mt792x_phy *phy);
+int mt7921_regd_update(struct mt792x_phy *phy, char *alpha2);
 
 #endif
-- 
2.18.0


^ permalink raw reply related

* [PATCH v2] wifi: mt76: mt7925: add regulatory wiphy self manager support
From: JB Tsai @ 2026-06-09  6:50 UTC (permalink / raw)
  To: nbd, lorenzo
  Cc: linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang, Quan.Zhou,
	Ryder.Lee, Leon.Yen, litien.chang, Charlie-cy.Wu, jb.tsai

From: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>

Introduce regulatory wiphy self-managed mode support for MT7925,
allowing the driver to manage its own regulatory domain independently
from the kernel's regulatory framework.

Signed-off-by: Charlie-cy Wu <Charlie-cy.Wu@mediatek.com>
---
v2: fix regd.c build warning
---
 .../net/wireless/mediatek/mt76/mt7925/mcu.c   |   3 +-
 .../net/wireless/mediatek/mt76/mt7925/regd.c  | 204 ++++++++++++++++--
 .../net/wireless/mediatek/mt76/mt7925/regd.h  |  54 +++++
 drivers/net/wireless/mediatek/mt76/mt792x.h   |   2 +
 4 files changed, 244 insertions(+), 19 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
index 37cdf3e8a067..56b265602faa 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c
@@ -3457,7 +3457,8 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
 
 	/* submit all clc config */
 	for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
-		if (i == MT792x_CLC_BE_CTRL)
+		if (i == MT792x_CLC_BE_CTRL ||
+		    i == MT792x_CLC_REGD)
 			continue;
 
 		ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap,
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
index 16f56ee879d4..3eb3f172dba4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.c
@@ -9,6 +9,15 @@ static bool mt7925_disable_clc;
 module_param_named(disable_clc, mt7925_disable_clc, bool, 0644);
 MODULE_PARM_DESC(disable_clc, "disable CLC support");
 
+static struct ieee80211_regdomain mt7925_regd_ww = {
+	.n_reg_rules = 1,
+	.alpha2 =  "00",
+	.reg_rules = {
+		/* IEEE 802.11b/g, channels 1..11 */
+		REG_RULE(2412 - 10, 2462 + 10, 40, 6, 20, 0),
+	}
+};
+
 bool mt7925_regd_clc_supported(struct mt792x_dev *dev)
 {
 	if (mt7925_disable_clc ||
@@ -128,35 +137,37 @@ mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
 	}
 }
 
-int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
-			   enum environment_cap country_ie_env)
+static int mt7925_mcu_apply_regd(struct mt792x_dev *dev, u8 *alpha2,
+				  enum environment_cap env)
 {
 	struct ieee80211_hw *hw = mt76_hw(dev);
 	struct wiphy *wiphy = hw->wiphy;
-	int ret = 0;
-
-	dev->regd_in_progress = true;
-
-	mt792x_mutex_acquire(dev);
-	if (!dev->regd_change)
-		goto err;
+	int ret;
 
-	ret = mt7925_mcu_set_clc(dev, alpha2, country_ie_env);
+	ret = mt7925_mcu_set_clc(dev, alpha2, env);
 	if (ret < 0)
-		goto err;
+		return ret;
 
 	mt7925_regd_be_ctrl(dev, alpha2);
 	mt7925_regd_channel_update(wiphy, dev);
 
 	ret = mt7925_mcu_set_channel_domain(hw->priv);
 	if (ret < 0)
-		goto err;
+		return ret;
 
-	ret = mt7925_set_tx_sar_pwr(hw, NULL);
-	if (ret < 0)
-		goto err;
+	return mt7925_set_tx_sar_pwr(hw, NULL);
+}
 
-err:
+int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
+			   enum environment_cap country_ie_env)
+{
+	int ret = 0;
+
+	dev->regd_in_progress = true;
+
+	mt792x_mutex_acquire(dev);
+	if (dev->regd_change)
+		ret = mt7925_mcu_apply_regd(dev, alpha2, country_ie_env);
 	mt792x_mutex_release(dev);
 	dev->regd_change = false;
 	dev->regd_in_progress = false;
@@ -197,11 +208,162 @@ void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req)
 		/* postpone the mcu update to resume */
 		return;
 
+	if (MT7925_REGD_SUPPORTED(&dev->phy)) {
+		mt7925_regd_update(&dev->phy, req->alpha2);
+
+		return;
+	}
+
 	mt7925_mcu_regd_update(dev, req->alpha2,
 			       req->country_ie_env);
 	return;
 }
 
+static struct sk_buff *
+mt7925_regd_query_regdb(struct mt792x_phy *phy, char *alpha2)
+{
+	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt7925_clc *clc = phy->clc[MT792x_CLC_REGD];
+	struct mt7925_regd_query_req *req;
+	struct mt7925_regd_cc *regd_cc;
+	struct sk_buff *ret_skb = NULL;
+	u8 *pos, *last_pos;
+	int ret = 0;
+
+	if (!clc)
+		return NULL;
+
+	pos = clc->data;
+	last_pos = pos + le32_to_cpu(clc->len) - sizeof(struct mt7925_clc);
+	while (pos < last_pos) {
+		u32 req_len = 0;
+		u32 rules_len = 0;
+		u32 sign_len = 4;
+
+		if (pos + sizeof(*regd_cc) > last_pos)
+			break;
+
+		regd_cc = (struct mt7925_regd_cc *)pos;
+		rules_len = sizeof(struct mt7925_regd_rule_header) +
+			    sizeof(struct mt7925_regd_rule) *
+			    le32_to_cpu(regd_cc->n_reg_rules);
+
+		if (pos + sizeof(*regd_cc) + rules_len + sign_len > last_pos)
+			break;
+
+		pos += sizeof(*regd_cc) + rules_len + sign_len;
+		if (memcmp(regd_cc->alpha2, alpha2, 2))
+			continue;
+
+		req_len = sizeof(*req) + rules_len + sign_len;
+		req = devm_kmalloc(dev->mt76.dev, req_len, GFP_KERNEL);
+
+		if (!req)
+			return NULL;
+
+		req->tag = cpu_to_le16(0x6);
+		req->len = cpu_to_le16(req_len - 4);
+		req->ver = regd_cc->ver;
+		req->sign_type = regd_cc->sign_type;
+		req->size = cpu_to_le32(rules_len + sign_len);
+		req->n_reg_rules = regd_cc->n_reg_rules;
+
+		memcpy(req->alpha2, regd_cc->alpha2, 2);
+		memcpy(req->data, regd_cc->data, rules_len + sign_len);
+
+		ret = mt76_mcu_send_and_get_msg(&dev->mt76,
+						MCU_UNI_CMD(SET_POWER_LIMIT),
+						req, req_len, true, &ret_skb);
+		devm_kfree(dev->mt76.dev, req);
+
+		return ret < 0 ? NULL : ret_skb;
+	}
+
+	return NULL;
+}
+
+int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2)
+{
+	struct wiphy *wiphy = phy->mt76->hw->wiphy;
+	struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
+	struct mt792x_dev *dev = mt792x_hw_dev(hw);
+	struct mt7925_regd_rule *mt7925_rule;
+	struct mt76_dev *mdev = &dev->mt76;
+	struct ieee80211_regdomain *regd;
+	struct ieee80211_reg_rule *rule;
+	struct mt7925_regd_rule_ev *ev;
+	int i, num_of_rules = 0;
+	struct sk_buff *skb;
+	int ret = 0;
+
+	if (dev->hw_full_reset)
+		return 0;
+
+	if (!MT7925_REGD_SUPPORTED(phy))
+		return -EOPNOTSUPP;
+
+	mt792x_mutex_acquire(dev);
+	skb = mt7925_regd_query_regdb(phy, alpha2);
+	mt792x_mutex_release(dev);
+
+	if (!skb)
+		return -EINVAL;
+
+	ev = (struct mt7925_regd_rule_ev *)(skb->data + 4);
+	num_of_rules = le32_to_cpu(ev->n_reg_rules);
+
+	if (!num_of_rules ||
+		WARN_ON_ONCE(num_of_rules > NL80211_MAX_SUPP_REG_RULES)) {
+		ret = -EINVAL;
+		goto err;
+	}
+
+	regd = kzalloc(struct_size(regd, reg_rules, num_of_rules), GFP_KERNEL);
+	if (!regd) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	for (i = 0; i < num_of_rules; i++) {
+		mt7925_rule = &ev->reg_rule[i];
+		rule = &regd->reg_rules[i];
+
+		rule->freq_range.start_freq_khz =
+					MHZ_TO_KHZ(mt7925_rule->start_freq);
+		rule->freq_range.end_freq_khz =
+					MHZ_TO_KHZ(mt7925_rule->end_freq);
+		rule->freq_range.max_bandwidth_khz =
+					MHZ_TO_KHZ(mt7925_rule->max_bw);
+		/* not used by fw */
+		rule->power_rule.max_antenna_gain = DBI_TO_MBI(6);
+		rule->power_rule.max_eirp = DBM_TO_MBM(22);
+		rule->flags = mt7925_rule->flags;
+	}
+
+	regd->n_reg_rules = num_of_rules;
+	regd->dfs_region = ev->dfs_region;
+
+	memcpy(regd->alpha2, alpha2, 2);
+	memcpy(mdev->alpha2, alpha2, 2);
+
+	dev->regd_change = true;
+	mt7925_mcu_regd_update(dev, alpha2, ENVIRON_ANY);
+
+	ret = regulatory_set_wiphy_regd(wiphy, regd);
+
+	kfree(regd);
+err:
+	dev_kfree_skb(skb);
+
+	if (ret < 0)
+		return regulatory_set_wiphy_regd(wiphy, &mt7925_regd_ww);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mt7925_regd_update);
+
 static bool
 mt7925_regd_is_valid_alpha2(const char *alpha2)
 {
@@ -240,7 +402,9 @@ int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2)
 	if (!memcmp(alpha2, mdev->alpha2, 2))
 		return 0;
 
-	if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
+	if (MT7925_REGD_SUPPORTED(phy)) {
+		return mt7925_regd_update(phy, alpha2);
+	} else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
 		return regulatory_hint(wiphy, alpha2);
 	} else {
 		return mt7925_mcu_set_clc(dev, alpha2, ENVIRON_INDOOR);
@@ -255,7 +419,11 @@ int mt7925_regd_init(struct mt792x_phy *phy)
 	struct mt792x_dev *dev = mt792x_hw_dev(hw);
 	struct mt76_dev *mdev = &dev->mt76;
 
-	if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
+	if (MT7925_REGD_SUPPORTED(phy)) {
+		wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED |
+					   REGULATORY_DISABLE_BEACON_HINTS;
+		return mt7925_regd_update(phy, "00");
+	} else if (phy->chip_cap & MT792x_CHIP_CAP_11D_EN) {
 		wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE |
 					   REGULATORY_DISABLE_BEACON_HINTS;
 	} else {
diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
index 0767f078862e..2feacf42dc22 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/regd.h
@@ -6,12 +6,66 @@
 
 #include "mt7925.h"
 
+struct mt7925_regd_rule_header {
+	u8 alpha2[2];
+	u8 dfs_region;
+	u8 rsv[13];
+};
+
+struct mt7925_regd_rule {
+	u32 start_freq;
+	u32 end_freq;
+	u32 max_bw;
+	u32 eirp;
+	u32 flags;
+	u8 rsv[12];
+};
+
+struct mt7925_regd_cc {
+	u8 alpha2[2];
+	u8 ver;
+	u8 rsv;
+	__le32 n_reg_rules;
+	u8 sign_type;
+	u8 rsv1[7];
+	u8 data[];
+};
+
+struct mt7925_regd_rule_ev {
+	__le16 tag;
+	__le16 len;
+	__le32 n_reg_rules;
+	u8 dfs_region;
+	u8 rsv[15];
+	struct mt7925_regd_rule reg_rule[];
+};
+
+struct mt7925_regd_query_req {
+	u8 rsv[4];
+	__le16 tag;
+	__le16 len;
+	u8 ver;
+	u8 sign_type;
+	u8 rsv1[2];
+	__le32 size;
+	u8 alpha2[2];
+	u8 rsv2[2];
+	__le32 n_reg_rules;
+	u8 rsv3[64];
+	u8 data[];
+};
+
+#define MT7925_REGD_SUPPORTED(phy) \
+	(((phy)->chip_cap & MT792x_CHIP_CAP_REGD_EN) && \
+	(phy)->clc[MT792x_CLC_REGD])
+
 int mt7925_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
 			   enum environment_cap country_ie_env);
 
 void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2);
 void mt7925_regd_notifier(struct wiphy *wiphy, struct regulatory_request *req);
 bool mt7925_regd_clc_supported(struct mt792x_dev *dev);
+int mt7925_regd_update(struct mt792x_phy *phy, char *alpha2);
 int mt7925_regd_change(struct mt792x_phy *phy, char *alpha2);
 int mt7925_regd_init(struct mt792x_phy *phy);
 
diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h
index 4ff93f2cd624..cd81cc519ef4 100644
--- a/drivers/net/wireless/mediatek/mt76/mt792x.h
+++ b/drivers/net/wireless/mediatek/mt76/mt792x.h
@@ -29,6 +29,7 @@
 #define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1)
 #define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3)
 #define MT792x_CHIP_CAP_11D_EN BIT(4)
+#define MT792x_CHIP_CAP_REGD_EN BIT(5)
 #define MT792x_CHIP_CAP_MLO_EN BIT(8)
 #define MT792x_CHIP_CAP_MLO_EML_EN BIT(9)
 
@@ -75,6 +76,7 @@ enum {
 	MT792x_CLC_POWER,
 	MT792x_CLC_POWER_EXT,
 	MT792x_CLC_BE_CTRL,
+	MT792x_CLC_REGD,
 	MT792x_CLC_MAX_NUM,
 };
 
-- 
2.18.0


^ permalink raw reply related

* [PATCH ath-next 1/2] wifi: ath12k: Skip setting RX_FLAG_8023 for Ethernet-II (DIX) frames in monitor mode
From: Sushant Butta @ 2026-06-09  6:48 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless, Sushant Butta
In-Reply-To: <20260609064856.547032-1-sushant.butta@oss.qualcomm.com>

Monitor mode delivers raw 802.11 frames, not 802.3/Ethernet frames. Setting
RX_FLAG_8023 for monitor RX is incorrect and can break userspace capture and
analysis. Do not update this flag in the monitor path to ensure correct
handling of captured frames.

In the monitor path, RX_FLAG_ONLY_MONITOR is always set before decap
is evaluated, which forces decap to remain DP_RX_DECAP_TYPE_RAW.
As a result, the condition to set RX_FLAG_8023 can never be satisfied.
Hence, drop this unreachable code.

Also remove the unused hal_rx_mon_ppdu_info parameter from
ath12k_dp_mon_rx_deliver_msdu(), as it was passed but never used.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1

Signed-off-by: Sushant Butta <sushant.butta@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/dp_mon.c       | 16 +---------------
 drivers/net/wireless/ath/ath12k/dp_mon.h       |  4 +---
 drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c |  7 +------
 3 files changed, 3 insertions(+), 24 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 44c5cff75f16..cfcfa93eeb44 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -493,9 +493,7 @@ EXPORT_SYMBOL(ath12k_dp_mon_update_radiotap);
 void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 				   struct napi_struct *napi,
 				   struct sk_buff *msdu,
-				   const struct hal_rx_mon_ppdu_info *ppduinfo,
-				   struct ieee80211_rx_status *status,
-				   u8 decap)
+				   struct ieee80211_rx_status *status)
 {
 	struct ath12k_dp *dp = dp_pdev->dp;
 	struct ath12k_base *ab = dp->ab;
@@ -511,7 +509,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
 	struct hal_rx_desc_data rx_info;
 	bool is_mcbc = rxcb->is_mcbc;
-	bool is_eapol_tkip = rxcb->is_eapol;
 	struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data;
 	u8 addr[ETH_ALEN] = {};
 
@@ -570,17 +567,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	rx_status = IEEE80211_SKB_RXCB(msdu);
 	*rx_status = *status;
 
-	/* TODO: trace rx packet */
-
-	/* PN for multicast packets are not validate in HW,
-	 * so skip 802.3 rx path
-	 * Also, fast_rx expects the STA to be authorized, hence
-	 * eapol packets are sent in slow path.
-	 */
-	if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol_tkip &&
-	    !(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
-		rx_status->flag |= RX_FLAG_8023;
-
 	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
 }
 EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu);
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h
index 167028d27513..162cdcaa57a7 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.h
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.h
@@ -112,9 +112,7 @@ void ath12k_dp_mon_update_radiotap(struct ath12k_pdev_dp *dp_pdev,
 void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 				   struct napi_struct *napi,
 				   struct sk_buff *msdu,
-				   const struct hal_rx_mon_ppdu_info *ppduinfo,
-				   struct ieee80211_rx_status *status,
-				   u8 decap);
+				   struct ieee80211_rx_status *status);
 struct sk_buff *
 ath12k_dp_mon_rx_merg_msdus(struct ath12k_pdev_dp *dp_pdev,
 			    struct dp_mon_mpdu *mon_mpdu,
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
index 7dd4a49d64d5..67acaebdc605 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/dp_mon.c
@@ -2480,7 +2480,6 @@ ath12k_wifi7_dp_mon_rx_deliver(struct ath12k_pdev_dp *dp_pdev,
 {
 	struct sk_buff *mon_skb, *skb_next, *header;
 	struct ieee80211_rx_status *rxs = &dp_pdev->rx_status;
-	u8 decap = DP_RX_DECAP_TYPE_RAW;
 
 	mon_skb = ath12k_dp_mon_rx_merg_msdus(dp_pdev, mon_mpdu, ppduinfo, rxs);
 	if (!mon_skb)
@@ -2507,12 +2506,8 @@ ath12k_wifi7_dp_mon_rx_deliver(struct ath12k_pdev_dp *dp_pdev,
 		}
 		rxs->flag |= RX_FLAG_ONLY_MONITOR;
 
-		if (!(rxs->flag & RX_FLAG_ONLY_MONITOR))
-			decap = mon_mpdu->decap_format;
-
 		ath12k_dp_mon_update_radiotap(dp_pdev, ppduinfo, mon_skb, rxs);
-		ath12k_dp_mon_rx_deliver_msdu(dp_pdev, napi, mon_skb, ppduinfo,
-					      rxs, decap);
+		ath12k_dp_mon_rx_deliver_msdu(dp_pdev, napi, mon_skb, rxs);
 		mon_skb = skb_next;
 	} while (mon_skb);
 	rxs->flag = 0;
-- 
2.34.1


^ permalink raw reply related

* [PATCH ath-next 0/2] wifi: ath12k: correct monitor-mode RX metadata handling
From: Sushant Butta @ 2026-06-09  6:48 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless, Sushant Butta

This series fixes monitor-mode meta data handling in ath12k and trims dead code:

Monitor mode delivers raw 802.11 frames should not set RX_FLAG_8023 flag,
and populate peer/link metadata in ieee80211_rx_status for monitor MSDUs.

Sushant Butta (2):
  wifi: ath12k: Skip setting RX_FLAG_8023 for Ethernet-II (DIX) frames
    in monitor mode
  wifi: ath12k: Skip peer link info update in rx_status for monitor
    MSDUs

 drivers/net/wireless/ath/ath12k/dp_mon.c      | 70 +------------------
 drivers/net/wireless/ath/ath12k/dp_mon.h      |  4 +-
 .../net/wireless/ath/ath12k/wifi7/dp_mon.c    |  7 +-
 3 files changed, 4 insertions(+), 77 deletions(-)


base-commit: 38b2fb7d2df16f5801f7d88a4739942b95a5f6aa
-- 
2.34.1


^ permalink raw reply

* [PATCH ath-next 2/2] wifi: ath12k: Skip peer link info update in rx_status for monitor MSDUs
From: Sushant Butta @ 2026-06-09  6:48 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless, Sushant Butta
In-Reply-To: <20260609064856.547032-1-sushant.butta@oss.qualcomm.com>

Do not populate peer and link_id in ieee80211_rx_status for monitor
MSDUs.

The monitor RX path is handled differently in mac80211 when
RX_FLAG_ONLY_MONITOR is set, and does not consume peer/link metadata.
As such, looking up the peer and updating link_id here is unnecessary.

Additionally, this metadata is not required for monitor mode delivery,
and performing the lookup/update introduces redundant work and the
potential for inconsistent rx_status state if multiple paths modify it.
Hence, remove the peer lookup and link_id update from the monitor MSDU
delivery path.

This also removes the per-MSDU debug logging in the monitor path,
slightly reducing debuggability, but avoids unnecessary overhead in the
monitor RX path.

Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1

Signed-off-by: Sushant Butta <sushant.butta@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/dp_mon.c | 54 +-----------------------
 1 file changed, 1 insertion(+), 53 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index cfcfa93eeb44..7d5be77b081f 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -495,8 +495,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 				   struct sk_buff *msdu,
 				   struct ieee80211_rx_status *status)
 {
-	struct ath12k_dp *dp = dp_pdev->dp;
-	struct ath12k_base *ab = dp->ab;
 	static const struct ieee80211_radiotap_he known = {
 		.data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN |
 				     IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN),
@@ -504,13 +502,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 	};
 	struct ieee80211_rx_status *rx_status;
 	struct ieee80211_radiotap_he *he = NULL;
-	struct ieee80211_sta *pubsta = NULL;
-	struct ath12k_dp_link_peer *peer;
-	struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
-	struct hal_rx_desc_data rx_info;
-	bool is_mcbc = rxcb->is_mcbc;
-	struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data;
-	u8 addr[ETH_ALEN] = {};
 
 	status->link_valid = 0;
 
@@ -521,53 +512,10 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
 		status->flag |= RX_FLAG_RADIOTAP_HE;
 	}
 
-	ath12k_dp_extract_rx_desc_data(dp->hal, &rx_info, rx_desc, rx_desc);
-
-	rcu_read_lock();
-	spin_lock_bh(&dp->dp_lock);
-	peer = ath12k_dp_rx_h_find_link_peer(dp_pdev, msdu, &rx_info);
-	if (peer && peer->sta) {
-		pubsta = peer->sta;
-		memcpy(addr, peer->addr, ETH_ALEN);
-		if (pubsta->valid_links) {
-			status->link_valid = 1;
-			status->link_id = peer->link_id;
-		}
-	}
-
-	spin_unlock_bh(&dp->dp_lock);
-	rcu_read_unlock();
-
-	ath12k_dbg(ab, ATH12K_DBG_DATA,
-		   "rx skb %p len %u peer %pM %u %s %s%s%s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n",
-		   msdu,
-		   msdu->len,
-		   addr,
-		   rxcb->tid,
-		   (is_mcbc) ? "mcast" : "ucast",
-		   (status->encoding == RX_ENC_LEGACY) ? "legacy" : "",
-		   (status->encoding == RX_ENC_HT) ? "ht" : "",
-		   (status->encoding == RX_ENC_VHT) ? "vht" : "",
-		   (status->encoding == RX_ENC_HE) ? "he" : "",
-		   (status->bw == RATE_INFO_BW_40) ? "40" : "",
-		   (status->bw == RATE_INFO_BW_80) ? "80" : "",
-		   (status->bw == RATE_INFO_BW_160) ? "160" : "",
-		   (status->bw == RATE_INFO_BW_320) ? "320" : "",
-		   status->enc_flags & RX_ENC_FLAG_SHORT_GI ? "sgi " : "",
-		   status->rate_idx,
-		   status->nss,
-		   status->freq,
-		   status->band, status->flag,
-		   !!(status->flag & RX_FLAG_FAILED_FCS_CRC),
-		   !!(status->flag & RX_FLAG_MMIC_ERROR),
-		   !!(status->flag & RX_FLAG_AMSDU_MORE));
-
-	ath12k_dbg_dump(ab, ATH12K_DBG_DP_RX, NULL, "dp rx msdu: ",
-			msdu->data, msdu->len);
 	rx_status = IEEE80211_SKB_RXCB(msdu);
 	*rx_status = *status;
 
-	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
+	ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), NULL, msdu, napi);
 }
 EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu);
 
-- 
2.34.1


^ permalink raw reply related

* Re: [PATCH] ath12k: fix NULL pointer dereference in rhash table destroy
From: Jose Ignacio Tornos Martinez @ 2026-06-09  6:40 UTC (permalink / raw)
  To: jeff.johnson
  Cc: ath12k, jjohnson, jtornosm, linux-kernel, linux-wireless, stable
In-Reply-To: <973da9e9-3851-4e00-85d4-28140c039127@oss.qualcomm.com>

Hello Jeff,

Thank you for the feedback.

> My preference would be to fix the logic so that the deinit is symmetric with
> init, and if any stage of init fails, it should unwind whatever init occurred
> up to that point. So this patch seems to be a bandage instead of an engineered
> fix.
>
> But I'll let the Qualcomm engineering team give their opinion.
I understand the concern about symmetry.
The NULL check approach is intentional here. The ath12k driver has complex
multi-stage initialization, with many potential failure points.
The init/deinit paths are symmetric - destroy is called from the same
cleanup paths as init.
The issue is that init can fail partway through, leaving some components
uninitialized.
The rhashtable pointer itself serves as the initialization state indicator -
it's NULL before init and non-NULL after. Checking it directly is simpler
than adding separate state flags that must be kept in sync.
That said, I'm open to alternative approaches if you see a cleaner way to
handle partial initialization states across the driver's initialization
sequence. Happy to revise if there's a better solution I'm missing.

> not a fan of this pattern.
> if we go with a bandaid solution then i'd want to use guard(mutex) so that
> there isn't a need for a cleanup goto -- we could just return like the patch below
Agreed, guard(mutex) is cleaner. I'll incorporate this in v2 once we align
on the overall approach.

Thanks

Best regards
José Ignacio


^ permalink raw reply

* RE: [PATCH] wifi: rtlwifi: rtl8723be: Remove unnecessary irq save/restore in hw_init()
From: Ping-Ke Shih @ 2026-06-09  3:11 UTC (permalink / raw)
  To: William Hansen-Baird
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	William Hansen-Baird
In-Reply-To: <20260608135345.2526325-3-williamhb+k@fastmail.com>

William Hansen-Baird <william.hansen.baird@gmail.com> wrote:
> rtl8723be hw_init() calls local_save_flags(flags) followed by
> local_irq_enable(). Later, local_irq_restore(flags) is called.
> 
> This causes warnings from Lockdep on boot and modprobe,
> as local_irq_restore(flags) should only be called while irqs are disabled.
> 
> With testing I found that all paths which call hw_init() have irqs
> already enabled for rtl8723be.

By commits you found below, current hw_init() is not in scope of
spin_lock_irqsave(), so irqs is not disabled, right?

> 
> Furthermore, the calls were originally added for the rtl8192ce
> in commit f78bccd79ba3 ("rtlwifi: rtl8192ce: Fix too long disable of IRQs")
> before later being added to most other rtlwifi drivers.

This commit adding the code you are going to remove, is to resolve
"disabling for too long the local interrupts". I think current code
has no this problem, so I think removal is okay.

> 
> Commit d3feae41a347 ("rtlwifi: Update power-save routines for 062814 driver")
> then replaces the call to spin_lock_irqsave() before hw_init(),
> and thus the codepath which caused irqs to be disabled in hw_init and
> prompted the original commit has been removed.

> 
> The same irq save/restore pattern is also present in the hw_init() of
> rtl8192ce, rtl8723ae, rtl8188ee, rtl8192se and rtl8192cu,
> however I don't have the hardware to test them,
> so I did not include them in my changes.

Okay to me.

> 
> Tested on a Razer Blade 14 2017.
> 
> Example of output from Lockdep prior to fix:
> 
> [ 2305.476471] raw_local_irq_restore() called with IRQs enabled
> 
> ...
> 
> [ 2305.478709] Call Trace:
> [ 2305.478731]  <TASK>
> [ 2305.478753]  rtl8723be_hw_init+0x5992/0x7220 [rtl8723be]
> [ 2305.478798]  ? static_obj+0x61/0xa0
> [ 2305.478848]  rtl_pci_start+0x222/0x5c0 [rtl_pci]
> [ 2305.478891]  rtl_op_start+0x128/0x1a0 [rtlwifi]
> [ 2305.478940]  ? __kasan_check_read+0x11/0x20
> [ 2305.480082]  drv_start+0x16c/0x550 [mac80211]

Not prefer timestamp in commit message.

> 
> ...
> 
> [ 2305.570855] irq event stamp: 887679
> [ 2305.571569] hardirqs last  enabled at (887689): [<ffffffff96511170>] __up_console_sem+0x90/0xa0
> [ 2305.572347] hardirqs last disabled at (887698): [<ffffffff96511155>] __up_console_sem+0x75/0xa0
> [ 2305.573076] softirqs last  enabled at (887670): [<ffffffff962f4675>] __irq_exit_rcu+0x175/0x2f0
> [ 2305.573817] softirqs last disabled at (887649): [<ffffffff962f4675>] __irq_exit_rcu+0x175/0x2f0
> [ 2305.574636] ---[ end trace 0000000000000000 ]---
> 
> Link: https://lore.kernel.org/all/20210111153707.10071-1-mark.rutland@arm.com/

I think this is point out the commit which introduce the detection of
"raw_local_irq_restore() called with IRQs enabled". I think this isn't
very highly related to this commit. 

Please use below style, and describe what it is in commit message.
It can be ignored if a reviewer has understood this. 

[1] https://lore.kernel.org/all/20210111153707.10071-1-mark.rutland@arm.com/

(a blank line before s-o-b line)

> Signed-off-by: William Hansen-Baird <williamhb+k@fastmail.com>

My comments are minor, so

Acked-by: Ping-Ke Shih <pkshih@realtek.com>

Please revise commit message and take my ack to v2.


By the way, subject prefix should be "[PATCH rtw-next] wifi: rtlwifi: ..."

Ping-Ke


^ permalink raw reply

* Re: [PATCH v3 2/3] remoteproc: qcom_wcnss_iris: Add support for WCN3610
From: Bjorn Andersson @ 2026-06-09  3:04 UTC (permalink / raw)
  To: Jeff Johnson
  Cc: Krzysztof Kozlowski, Kerigan Creighton, linux-wireless,
	loic.poulain, wcn36xx, mathieu.poirier, linux-remoteproc,
	linux-arm-msm, robh, krzk+dt, conor+dt, devicetree, linux-kernel,
	Dmitry Baryshkov
In-Reply-To: <ecad737b-ea4c-4f32-b519-f338cfd6d48e@oss.qualcomm.com>

On Fri, Jun 05, 2026 at 05:33:22PM -0700, Jeff Johnson wrote:
> On 3/5/2026 11:25 PM, Krzysztof Kozlowski wrote:
> > On 06/03/2026 01:43, Kerigan Creighton wrote:
> >> WCN3610 has the same regulator requirements as
> >> WCN3620, so in qcom_wcnss_iris, we can use wcn3620_data.
> >>
> >> A separate compatible is needed for WCN3610 because the
> >> wcn36xx driver uses it for chip-specific configuration.
> >> Specifically, it sets BTC (Bluetooth Coexistence) CFGs,
> >> disables ENABLE_DYNAMIC_RA_START_RATE, and disables
> >> STA_POWERSAVE for this specific chip for stable
> >> functionality.
> > 
> > This goes to the binding description where you describe the hardware,
> > how I asked.
> > 
> > Please wrap commit message according to Linux coding style / submission
> > process (neither too early nor over the limit):
> > https://elixir.bootlin.com/linux/v6.4-rc1/source/Documentation/process/submitting-patches.rst#L597
> This series is sitting in my patchwork queue.
> Based upon Krzysztof's comments there should be a v4 that moves some
> descriptive text from 2/3 to 1/3.
> 
> Bjorn: Once v4 lands, do you want to take this series or should I?
> (Need to know if I should wait for ACK of 2/3 or give ACK for 3/3).
> 

I don't see any build-time dependencies between patch {1,2} and {3}. So
I'd suggest that I pick the two remoteproc patches and you pick the WiFi
patch.

Regards,
Bjorn

> /jeff

^ permalink raw reply

* [PATCH ath-current] wifi: ath12k: fix EAPOL TX failure caused by stale tcl_metadata bits
From: Baochen Qiang @ 2026-06-09  2:10 UTC (permalink / raw)
  To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang

On WCN7850, after the following sequence:

  1. load ath12k and connect to a non-MLO AP
  2. disconnect and connect to an MLO AP
  3. disconnect and reconnect to the non-MLO AP

the third connection always fails with a 4-Way handshake timeout. The
supplicant transmits message 2 of 4 four times in response to AP
retries of message 1, but the AP never sees any of them.

ath12k_dp_vdev_tx_attach() composes dp_link_vif->tcl_metadata using |=,
but dp_link_vif is embedded in struct ath12k_dp_vif and its slots are
reused across vif/peer teardown and setup. Since tcl_metadata is never
cleared on detach, vdev_id bits from a previous attach remain set when
the same link slot is reused with a different vdev_id. In this specific
issue, the same link slot is used for vdev_id 0, then vdev_id 1, then
vdev_id 0 again, the OR yields tcl_metadata == 0x9, which encodes
vdev_id 1 in the HTT_TCL_META_DATA_VDEV_ID field even though
ti.vdev_id is 0. Firmware then routes the EAPOL frame to the wrong
vdev and the AP never receives message 2.

Use plain assignment instead of |= so the field is fully recomputed
from the current arvif on every attach.

Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3

Fixes: af66c7640cf9 ("wifi: ath12k: Refactor ath12k_vif structure")
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
 drivers/net/wireless/ath/ath12k/dp.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c
index 90802ed1aa59..af5f11fc1d84 100644
--- a/drivers/net/wireless/ath/ath12k/dp.c
+++ b/drivers/net/wireless/ath/ath12k/dp.c
@@ -943,11 +943,11 @@ void ath12k_dp_vdev_tx_attach(struct ath12k *ar, struct ath12k_link_vif *arvif)
 
 	dp_link_vif = ath12k_dp_vif_to_dp_link_vif(&ahvif->dp_vif, link_id);
 
-	dp_link_vif->tcl_metadata |= u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) |
-				     u32_encode_bits(arvif->vdev_id,
-						     HTT_TCL_META_DATA_VDEV_ID) |
-				     u32_encode_bits(ar->pdev->pdev_id,
-						     HTT_TCL_META_DATA_PDEV_ID);
+	dp_link_vif->tcl_metadata = u32_encode_bits(1, HTT_TCL_META_DATA_TYPE) |
+				    u32_encode_bits(arvif->vdev_id,
+						    HTT_TCL_META_DATA_VDEV_ID) |
+				    u32_encode_bits(ar->pdev->pdev_id,
+						    HTT_TCL_META_DATA_PDEV_ID);
 
 	/* set HTT extension valid bit to 0 by default */
 	dp_link_vif->tcl_metadata &= ~HTT_TCL_META_DATA_VALID_HTT;

---
base-commit: 2e9422a6a2d8bf4036287c2fd3b034392af64048
change-id: 20260528-ath12k-fix-eapol-tcl-metadata-babf79570623

Best regards,
-- 
Baochen Qiang <baochen.qiang@oss.qualcomm.com>


^ permalink raw reply related

* Re: [PATCH 6.12.y] wifi: remove zero-length arrays
From: Carlos Llamas @ 2026-06-09  1:45 UTC (permalink / raw)
  To: Sasha Levin
  Cc: stable, yichenyu, kernel-team, Johannes Berg,
	syzbot+fd222bb38e916df26fa4, Lachlan Hodges,
	open list:802.11 (including CFG80211/NL80211), open list
In-Reply-To: <20260608-stable-reply-0011@kernel.org>

On Mon, Jun 08, 2026 at 08:51:57PM -0400, Sasha Levin wrote:
> > [PATCH 6.12.y] wifi: remove zero-length arrays
> 
> Queued for 6.12, thanks. I dropped the leftover "Change-Id:" trailer from
> the commit message while applying.

Oops, sorry about that. I was working from a gerrit mirror. Thanks for
fixing it.
--
Carlos Llamas

^ permalink raw reply

* Re: [PATCH 6.12.y] wifi: remove zero-length arrays
From: Sasha Levin @ 2026-06-09  0:51 UTC (permalink / raw)
  To: stable
  Cc: Sasha Levin, yichenyu, kernel-team, Johannes Berg,
	syzbot+fd222bb38e916df26fa4, Carlos Llamas, Lachlan Hodges,
	open list:802.11 (including CFG80211/NL80211), open list
In-Reply-To: <20260608133216.1396790-1-cmllamas@google.com>

> [PATCH 6.12.y] wifi: remove zero-length arrays

Queued for 6.12, thanks. I dropped the leftover "Change-Id:" trailer from
the commit message while applying.

--
Thanks,
Sasha

^ permalink raw reply

* RE: [PATCH next] wifi: rtw89: debug: fix off by on in rtw89_ppdu_str()
From: Ping-Ke Shih @ 2026-06-09  0:44 UTC (permalink / raw)
  To: Dan Carpenter, Damon Chen
  Cc: linux-wireless@vger.kernel.org, kernel-janitors@vger.kernel.org
In-Reply-To: <aia25i0ds3B6QF6c@stanley.mountain>

Dan Carpenter <error27@gmail.com> wrote:
> This > comparison should be >= to avoid an out of bounds access.
> 
> Fixes: 419ed7f4a053 ("wifi: rtw89: debug: extend bb_info with TX status and PER")
> Signed-off-by: Dan Carpenter <error27@gmail.com>

Thanks for the finding. 

Acked-by: Ping-Ke Shih <pkshih@realtek.com>



^ permalink raw reply

* Re: [PATCH] wifi: mt76: mt76u: use GRO on the USB RX path
From: Filip Bakreski @ 2026-06-09  0:35 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: nbd, ryder.lee, shayne.chen, sean.wang, linux-wireless
In-Reply-To: <aib-n_sCzWOjXFE1@lore-desk>

> I guess we do not need to add them, we can just reuse napi_dev pointer and
> napi[] array available in mt76_dev struct. Agree?

Agreed, I'll drop the new mt76_usb fields and reuse dev->napi_dev and dev->napi[MT_RXQ_MAIN] in v2

> I guess it would be interesting verifying if threaded-napi provides better
> results.

It does, on mt7921u a threaded NAPI servicing the RX queue averaged ~588 Mbit/s vs ~424 Mbit/s for the manually-driven NAPI. Suspend/resume also tested clean.

v2: https://lore.kernel.org/linux-wireless/20260609003224.132191-1-phial@phiality.com/

Thanks for the review,
Filip

^ permalink raw reply

* [PATCH v2] wifi: mt76: mt76u: use a threaded NAPI for the RX path
From: Filip Bakreski @ 2026-06-09  0:32 UTC (permalink / raw)
  To: nbd, lorenzo, ryder.lee; +Cc: shayne.chen, sean.wang, linux-wireless

The USB RX path delivers frames to the stack via mt76_rx_complete() with
a NULL napi pointer, taking the netif_receive_skb_list() path, so it never
benefits from GRO -- unlike the DMA-based mt76 drivers, which pass a real
napi and use napi_gro_receive(). For bulk TCP traffic this is costly, as
every segment traverses the stack individually.

Service the MT_RXQ_MAIN queue from a threaded NAPI, reusing mt76_dev's
existing napi_dev and napi[] rather than adding new fields. The URB
completion handler schedules the napi; its poll drains the URBs, builds
the skbs, resubmits and delivers them through napi_gro_receive(). The MCU
queue stays on the existing RX worker. This enables GRO and moves RX
processing into its own kernel thread, parallelising the datapath.

On mt7921u at HE-MCS 11 (2x2, 80 MHz; fast.com, multiple streams) this
averages ~588 Mbit/s, versus ~424 Mbit/s when the same napi is instead
driven manually from the RX worker, and ~380 Mbit/s for the unmodified
driver.

Suggested-by: Lorenzo Bianconi <lorenzo@kernel.org>
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Filip Bakreski <phial@phiality.com>
---
v2:
- Service MT_RXQ_MAIN from a threaded NAPI instead of a NAPI driven
  manually from the RX worker; on mt7921u the threaded variant measured
  ~39% faster (~588 vs ~424 Mbit/s, fast.com) (Lorenzo Bianconi).
- Reuse mt76_dev's existing napi_dev/napi[] instead of adding new fields
  to struct mt76_usb (Lorenzo Bianconi).

v1: https://lore.kernel.org/linux-wireless/20260608044109.31730-1-phial@phiality.com/

 drivers/net/wireless/mediatek/mt76/usb.c | 56 +++++++++++++++++++++---
 1 file changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
index d9638a9b7..aef8f855f 100644
--- a/drivers/net/wireless/mediatek/mt76/usb.c
+++ b/drivers/net/wireless/mediatek/mt76/usb.c
@@ -580,7 +580,10 @@ static void mt76u_complete_rx(struct urb *urb)
 
 	q->head = (q->head + 1) % q->ndesc;
 	q->queued++;
-	mt76_worker_schedule(&dev->usb.rx_worker);
+	if (q == &dev->q_rx[MT_RXQ_MAIN])
+		napi_schedule(&dev->napi[MT_RXQ_MAIN]);
+	else
+		mt76_worker_schedule(&dev->usb.rx_worker);
 out:
 	spin_unlock_irqrestore(&q->lock, flags);
 }
@@ -618,11 +621,23 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
 		}
 		mt76u_submit_rx_buf(dev, qid, urb);
 	}
-	if (qid == MT_RXQ_MAIN) {
-		local_bh_disable();
-		mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
-		local_bh_enable();
-	}
+}
+
+/* Threaded NAPI poll for the MAIN RX queue: drain URBs, build skbs, resubmit,
+ * then deliver through napi_gro_receive() and let napi_complete() flush GRO.
+ */
+static int mt76u_napi_poll(struct napi_struct *napi, int budget)
+{
+	struct mt76_dev *dev = mt76_priv(napi->dev);
+
+	rcu_read_lock();
+	mt76u_process_rx_queue(dev, &dev->q_rx[MT_RXQ_MAIN]);
+	mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi);
+	rcu_read_unlock();
+
+	napi_complete(napi);
+
+	return 0;
 }
 
 static void mt76u_rx_worker(struct mt76_worker *w)
@@ -632,8 +647,12 @@ static void mt76u_rx_worker(struct mt76_worker *w)
 	int i;
 
 	rcu_read_lock();
-	mt76_for_each_q_rx(dev, i)
+	mt76_for_each_q_rx(dev, i) {
+		/* MT_RXQ_MAIN is serviced by the threaded NAPI poll */
+		if (i == MT_RXQ_MAIN)
+			continue;
 		mt76u_process_rx_queue(dev, &dev->q_rx[i]);
+	}
 	rcu_read_unlock();
 }
 
@@ -723,6 +742,8 @@ void mt76u_stop_rx(struct mt76_dev *dev)
 	int i;
 
 	mt76_worker_disable(&dev->usb.rx_worker);
+	if (dev->napi_dev)
+		napi_disable(&dev->napi[MT_RXQ_MAIN]);
 
 	mt76_for_each_q_rx(dev, i) {
 		struct mt76_queue *q = &dev->q_rx[i];
@@ -751,6 +772,8 @@ int mt76u_resume_rx(struct mt76_dev *dev)
 	}
 
 	mt76_worker_enable(&dev->usb.rx_worker);
+	if (dev->napi_dev)
+		napi_enable(&dev->napi[MT_RXQ_MAIN]);
 
 	return 0;
 }
@@ -1051,6 +1074,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev)
 	mt76u_stop_rx(dev);
 	mt76u_stop_tx(dev);
 
+	/* mt76u_stop_rx() (above) already napi_disable()d the MAIN queue */
+	if (dev->napi_dev) {
+		netif_napi_del(&dev->napi[MT_RXQ_MAIN]);
+		free_netdev(dev->napi_dev);
+		dev->napi_dev = NULL;
+	}
+
 	mt76u_free_rx(dev);
 	mt76u_free_tx(dev);
 }
@@ -1115,6 +1145,18 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
 	sched_set_fifo_low(usb->rx_worker.task);
 	sched_set_fifo_low(usb->status_worker.task);
 
+	/* threaded NAPI on a dummy netdev (reusing mt76_dev's napi_dev/napi[])
+	 * services the MAIN RX queue and gives the RX path GRO
+	 */
+	dev->napi_dev = alloc_netdev_dummy(sizeof(struct mt76_dev *));
+	if (!dev->napi_dev)
+		return -ENOMEM;
+	*(struct mt76_dev **)netdev_priv(dev->napi_dev) = dev;
+	strscpy(dev->napi_dev->name, "mt76u-rx", sizeof(dev->napi_dev->name));
+	dev->napi_dev->threaded = 1;
+	netif_napi_add(dev->napi_dev, &dev->napi[MT_RXQ_MAIN], mt76u_napi_poll);
+	napi_enable(&dev->napi[MT_RXQ_MAIN]);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(__mt76u_init);

base-commit: 5f6099446d1ddb888e36cdf93b6a0551f05c1267
-- 
2.54.0


^ permalink raw reply related

* Re: [RESEND] Ongoing instability and connection loops with MT7922 (mt7921e driver)
From: Nick @ 2026-06-08 21:10 UTC (permalink / raw)
  To: Bitterblue Smith
  Cc: Angel Parra, Linux Wireless Mailing List, Felix Fietkau,
	Lorenzo Bianconi, Ryder Lee, Shayne Chen, Sean Wang
In-Reply-To: <c056f6b3-e2e2-4bfd-9cd0-14225bd4d510@gmail.com>

Hello Angel,

The first thing that comes to mind after reading your report is that
the connection between the antennas and card is not good. Recommend
that you remove the back of the laptop and check the connections or
get your tech support to do it.

My wife uses a laptop with mt7921 based card (kernel 7.0) and I am on
a desktop with a mt7922 based full size PCIe card (kernel 7.0). Both
operate very fast and stable through 2 walls and ~8 meters.


On Mon, Jun 8, 2026 at 2:12 PM Bitterblue Smith <rtl8821cerfe2@gmail.com> wrote:
>
> On 28/05/2026 04:44, Angel Parra wrote:
> > Hello,
> > I was advised to reach out to this mailing list regarding severe wireless connection issues with my MediaTek card. I am writing to request a review of the `mt7921e` driver. While I appreciate the significant improvements made to the `mt7902` driver recently, the support for some newer chipsets currently feels almost unusable in real-world conditions.
> >
> > My hardware details are as follows:
> > ```text
> > 02:00.0 Network controller: MEDIATEK Corp. MT7922 802.11ax PCI Express Wireless Network Adapter
> >         Subsystem: Foxconn International, Inc. Device e0cd
> >         Kernel driver in use: mt7921e
> > ```
> >
> > Below are the most consistent bugs and instabilities I have observed to the date that constantly interrupt our workflow:
> >
> > * The adapter often fails to discover nearby access points, despite multiple mobile phones confirming their active presence.
> > * Both 2.4GHz and 5GHz WLANs frequently remain undetected, even when the laptop is directly next to the router.
> > * Regardless of physical proximity to the modem, the connection process regularly hangs on "Configuring interface" (in the KDE Plasma desktop applet) for over 15 seconds. After this delay, the connection either succeeds or fails completely.
> > * When successfully connected to 2.4GHz bands, download speeds are remarkably slow and fall significantly below what my phone achieves on the same connection.
> > * The detection range is severely limited; establishing a link to a 5GHz SSID requires being in the exact same room as the access point.
> > * Introducing a single wall between the router and the laptop triggers the aforementioned 15-second "Configuring interface" hang, usually resulting in a failed connection.
> > * Upon failing to connect to an otherwise healthy access point, the system enters an infinite loop. It hangs on configuration, fails, jumps to the next saved SSID, fails again, and cycles endlessly through all saved profiles.
> > * Overall connection stability is exceptionally poor. A successful connection might persist only if the laptop remains perfectly stationary. Otherwise, the link randomly drops after a few minutes with a "Connection deactivated" state, triggering the failed connection loop.
> > * Attempting to bypass these issues by tethering to a mobile hotspot directly next to the laptop also fails. The adapter either cannot detect the hotspot at all or hangs during configuration, leaving the machine entirely offline.
> >
> > The failing connection loop in `dmesg` looks like this over a span of several minutes, clearly illustrating the continuous authentication timeouts as the adapter cycles through my saved profiles:
> >
> > ```text
> > [148485.661369] PM: suspend exit
> > [148488.781860] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148488.795305] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148488.843935] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148488.996043] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148489.044916] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148491.867424] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148491.880895] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148491.939014] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148492.094896] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148492.142676] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148495.354718] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148495.368216] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148495.418798] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148495.577799] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148495.625990] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148499.324558] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148499.337991] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148499.387641] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148499.542987] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148499.593575] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148510.294935] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148510.308356] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148510.360427] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148510.406754] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148510.566970] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148520.573843] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148520.587285] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148520.644274] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148520.693177] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148521.140382] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148541.462597] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148541.476062] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148541.522330] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148541.973505] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148542.022304] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148548.610998] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148548.624376] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148549.075668] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148549.121771] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148549.574886] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148552.427178] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148552.430014] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148552.470639] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148552.513942] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148552.556374] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148565.252575] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148565.266023] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148565.396382] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148565.446749] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148565.605849] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148579.997166] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148580.010594] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148580.072257] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148580.112398] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148580.152361] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148592.848439] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148592.861867] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148592.903480] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148592.995422] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148593.041528] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148607.612953] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148607.626393] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148607.698476] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148607.742091] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148607.783429] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148620.219579] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148620.233011] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148620.328013] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148620.374159] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148620.534020] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148633.224869] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148633.238306] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148633.303939] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148633.356603] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148633.514978] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148637.634640] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148637.647620] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148637.694360] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148637.743291] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148638.192244] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148655.509368] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148655.522809] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148655.572981] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [148656.021379] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [148656.070061] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [148677.626050] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [148677.639450] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [148677.704689] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [148677.753188] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [148677.914243] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [148680.509871] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [148680.512694] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [148680.530021] wlp2s0: authenticated
> > [148680.530280] wlp2s0: associate with 84:d8:1b:9f:91:10 (try 1/3)
> > [148680.555487] wlp2s0: RX AssocResp from 84:d8:1b:9f:91:10 (capab=0x411 status=0 aid=4)
> > [148680.582777] wlp2s0: associated
> > [149142.517235] nvidia-modeset: WARNING: GPU:0: Correcting number of heads for current head configuration (0x00)
> > [149361.823195] wlp2s0: Connection to AP 84:d8:1b:9f:91:10 lost
> > [149418.133906] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149418.145316] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149418.194402] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149418.242746] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149418.692292] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149437.001016] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149437.012391] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149437.095855] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149437.142168] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149437.593548] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149464.557438] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149464.568407] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149464.730630] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149464.777125] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149464.932766] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149474.631073] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149474.644554] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149474.690700] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149474.849313] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149474.895328] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149535.532981] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149535.546820] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149535.678835] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149535.725111] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149535.880819] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149879.705065] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149879.718556] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149879.794297] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149879.843592] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149880.017253] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149888.633997] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149888.647548] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149888.805077] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149888.856075] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149889.015401] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149928.562888] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149928.576448] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149928.619097] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149928.661687] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149928.737998] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149941.444820] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149941.458370] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149941.615999] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149941.662509] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149941.711201] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149956.605669] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149956.619156] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149956.673039] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149956.721411] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149957.172885] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149969.882794] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> > [149969.896301] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> > [149969.943140] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> > [149969.990954] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> > [149970.442757] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> > [149984.624558] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [149984.638187] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [149984.697458] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [149984.737544] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [149984.778744] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [150016.560488] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [150016.573492] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [150016.614971] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [150016.673246] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [150016.721924] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [150051.575890] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [150051.589489] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [150051.636535] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [150051.685162] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [150052.134899] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > [150064.827990] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> > [150064.841478] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> > [150064.966855] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> > [150065.015378] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> > [150065.174847] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> > ```
> >
> > Thank you for your time and for reading through this report. I would greatly appreciate it if this could be routed to the appropriate maintainers to help address these driver issues. Have a great day wherever you are.
> >
>
> Hi!
>
> These details could be helpful:
>
> - Laptop model
> - Wifi card's PCI ID (from "lspci -nn")
> - Name and version of the Linux distro
> - Full output of dmesg
>
> The mt7921e module has a parameter called "disable_aspm". Have you
> tried to set this to 1? Just a random thing you can try.
>
> I'm not sure if it's necessary with the Mediatek cards, but if you
> try this parameter, do a cold boot (set the parameter using the
> bootloader or a file in /etc/modprobe.d/, shut down the laptop, then
> turn it on again).
>
> I added the maintainers of mt76 to the Cc list.
>

^ permalink raw reply

* Re: [RESEND] Ongoing instability and connection loops with MT7922 (mt7921e driver)
From: Bitterblue Smith @ 2026-06-08 19:12 UTC (permalink / raw)
  To: Angel Parra, Linux Wireless Mailing List
  Cc: Felix Fietkau, Lorenzo Bianconi, Ryder Lee, Shayne Chen,
	Sean Wang
In-Reply-To: <fa96f303-ba3d-47f0-8ffe-04c088d22913@gmail.com>

On 28/05/2026 04:44, Angel Parra wrote:
> Hello,
> I was advised to reach out to this mailing list regarding severe wireless connection issues with my MediaTek card. I am writing to request a review of the `mt7921e` driver. While I appreciate the significant improvements made to the `mt7902` driver recently, the support for some newer chipsets currently feels almost unusable in real-world conditions.
> 
> My hardware details are as follows:
> ```text
> 02:00.0 Network controller: MEDIATEK Corp. MT7922 802.11ax PCI Express Wireless Network Adapter
>         Subsystem: Foxconn International, Inc. Device e0cd
>         Kernel driver in use: mt7921e
> ```
> 
> Below are the most consistent bugs and instabilities I have observed to the date that constantly interrupt our workflow:
> 
> * The adapter often fails to discover nearby access points, despite multiple mobile phones confirming their active presence.
> * Both 2.4GHz and 5GHz WLANs frequently remain undetected, even when the laptop is directly next to the router.
> * Regardless of physical proximity to the modem, the connection process regularly hangs on "Configuring interface" (in the KDE Plasma desktop applet) for over 15 seconds. After this delay, the connection either succeeds or fails completely.
> * When successfully connected to 2.4GHz bands, download speeds are remarkably slow and fall significantly below what my phone achieves on the same connection.
> * The detection range is severely limited; establishing a link to a 5GHz SSID requires being in the exact same room as the access point.
> * Introducing a single wall between the router and the laptop triggers the aforementioned 15-second "Configuring interface" hang, usually resulting in a failed connection.
> * Upon failing to connect to an otherwise healthy access point, the system enters an infinite loop. It hangs on configuration, fails, jumps to the next saved SSID, fails again, and cycles endlessly through all saved profiles.
> * Overall connection stability is exceptionally poor. A successful connection might persist only if the laptop remains perfectly stationary. Otherwise, the link randomly drops after a few minutes with a "Connection deactivated" state, triggering the failed connection loop.
> * Attempting to bypass these issues by tethering to a mobile hotspot directly next to the laptop also fails. The adapter either cannot detect the hotspot at all or hangs during configuration, leaving the machine entirely offline.
> 
> The failing connection loop in `dmesg` looks like this over a span of several minutes, clearly illustrating the continuous authentication timeouts as the adapter cycles through my saved profiles:
> 
> ```text
> [148485.661369] PM: suspend exit
> [148488.781860] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148488.795305] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148488.843935] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148488.996043] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148489.044916] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148491.867424] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148491.880895] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148491.939014] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148492.094896] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148492.142676] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148495.354718] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148495.368216] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148495.418798] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148495.577799] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148495.625990] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148499.324558] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148499.337991] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148499.387641] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148499.542987] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148499.593575] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148510.294935] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148510.308356] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148510.360427] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148510.406754] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148510.566970] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148520.573843] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148520.587285] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148520.644274] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148520.693177] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148521.140382] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148541.462597] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148541.476062] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148541.522330] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148541.973505] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148542.022304] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148548.610998] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148548.624376] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148549.075668] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148549.121771] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148549.574886] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148552.427178] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148552.430014] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148552.470639] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148552.513942] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148552.556374] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148565.252575] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148565.266023] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148565.396382] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148565.446749] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148565.605849] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148579.997166] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148580.010594] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148580.072257] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148580.112398] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148580.152361] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148592.848439] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148592.861867] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148592.903480] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148592.995422] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148593.041528] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148607.612953] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148607.626393] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148607.698476] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148607.742091] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148607.783429] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148620.219579] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148620.233011] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148620.328013] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148620.374159] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148620.534020] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148633.224869] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148633.238306] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148633.303939] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148633.356603] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148633.514978] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148637.634640] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148637.647620] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148637.694360] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148637.743291] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148638.192244] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148655.509368] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148655.522809] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148655.572981] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [148656.021379] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [148656.070061] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [148677.626050] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [148677.639450] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [148677.704689] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [148677.753188] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [148677.914243] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [148680.509871] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [148680.512694] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [148680.530021] wlp2s0: authenticated
> [148680.530280] wlp2s0: associate with 84:d8:1b:9f:91:10 (try 1/3)
> [148680.555487] wlp2s0: RX AssocResp from 84:d8:1b:9f:91:10 (capab=0x411 status=0 aid=4)
> [148680.582777] wlp2s0: associated
> [149142.517235] nvidia-modeset: WARNING: GPU:0: Correcting number of heads for current head configuration (0x00)
> [149361.823195] wlp2s0: Connection to AP 84:d8:1b:9f:91:10 lost
> [149418.133906] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149418.145316] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149418.194402] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149418.242746] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149418.692292] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149437.001016] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149437.012391] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149437.095855] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149437.142168] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149437.593548] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149464.557438] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149464.568407] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149464.730630] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149464.777125] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149464.932766] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149474.631073] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149474.644554] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149474.690700] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149474.849313] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149474.895328] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149535.532981] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149535.546820] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149535.678835] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149535.725111] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149535.880819] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149879.705065] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149879.718556] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149879.794297] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149879.843592] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149880.017253] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149888.633997] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149888.647548] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149888.805077] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149888.856075] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149889.015401] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149928.562888] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149928.576448] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149928.619097] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149928.661687] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149928.737998] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149941.444820] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149941.458370] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149941.615999] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149941.662509] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149941.711201] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149956.605669] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149956.619156] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149956.673039] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149956.721411] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149957.172885] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149969.882794] wlp2s0: authenticate with 84:d8:1b:9f:91:10 (local address=38:d5:7a:12:c2:3b)
> [149969.896301] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 1/3)
> [149969.943140] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 2/3)
> [149969.990954] wlp2s0: send auth to 84:d8:1b:9f:91:10 (try 3/3)
> [149970.442757] wlp2s0: authentication with 84:d8:1b:9f:91:10 timed out
> [149984.624558] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [149984.638187] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [149984.697458] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [149984.737544] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [149984.778744] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [150016.560488] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [150016.573492] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [150016.614971] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [150016.673246] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [150016.721924] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [150051.575890] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [150051.589489] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [150051.636535] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [150051.685162] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [150052.134899] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> [150064.827990] wlp2s0: authenticate with f8:64:b8:b8:3d:7a (local address=38:d5:7a:12:c2:3b)
> [150064.841478] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 1/3)
> [150064.966855] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 2/3)
> [150065.015378] wlp2s0: send auth to f8:64:b8:b8:3d:7a (try 3/3)
> [150065.174847] wlp2s0: authentication with f8:64:b8:b8:3d:7a timed out
> ```
> 
> Thank you for your time and for reading through this report. I would greatly appreciate it if this could be routed to the appropriate maintainers to help address these driver issues. Have a great day wherever you are.
> 

Hi!

These details could be helpful:

- Laptop model
- Wifi card's PCI ID (from "lspci -nn")
- Name and version of the Linux distro
- Full output of dmesg

The mt7921e module has a parameter called "disable_aspm". Have you
tried to set this to 1? Just a random thing you can try.

I'm not sure if it's necessary with the Mediatek cards, but if you
try this parameter, do a cold boot (set the parameter using the
bootloader or a file in /etc/modprobe.d/, shut down the laptop, then
turn it on again).

I added the maintainers of mt76 to the Cc list.

^ permalink raw reply

* Re: [PATCH wireless-next 4/5] wifi: cfg80211: Fragment per-link station stats in nl80211_dump_station()
From: Praneesh P @ 2026-06-08 19:01 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, ath12k
In-Reply-To: <88c183f827eb184e6d471964662b7ee89caf63ad.camel@sipsolutions.net>


On 6/8/2026 1:12 PM, Johannes Berg wrote:
> On Sun, 2026-06-07 at 23:29 +0530, P Praneesh wrote:
>>   
>> +static int nl80211_fill_link_station(struct sk_buff *msg,
>> +				     struct cfg80211_registered_device *rdev,
>> +				     struct link_station_info *link_sinfo)
>> +{
>> +	struct nlattr *bss_param, *link_sinfoattr;
>> +
>> +#define PUT_LINK_SINFO(attr, memb, type) do {				\
>> +	BUILD_BUG_ON(sizeof(type) == sizeof(u64));			\
>> +	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
>> +	    nla_put_ ## type(msg, NL80211_STA_INFO_ ## attr,		\
>> +			     link_sinfo->memb))				\
>> +		goto nla_put_failure;					\
>> +	} while (0)
>> +#define PUT_LINK_SINFO_U64(attr, memb) do {				\
>> +	if (link_sinfo->filled & BIT_ULL(NL80211_STA_INFO_ ## attr) &&	\
>> +	    nla_put_u64_64bit(msg, NL80211_STA_INFO_ ## attr,		\
>> +			      link_sinfo->memb, NL80211_STA_INFO_PAD))	\
>> +		goto nla_put_failure;					\
>> +	} while (0)
>> +
>> +	link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
> See previous note about _noflag() in all of this code - that shouldn't
> be there.
Noted, I will address it in next version.
>> +static int nl80211_send_link_station(struct sk_buff *msg,
>> +				     struct netlink_callback *cb,
>> +				     struct cfg80211_registered_device *rdev,
>> +				     struct wireless_dev *wdev,
>> +				     const u8 *mac_addr,
>> +				     struct station_info *sinfo,
>> +				     int link_idx)
>> +{
>> +	void *hdr;
>> +	struct nlattr *links, *link;
>> +	struct link_station_info *link_sinfo;
>> +	struct nlattr *sinfoattr;
>> +	int err;
>> +
>> +	hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).portid,
>> +			     cb->nlh->nlmsg_seq, NLM_F_MULTI,
>> +			     NL80211_CMD_NEW_STATION);
>> +	if (!hdr)
>> +		return -EMSGSIZE;
>> +
>> +	if ((wdev->netdev &&
>> +	     nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex)) ||
>> +	    nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, wdev_id(wdev),
>> +			      NL80211_ATTR_PAD) ||
>> +	    nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
>> +	    nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation)) {
>> +		err = -EMSGSIZE;
>> +		goto err_cancel;
>> +	}
>> +
>> +	sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
>> +	if (!sinfoattr) {
>> +		err = -EMSGSIZE;
>> +		goto err_cancel;
>> +	}
>> +
>> +	link_sinfo = sinfo->links[link_idx];
>> +	if (!link_sinfo) {
>> +		err = -ENOENT;
>> +		goto err_cancel;
>> +	}
>> +
>> +	nla_nest_end(msg, sinfoattr);
>> +	if (!is_valid_ether_addr(link_sinfo->addr)) {
>> +		err = -EADDRNOTAVAIL;
>> +		goto err_cancel;
> That seems really odd? why even bother going into the whole thing if
> it's invalid? Also, doesn't that ENOENT get propagated all the way and
> it aborts? I guess it does but it should never happen because of
> valid_links? Still seems a bit odd.
Both the link_sinfo null check and is_valid_ether_addr() check
will be moved to the top of the function, before any header or
nest construction. The null check for sinfo->links[link_idx] is
already guarded upstream by the valid_links bitmask so it should
never be NULL in practice, I'll add a WARN_ON_ONCE and skip rather
than returning -ENOENT, which would otherwise abort the entire dump.
Will it be okay ?
>> @@ -8354,13 +8616,22 @@ static int nl80211_dump_station(struct sk_buff *skb,
>>   	struct wireless_dev *wdev;
>>   	struct nl80211_dump_station_cb *cb_data = (void *)cb->ctx;
>>   	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
>> +	struct nlattr **attrbuf = NULL;
>>   	int err, ret;
>>   
>>   	NL_ASSERT_CTX_FITS(struct nl80211_dump_station_cb);
>>   
>> -	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, NULL);
>> -	if (err)
>> +	if (!ctx) {
>> +		attrbuf = kzalloc_objs(*attrbuf, NUM_NL80211_ATTR);
>> +		if (!attrbuf)
>> +			return -ENOMEM;
>> +	}
>> +
>> +	err = nl80211_prepare_wdev_dump(cb, &rdev, &wdev, attrbuf);
>> +	if (err) {
>> +		kfree(attrbuf);
>>   		return err;
>> +	}
>>   
>>   	/* nl80211_prepare_wdev_dump acquired it in the successful case */
>>   	__acquire(&rdev->wiphy.mtx);
>> @@ -8369,15 +8640,22 @@ static int nl80211_dump_station(struct sk_buff *skb,
>>   	if (!ctx) {
>>   		ctx = kzalloc_obj(*ctx);
>>   		if (!ctx) {
>> +			kfree(attrbuf);
> perhaps better to __free(kfree) instead of doing all these kfree()
> calls?
Yes, will use __free(kfree) for attrbuf.
>> -	/* Phase 0: dump aggregated station info */
>> -	if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
>> -		while (true) {
>> +	while (true) {
>> +		/* Phase 0: dump aggregated station info */
>> +		if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
> changing it now also kind of argues for not having it in the first patch
> in the first place ;-)
Noted, the loop restructuring will be moved entirely into
patch 4, not split across patches 3 and 4.
>>   			memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
>>   			for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) {
>>   				ctx->sinfo.links[i] =
>> @@ -8428,15 +8706,45 @@ static int nl80211_dump_station(struct sk_buff *skb,
>>   				goto out_err_release;
>>   			}
>>   
>> -			/* Reset ctx for next station */
>> -			cfg80211_sinfo_release_content(&ctx->sinfo);
>> -			ctx->sta_idx++;
>> +			ctx->phase = NL80211_DUMP_STA_PHASE_PER_LINK;
>>   		}
>> -	}
>>   
>> -	ctx->phase = NL80211_DUMP_STA_PHASE_AGGREGATED;
>> -	err = skb->len;
>> -	goto out_err;
>> +		/* Phase 1: dump per-link station info */
>> +		if (ctx->phase == NL80211_DUMP_STA_PHASE_PER_LINK &&
>> +		    ctx->dump_link_stats && ctx->sinfo.valid_links) {
>> +			while (ctx->link_idx < IEEE80211_MLD_MAX_NUM_LINKS) {
>> +				if (!(ctx->sinfo.valid_links &
>> +				      BIT(ctx->link_idx))) {
>> +					ctx->link_idx++;
>> +					continue;
>> +				}
>> +
>> +				ret = nl80211_send_link_station(skb, cb, rdev,
>> +								wdev,
>> +								ctx->mac_addr,
>> +								&ctx->sinfo,
>> +								ctx->link_idx);
>>
> I think conceptually this code is structured a bit strangely. It would
> seem a lot simpler to me to do something like
>
>
> 	// not literally such a macro, just the while (true) loop
> 	for_each_station_starting_from_idx(...) {
> 		if (ctx->phase == 0) {
> 			// fill sinfo etc.
> 		}
>
> 		// common stuff
> 		nl80211hdr_put()
> 		nla_put_u32(msg, NL80211_ATTR_IFINDEX...)
> 		nla_put_u64_64bit(msg, NL80211_ATTR_WDEV..)
> 		nla_put(msg, NL80211_ATTR_MAC...)
> 		nla_put_u32(msg, NL80211_ATTR_GENERATION...)
>
> 		switch (phase) {
> 		case 0:
> 			nl80211_put_sta_info_common(...);
> 			phase++;
> 			ctx->link_id = 0;
> 			break;
> 		case 1:
> 			if (!multi-phase-requested)
> 				break;
> 			nl80211_put_link_station(..., ctx->link_id);
> 			ctx->link_id++;
> 			if (link_id == NUM_LINKS)
> 				ctx->phase = 0; // next sta
> 			break;
> 		}
> 	}
>
>
> or something like that, more like we handle wiphy dump with all the
> messages, for example?
>
> I think it'd be better to have just one place that creates the header
> and all the common info.
>
> But maybe it's just my brain pathways being trained for that scheme.
>
> johannes
ok, I will try to align with this suggested model. I'll refactor so the 
outer loop
produces exactly one netlink message per iteration, with header
construction in one place:

while (true) {
     if (ctx->phase == PHASE_AGGREGATED) {
         /* fetch sinfo from driver once per station */
         memset(&ctx->sinfo, ...);
         /* allocate links[], call rdev_dump_station() */
         /* apply cfg80211_sta_set_mld_sinfo() if needed */
     }

     hdr = nl80211hdr_put(msg, ...);
     /* common attrs: ifindex, wdev, mac, generation */
     nla_put_u32(msg, NL80211_ATTR_IFINDEX, ...);
     nla_put_u64_64bit(msg, NL80211_ATTR_WDEV, ...);
     nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ctx->mac_addr);
     nla_put_u32(msg, NL80211_ATTR_GENERATION, ...);

     switch (ctx->phase) {
     case PHASE_AGGREGATED:
         nl80211_put_sta_info_common(msg, rdev, &ctx->sinfo);
         ctx->phase = PHASE_PER_LINK;
         ctx->link_idx = first_valid_link();
         break;
     case PHASE_PER_LINK:
         nl80211_put_link_station(msg, rdev, &ctx->sinfo, ctx->link_idx);
         advance ctx->link_idx to next valid link;
         if (no more links) {
             cfg80211_sinfo_release_content(&ctx->sinfo);
             ctx->sta_idx++;
             ctx->phase = PHASE_AGGREGATED;
         }
         break;
     }

     genlmsg_end(msg, hdr);   /* or genlmsg_cancel() on EMSGSIZE 
break-out */
}


This eliminates the duplicated nla_put_u32/u64/MAC header blocks
that currently exist in both nl80211_send_accumulated_station()
and nl80211_send_link_station(), and makes the EMSGSIZE bail-out
uniform since we return before committing the message.

should the rdev_dump_station() / sinfo fill stay outside the
per-message loop (as a separate if (phase == AGGREGATED) guard
at the top), or is it preferable to fold it into the switch
case? I've sketched the former above since fetching sinfo is
logically per-station, not per-message. I'm also ok to adjust
if you'd prefer it inside the switch ?

- Praneesh

^ permalink raw reply

* Re: [PATCH wireless-next 3/5] wifi: cfg80211: Refactor nl80211_dump_station() to prepare for per-link stats
From: Praneesh P @ 2026-06-08 18:02 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, ath12k
In-Reply-To: <39d12d9ae3b18495ed634715c93ede2f68c5bbf0.camel@sipsolutions.net>


On 6/8/2026 1:00 PM, Johannes Berg wrote:
> On Sun, 2026-06-07 at 23:29 +0530, P Praneesh wrote:
>> +++ b/net/wireless/nl80211.c
>> @@ -39,6 +39,39 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
>>   
>>   /* the netlink family */
>>   static struct genl_family nl80211_fam;
>> +/**
>> + * enum nl80211_dump_station_phase - station dump fragmentation phases
>> + * @NL80211_DUMP_STA_PHASE_AGGREGATED: send aggregated (MLO-combined) station
>> + *	statistics for the station entry
>> + * @NL80211_DUMP_STA_PHASE_PER_LINK: send per-link statistics for each active
>> + *	MLO link of the station; only used when dump_link_stats is set
>> + */
>> +enum nl80211_dump_station_phase {
>> +	NL80211_DUMP_STA_PHASE_AGGREGATED = 0,
>> +	NL80211_DUMP_STA_PHASE_PER_LINK   = 1,
>> +};
> [snip]
>
> probably nicer to move this stuff to closer to the dump code that needs
> it; at the very least it's missing a blank line.
>
> Also I think the whole 'phase' introduction etc. seems to more belong to
> patch 4 instead of this one? We can still do the structure allocation
> etc. here.
Will add the missing blank line and move both the enum and struct
definitions to sit just above nl80211_dump_station(). Patch 3 will
only introduce the context struct (sta_idx, mac_addr, sinfo) and
the stateful allocation/free mechanism. The phase field and its
enum will move to patch 4 where they are actually used.
>> +struct nl80211_dump_station_ctx {
>> +	int sta_idx;
>> +	enum nl80211_dump_station_phase phase;
>> +	u8 mac_addr[ETH_ALEN];
>> +	struct station_info sinfo;
>> +};
>> +
>> +/**
>> + * struct nl80211_dump_station_cb - state stored in netlink_callback::ctx
>> + * @wiphy_idx: args[0] - wiphy index from nl80211_prepare_wdev_dump
>> + * @wdev_id: args[1] - wdev identifier from nl80211_prepare_wdev_dump
>> + * @ctx: args[2] - dump station context pointer
>> + *
>> + * args[0] and args[1] are reserved by nl80211_prepare_wdev_dump().
>> + * The ctx pointer must live at args[2] to avoid corrupting those fields.
>> + */
>> +struct nl80211_dump_station_cb {
>> +	long wiphy_idx;
>> +	long wdev_id;
>> +	struct nl80211_dump_station_ctx *ctx;
>> +};
> I'm not sure I'm happy with this - better to just use args[2] directly
> for a pointer to struct nl80211_dump_station_ctx?
>
>>   static int nl80211_dump_station(struct sk_buff *skb,
>>   				struct netlink_callback *cb)
>>   {
>> -	struct station_info sinfo;
>>   	struct cfg80211_registered_device *rdev;
>>   	struct wireless_dev *wdev;
>> -	u8 mac_addr[ETH_ALEN];
>> -	int sta_idx = cb->args[2];
>> -	bool sinfo_alloc = false;
>> -	int err, i;
>> +	struct nl80211_dump_station_cb *cb_data = (void *)cb->ctx;
>> +	struct nl80211_dump_station_ctx *ctx = cb_data->ctx;
> This doesn't really seem better than just doing
>
> 	struct nl80211_dump_station_ctx *ctx = cb->args[2];
>
> given the overlay with generic code.
>
> Alternatively we could change the _whole_ nl80211 code (as a separate
> commit, perhaps even converting other things later) to have
>
> struct nl80211_dump_cb {
> 	long wiphy_idx;
> 	long wdev_id;
> 	union {
> 		long wiphy_filter;
> 		int mpath_idx;
> 		int reg_idx;
> 		int bss_idx;
> 		int survey_idx;
> 		//...
>
> 		// and later add
> 		struct nl80211_dump_station_ctx *sta_ctx;
> 	};
> };
>
> or so, and then we don't have the args[0]/... problem at all.
>
> But I'm not convinced mixing the structs with "must have this type at
> this offset" etc. is a good idea if it's going to stay this way.
Ok, I'll drop nl80211_dump_station_cb entirely and use:

struct nl80211_dump_station_ctx *ctx = (void *)cb->args[2];

directly, mirroring nl80211_dump_wiphy's pattern of casting
cb->args[0] to its state pointer. The cb->args[0]/[1] contract
is already established by nl80211_prepare_wdev_dump(), no
overlay struct is needed to document it.
>> +	/* Phase 0: dump aggregated station info */
>> +	if (ctx->phase == NL80211_DUMP_STA_PHASE_AGGREGATED) {
>> +		while (true) {
>> +			memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
>>
> if you have this memset, why do you need this one:
>
>> +out_err_release:
>> +	cfg80211_sinfo_release_content(&ctx->sinfo);
>> +	memset(&ctx->sinfo, 0, sizeof(ctx->sinfo));
> Seems a bit odd?
>
> johannes
The two memsets serve different purposes.

The first one (Phase 0 loop top) is a clean-slate reset before allocating
the per-station sinfo.links[] entries for each iteration.

The second one at out_err_release is a double-free guard. The ctx is
persistent across netlink dump callbacks (stored in cb->ctx), so when the
function returns early via out_err_release, nl80211_dump_station_done()
will eventually be called and it unconditionally invokes
cfg80211_sinfo_release_content() again:

if (ctx) {
   cfg80211_sinfo_release_content(&ctx->sinfo);
   kfree(ctx);
}

Without the memset at out_err_release, sinfo.links[] would still hold the
already-freed pointers when _done() runs, resulting in a double-free.
The memset nullifies those pointers so the second
cfg80211_sinfo_release_content() call in _done() hits
kfree(NULL), which is harmless.

^ permalink raw reply

* Re: [PATCH wireless-next 1/5] wifi: cfg80211: Drop unused link stats handling in nl80211_send_station()
From: Praneesh P @ 2026-06-08 17:53 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, ath12k
In-Reply-To: <ba13edbcfa590fe2bd764719b843e6ee8c675310.camel@sipsolutions.net>


On 6/8/2026 12:47 PM, Johannes Berg wrote:
> On Sun, 2026-06-07 at 23:29 +0530, P Praneesh wrote:
>> -static int nl80211_fill_link_station(struct sk_buff *msg,
>> -				     struct cfg80211_registered_device *rdev,
>> -				     struct link_station_info *link_sinfo)
> It's a bit odd that you're dropping this only to reintroduce it in patch
> 4, but I guess it served at least to notice that:
Sure, I'll restructure the series so patch 1 retains 
nl80211_fill_link_station() as-is and only removes
the dead link_stats path from nl80211_send_station(). The function will 
then be modified in
patch 4 where nla_nest_start_noflag -> nla_nest_start is corrected as 
part of introducing the
new per-link send path.
>> -	link_sinfoattr = nla_nest_start_noflag(msg, NL80211_ATTR_STA_INFO);
> all of the nla_nest_start() calls in it really shouldn't have been
> _noflag in the new code at all.
>
> johannes
Understood. NL80211_ATTR_STA_INFO, NL80211_STA_INFO_BSS_PARAM,
NL80211_STA_INFO_TID_STATS, and the per-tid nested attribute are all 
properly-nested
types and must use nla_nest_start(), not nla_nest_start_noflag(). All 
occurrences in new
code will be fixed in v2.

-Praneesh

^ permalink raw reply

* Re: [PATCH wireless-next 0/5] wifi: cfg80211: Add fragmented per-link station stats in MLO
From: Praneesh P @ 2026-06-08 17:48 UTC (permalink / raw)
  To: Johannes Berg; +Cc: linux-wireless, ath12k
In-Reply-To: <7d251045171a4146604b8788479efc0e99224ff8.camel@sipsolutions.net>


On 6/8/2026 12:46 PM, Johannes Berg wrote:
> On Sun, 2026-06-07 at 23:29 +0530, P Praneesh wrote:
>> This series introduces fragmentation support for Multi-Link Operation
>> (MLO) station statistics in nl80211.
>>
>> The current nl80211_dump_station() implementation works well for legacy
>> (single-link) stations, but it does not scale for MLO peers. With
>> multiple links per station, dumping all per-link information in a single
>> netlink message can exceed size limits, resulting in -EMSGSIZE errors
>> and incomplete dumps.
>>
>> With 802.11be (Wi‑Fi 7), a single station may have multiple links, each
>> with its own statistics. To address this, this series introduces a
>> stateful dump mechanism that splits station information into multiple
>> messages when required.
>>
>> The dump is performed in two phases:
>>    - Phase 0: Aggregated (station-level) statistics
>>    - Phase 1: Per-link statistics sent in separate messages
> [snip]
>
> Overall, this approach seems (mostly?) right. I have a couple of
> comments reading the code itself, but also:
Thanks a lot for your quick and detailed review on this series.
>
>> Aggregated Stats (Phase 0)
>>
>> NL80211_CMD_NEW_STATION
>>   ├─ NL80211_ATTR_IFINDEX
>>   ├─ NL80211_ATTR_MAC
>>   ├─ NL80211_ATTR_GENERATION
>>   └─ NL80211_ATTR_STA_INFO
>>        ├─ <aggregated station attributes>
>>        └─ ...
>>
>> Note: No NL80211_ATTR_MLO_LINKS is included in this phase.
>> Per‑link stats are sent in Phase 1.
>>
>> Per-Link Stats (Phase 1)
>>
>> NL80211_CMD_NEW_STATION
>>    ├─ NL80211_ATTR_IFINDEX
>>    ├─ NL80211_ATTR_MAC                 (MLO MAC)
>>    ├─ NL80211_ATTR_GENERATION
>>    └─ NL80211_ATTR_STA_INFO
>>         └─ NL80211_ATTR_MLO_LINKS
>>              ├─ [link0]
>>              │    ├─ NL80211_ATTR_MLO_LINK_ID = 0
>>              │    ├─ NL80211_ATTR_MAC = <link0 MAC>
>>              │    ├─ NL80211_STA_INFO_RX_BYTES
>>              │    ├─ NL80211_STA_INFO_TX_BYTES
>>              │    └─ ... more link0 stats ...
> This description is missing another nesting level as
> NL80211_ATTR_STA_INFO no?
>
> johannes
Yes, you are right. The per-link stat attributes are wrapped in a nested
NL80211_ATTR_STA_INFO inside each link entry. The diagram incorrectly
placed them directly under [linkN]. The correct layout is:

NL80211_CMD_NEW_STATION
├─ NL80211_ATTR_IFINDEX
├─ NL80211_ATTR_MAC = (MLO MAC)
├─ NL80211_ATTR_GENERATION
└─ NL80211_ATTR_STA_INFO
      └─ NL80211_ATTR_MLO_LINKS
           └─ [linkN]
                ├─ NL80211_ATTR_MLO_LINK_ID = N
                ├─ NL80211_ATTR_MAC = <linkN MAC>
                └─ NL80211_ATTR_STA_INFO
                     ├─ NL80211_STA_INFO_RX_BYTES
                     ├─ NL80211_STA_INFO_TX_BYTES

                     └─ ...

I will correct it in the next version.

Praneesh.



^ permalink raw reply

* Re: [PATCH] wifi: mt76: mt76u: use GRO on the USB RX path
From: Lorenzo Bianconi @ 2026-06-08 17:40 UTC (permalink / raw)
  To: Filip Bakreski; +Cc: nbd, ryder.lee, shayne.chen, sean.wang, linux-wireless
In-Reply-To: <20260608044109.31730-1-phial@phiality.com>

[-- Attachment #1: Type: text/plain, Size: 4557 bytes --]

> The USB RX path delivers frames to the stack via mt76_rx_complete() with
> a NULL napi pointer, which takes the netif_receive_skb_list() path and
> therefore never benefits from GRO. The DMA-based mt76 drivers pass a real
> napi and get napi_gro_receive(); the USB path does not. For bulk TCP
> traffic this is costly, as every segment traverses the network stack
> individually instead of being coalesced.
> 
> Add a small container NAPI on a dummy netdev that the RX worker drives
> manually: napi_schedule_prep() marks it scheduled, frames are delivered
> through napi_gro_receive(), and napi_complete_done() flushes the coalesced
> list. The poll handler is never invoked by the core.
> 
> On mt7921u at HE-MCS 11 (2x2, 80 MHz) this raises single-stream TCP
> download throughput from ~383 to ~475 Mbit/s (~+24%), averaged over six
> interleaved A/B measurements. The gain only applies while the link is not
> RF-limited, as expected for a host-side optimisation.
> 
> Assisted-by: Claude:claude-opus-4-8
> Signed-off-by: Filip Bakreski <phial@phiality.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt76.h |  4 +++
>  drivers/net/wireless/mediatek/mt76/usb.c  | 36 ++++++++++++++++++++++-
>  2 files changed, 39 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
> index 07955555f..f5e52c1f4 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt76.h
> @@ -668,6 +668,10 @@ struct mt76_usb {
>  	struct mt76_worker status_worker;
>  	struct mt76_worker rx_worker;
>  
> +	/* container NAPI used only to batch GRO for the RX worker */
> +	struct net_device *napi_dev;
> +	struct napi_struct napi;

I guess we do not need to add them, we can just reuse napi_dev pointer and
napi[] array available in mt76_dev struct. Agree?

> +
>  	struct work_struct stat_work;
>  
>  	u8 out_ep[__MT_EP_OUT_MAX];
> diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
> index d9638a9b7..f9c48140c 100644
> --- a/drivers/net/wireless/mediatek/mt76/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/usb.c
> @@ -619,12 +619,31 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
>  		mt76u_submit_rx_buf(dev, qid, urb);
>  	}
>  	if (qid == MT_RXQ_MAIN) {
> +		struct napi_struct *napi = &dev->usb.napi;
> +
>  		local_bh_disable();
> -		mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
> +		/* Drive a container NAPI so the RX path can use
> +		 * napi_gro_receive(): napi_schedule_prep() marks it SCHED and
> +		 * napi_complete_done() flushes the coalesced GRO list. The poll
> +		 * handler is never actually invoked by the core.
> +		 */
> +		if (dev->usb.napi_dev && napi_schedule_prep(napi)) {
> +			mt76_rx_poll_complete(dev, MT_RXQ_MAIN, napi);
> +			napi_complete_done(napi, 1);
> +		} else {
> +			mt76_rx_poll_complete(dev, MT_RXQ_MAIN, NULL);
> +		}
>  		local_bh_enable();
>  	}
>  }
>  
> +/* Never invoked by the core: the RX worker drives GRO via the napi manually. */
> +static int mt76u_napi_poll(struct napi_struct *napi, int budget)
> +{
> +	napi_complete(napi);
> +	return 0;
> +}
> +
>  static void mt76u_rx_worker(struct mt76_worker *w)
>  {
>  	struct mt76_usb *usb = container_of(w, struct mt76_usb, rx_worker);
> @@ -1051,6 +1070,13 @@ void mt76u_queues_deinit(struct mt76_dev *dev)
>  	mt76u_stop_rx(dev);
>  	mt76u_stop_tx(dev);
>  
> +	if (dev->usb.napi_dev) {
> +		napi_disable(&dev->usb.napi);
> +		netif_napi_del(&dev->usb.napi);
> +		free_netdev(dev->usb.napi_dev);
> +		dev->usb.napi_dev = NULL;
> +	}
> +
>  	mt76u_free_rx(dev);
>  	mt76u_free_tx(dev);
>  }
> @@ -1115,6 +1141,14 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
>  	sched_set_fifo_low(usb->rx_worker.task);
>  	sched_set_fifo_low(usb->status_worker.task);
>  
> +	/* container netdev + NAPI used only to enable GRO on the RX path */
> +	usb->napi_dev = alloc_netdev_dummy(0);
> +	if (!usb->napi_dev)
> +		return -ENOMEM;
> +	strscpy(usb->napi_dev->name, "mt76u-rx", sizeof(usb->napi_dev->name));
> +	netif_napi_add(usb->napi_dev, &usb->napi, mt76u_napi_poll);

I guess it would be interesting verifying if threaded-napi provides better
results.

Regards,
Lorenzo

> +	napi_enable(&usb->napi);
> +
>  	return 0;
>  }
>  EXPORT_SYMBOL_GPL(__mt76u_init);
> 
> base-commit: 5f6099446d1ddb888e36cdf93b6a0551f05c1267
> -- 
> 2.54.0
> 
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH net] wifi: mac80211: fix memory leak in ieee80211_register_hw()
From: Jeff Johnson @ 2026-06-08 17:24 UTC (permalink / raw)
  To: Dawei Feng, johannes
  Cc: linux-wireless, linux-kernel, jianhao.xu, stable, Zilin Guan
In-Reply-To: <20260608145543.3443390-1-dawei.feng@seu.edu.cn>

On 6/8/2026 7:55 AM, Dawei Feng wrote:
> If kmemdup() fails while copying supported band structures, the error
> path jumps to fail_rate. This skips rate_control_deinitialize() and
> leaks the initialized local->rate_ctrl.
> 
> Fix this by redirecting the error path to fail_wiphy_register to
> ensure proper cleanup.
> 
> The bug was first flagged by an experimental analysis tool we are
> developing for kernel memory-management bugs while analyzing
> v6.13-rc1. The tool is still under development and is not yet publicly
> available. Manual inspection confirms that the bug is still present in
> v7.1-rc7.
> 
> An x86_64 allyesconfig build showed no new warnings. As we do not have a
> suitable mac80211 device/driver combination to test with, no runtime
> testing was able to be performed.
> 
> Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support")
> Cc: stable@vger.kernel.org
> Signed-off-by: Zilin Guan <zilin@seu.edu.cn>

why is this SOB here?

> Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>

this is the posted author of the patch, and this patch hasn't been posted
previously, so it is unclear why there is an additional S-o-b

> ---
>  net/mac80211/main.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/net/mac80211/main.c b/net/mac80211/main.c
> index f47dd58770ad..9306e0af3b5f 100644
> --- a/net/mac80211/main.c
> +++ b/net/mac80211/main.c
> @@ -1599,7 +1599,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
>  		sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL);
>  		if (!sband) {
>  			result = -ENOMEM;
> -			goto fail_rate;
> +			goto fail_wiphy_register;

I'm wondering if it would be more logical to have another label at the same
place, i.e. fail_band, since it is illogical to goto fail_wiphy_register when
you aren't performing the wiphy_register function

>  		}
>  
>  		wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n",




^ permalink raw reply

* Re: [PATCH] ath12k: fix NULL pointer dereference in rhash table destroy
From: Jeff Johnson @ 2026-06-08 17:08 UTC (permalink / raw)
  To: Jose Ignacio Tornos Martinez, jjohnson
  Cc: linux-wireless, ath12k, linux-kernel, stable
In-Reply-To: <20260604071032.659009-1-jtornosm@redhat.com>

On 6/4/2026 12:10 AM, Jose Ignacio Tornos Martinez wrote:
> When unbinding the ath12k driver, kernel NULL pointer dereferences
> occur in irq_work_sync() called from rhashtable_destroy().
> 
> Two hash tables are affected:
> 1. ath12k_link_sta hash table in ath12k_base
> 2. ath12k_dp_link_peer hash table in ath12k_dp
> 
> The issue happens because the destroy functions are called unconditionally
> in cleanup paths, but the hash tables are only initialized late in their
> respective init functions. If the device was never fully started or if the
> init functions failed before initializing the hash tables, the pointers
> will be NULL. The issues are always reproducible from a VM because the MSI
> addressing initialization is failing.

My preference would be to fix the logic so that the deinit is symmetric with
init, and if any stage of init fails, it should unwind whatever init occurred
up to that point. So this patch seems to be a bandage instead of an engineered
fix.

But I'll let the Qualcomm engineering team give their opinion.

> 
> Call trace for ath12k_link_sta_rhash_tbl_destroy:
>  RIP: irq_work_sync+0x1e/0x70
>  rhashtable_destroy+0x12/0x60
>  ath12k_link_sta_rhash_tbl_destroy+0x19/0x40 [ath12k]
>  ath12k_core_stop+0xe/0x80 [ath12k]
>  ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k]
>  ath12k_pci_remove+0x60/0x110 [ath12k]
> 
> Call trace for ath12k_dp_link_peer_rhash_tbl_destroy:
>  RIP: irq_work_sync+0x1e/0x70
>  rhashtable_destroy+0x12/0x60
>  ath12k_dp_link_peer_rhash_tbl_destroy+0x29/0x50 [ath12k]
>  ath12k_dp_cmn_device_deinit+0x21/0x140 [ath12k]
>  ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k]
>  ath12k_pci_remove+0x60/0x110 [ath12k]
> 
> Fix this by adding NULL checks before calling rhashtable_destroy() in
> both destroy functions.
> 
> Fixes: 57ccca410237 ("wifi: ath12k: Add hash table for ath12k_link_sta in ath12k_base")
> Fixes: a88cf5f71adf ("wifi: ath12k: Add hash table for ath12k_dp_link_peer")
> Cc: stable@vger.kernel.org
> Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
> ---
>  drivers/net/wireless/ath/ath12k/dp_peer.c | 5 +++++
>  drivers/net/wireless/ath/ath12k/peer.c    | 3 +++
>  2 files changed, 8 insertions(+)
> 
> diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
> index a1100782d45e..38045564e223 100644
> --- a/drivers/net/wireless/ath/ath12k/dp_peer.c
> +++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
> @@ -275,9 +275,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)
>  void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)
>  {
>  	mutex_lock(&dp->link_peer_rhash_tbl_lock);
> +	if (!dp->rhead_peer_addr)
> +		goto unlock;

not a fan of this pattern.
if we go with a bandaid solution then i'd want to use guard(mutex) so that
there isn't a need for a cleanup goto -- we could just return like the patch below

> +
>  	rhashtable_destroy(dp->rhead_peer_addr);
>  	kfree(dp->rhead_peer_addr);
>  	dp->rhead_peer_addr = NULL;
> +
> +unlock:
>  	mutex_unlock(&dp->link_peer_rhash_tbl_lock);
>  }
>  
> diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
> index 2e875176baaa..80fee2ce68f1 100644
> --- a/drivers/net/wireless/ath/ath12k/peer.c
> +++ b/drivers/net/wireless/ath/ath12k/peer.c
> @@ -444,6 +444,9 @@ int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab)
>  
>  void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab)
>  {
> +	if (!ab->rhead_sta_addr)
> +		return;
> +
>  	rhashtable_destroy(ab->rhead_sta_addr);
>  	kfree(ab->rhead_sta_addr);
>  	ab->rhead_sta_addr = NULL;


^ permalink raw reply

* Re: [PATCH v2 0/7] arm64: dts: qcom: enable WiFi/BT on SM8350 HDK
From: Rob Herring @ 2026-06-08 15:18 UTC (permalink / raw)
  To: Dmitry Baryshkov
  Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
	Krzysztof Wilczyński, Bjorn Helgaas, Konrad Dybcio, Qiang Yu,
	Jeff Johnson, Liam Girdwood, Mark Brown, Krzysztof Kozlowski,
	Conor Dooley, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	Bjorn Andersson, Konrad Dybcio, linux-arm-msm, linux-pci,
	linux-kernel, linux-wireless, ath11k, devicetree,
	Bartosz Golaszewski, linux-bluetooth, Bartosz Golaszewski
In-Reply-To: <20260608-sm8350-wifi-v2-0-efb68f1ff04c@oss.qualcomm.com>

On Mon, Jun 08, 2026 at 09:59:18AM +0300, Dmitry Baryshkov wrote:
> The SM8350 HDK has an onboard WCN6851 WiFi/BT chip, which for a long
> time was not supported. Bring up different pieces required to enable
> this SoC.
> 
> Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
> ---
> Changes in v2:
> - Bumped num_vdevs to 4 to follow other similar devices (Jeff)
> - Link to v1: https://patch.msgid.link/20260601-sm8350-wifi-v1-0-242917d88031@oss.qualcomm.com
> 
> ---
> Dmitry Baryshkov (7):
>       PCI: qcom: fix parsing of PERST# in the legacy case
>       wifi: ath11k: enable support for WCN6851
>       regulator: dt-bindings: qcom,qca6390-pmu: document WCN6851
>       dt-bindings: bluetooth: qcom,wcn6855-bt: document WCN6851
>       arm64: dts: qcom: sm8350: expand UART18 to 4 pins config
>       arm64: dts: qcom: sm8350: modernize PCIe entries
>       arm64: dts: qcom: sm8350-hdk: describe WiFi/BT chip

Before adding new devices, can you (Qcom) fix the all the existing DT 
warnings related to QCom WiFi/BT:

      6 (qcom,wcn6855-bt): 'vddrfa1p7-supply' is a required property
      6 (qcom,wcn6855-bt): Unevaluated properties are not allowed ('vddrfa1p8-supply' was unexpected)
      2 (qcom,wcn6855-bt): 'vddwlmx-supply' is a required property
      2 (qcom,wcn6855-bt): 'vddwlcx-supply' is a required property
      2 (qcom,wcn6855-bt): 'vddbtcmx-supply' is a required property
      2 (qcom,wcn6855-bt): 'vddaon-supply' is a required property
      2 (pci17cb,1103): 'vddwlmx-supply' is a required property
      2 (pci17cb,1103): 'vddwlcx-supply' is a required property
      2 (pci17cb,1103): 'vddrfacmn-supply' is a required property
      2 (pci17cb,1103): 'vddrfa1p8-supply' is a required property
      2 (pci17cb,1103): 'vddrfa1p2-supply' is a required property
      2 (pci17cb,1103): 'vddrfa0p8-supply' is a required property
      2 (pci17cb,1103): 'vddpcie1p8-supply' is a required property
      2 (pci17cb,1103): 'vddpcie0p9-supply' is a required property
      2 (pci17cb,1103): 'vddaon-supply' is a required property

Rob

^ permalink raw reply

* [PATCH net] wifi: mac80211: fix memory leak in ieee80211_register_hw()
From: Dawei Feng @ 2026-06-08 14:55 UTC (permalink / raw)
  To: johannes
  Cc: linux-wireless, linux-kernel, jianhao.xu, Dawei Feng, stable,
	Zilin Guan

If kmemdup() fails while copying supported band structures, the error
path jumps to fail_rate. This skips rate_control_deinitialize() and
leaks the initialized local->rate_ctrl.

Fix this by redirecting the error path to fail_wiphy_register to
ensure proper cleanup.

The bug was first flagged by an experimental analysis tool we are
developing for kernel memory-management bugs while analyzing
v6.13-rc1. The tool is still under development and is not yet publicly
available. Manual inspection confirms that the bug is still present in
v7.1-rc7.

An x86_64 allyesconfig build showed no new warnings. As we do not have a
suitable mac80211 device/driver combination to test with, no runtime
testing was able to be performed.

Fixes: 09b4a4faf9d0 ("mac80211: introduce capability flags for VHT EXT NSS support")
Cc: stable@vger.kernel.org
Signed-off-by: Zilin Guan <zilin@seu.edu.cn>
Signed-off-by: Dawei Feng <dawei.feng@seu.edu.cn>
---
 net/mac80211/main.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/net/mac80211/main.c b/net/mac80211/main.c
index f47dd58770ad..9306e0af3b5f 100644
--- a/net/mac80211/main.c
+++ b/net/mac80211/main.c
@@ -1599,7 +1599,7 @@ int ieee80211_register_hw(struct ieee80211_hw *hw)
 		sband = kmemdup(sband, sizeof(*sband), GFP_KERNEL);
 		if (!sband) {
 			result = -ENOMEM;
-			goto fail_rate;
+			goto fail_wiphy_register;
 		}
 
 		wiphy_dbg(hw->wiphy, "copying sband (band %d) due to VHT EXT NSS BW flag\n",
-- 
2.34.1


^ permalink raw reply related


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