* Re: [PATCH 3/5] wifi: mt76: mt7921: refactor regulatory notifier flow
From: Sean Wang @ 2026-02-23 23:17 UTC (permalink / raw)
To: JB Tsai
Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-3-jb.tsai@mediatek.com>
Hi, JB
On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Rename mt7921_regd_update() to mt7921_mcu_regd_update() to centralize
> regd updates with error handling.
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
> .../net/wireless/mediatek/mt76/mt7921/pci.c | 2 +-
> .../net/wireless/mediatek/mt76/mt7921/regd.c | 45 +++++++++++++------
> .../net/wireless/mediatek/mt76/mt7921/regd.h | 3 +-
> 3 files changed, 35 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> index a173a61f2b49..3fdf55c056a6 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> @@ -545,7 +545,7 @@ static int mt7921_pci_resume(struct device *device)
> if (err < 0)
> goto failed;
>
> - mt7921_regd_update(dev);
> + mt7921_mcu_regd_update(dev, mdev->alpha2, dev->country_ie_env);
> err = mt7921_mcu_radio_led_ctrl(dev, EXT_CMD_RADIO_ON_LED);
> failed:
> pm->suspended = false;
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> index 70440ab8ba82..f795ee2eb446 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -72,18 +72,43 @@ mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> }
> }
>
> -void mt7921_regd_update(struct mt792x_dev *dev)
> +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
> + enum environment_cap country_ie_env)
To keep naming consistent, how about renaming mt7921_mcu_regd_update
to mt7921_regd_mcu_update, matching the mt7921_regd_* prefix?
> {
> struct mt76_dev *mdev = &dev->mt76;
> struct ieee80211_hw *hw = mdev->hw;
> struct wiphy *wiphy = hw->wiphy;
> + int ret = 0;
> +
> + dev->regd_in_progress = true;
> +
> + mt792x_mutex_acquire(dev);
> + if (!dev->regd_change)
> + goto err;
> +
> + ret = mt7921_mcu_set_clc(dev, alpha2, country_ie_env);
> + if (ret < 0)
> + goto err;
>
I’d prefer to keep the refactor logic-preserving. The new error
handling and flag sequencing the dev->regd_change handling looks like
a functional change, so it would be clearer as a follow-up patch where
the behavior change can be explained. For example, the
dev->regd_change handling could be moved to the “wifi: mt76: mt7921:
add auto regdomain switch support” patch so the regd_change flow is
handled end-to-end there.
> - mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
> mt7921_regd_channel_update(wiphy, dev);
> - mt76_connac_mcu_set_channel_domain(hw->priv);
> - mt7921_set_tx_sar_pwr(hw, NULL);
> +
> + ret = mt76_connac_mcu_set_channel_domain(hw->priv);
> + if (ret < 0)
> + goto err;
> +
> + ret = mt7921_set_tx_sar_pwr(hw, NULL);
> + if (ret < 0)
> + goto err;
> +
> +err:
> + mt792x_mutex_release(dev);
> + dev->regd_change = false;
> + dev->regd_in_progress = false;
> + wake_up(&dev->wait);
> +
> + return ret;
> }
> -EXPORT_SYMBOL_GPL(mt7921_regd_update);
> +EXPORT_SYMBOL_GPL(mt7921_mcu_regd_update);
>
> void mt7921_regd_notifier(struct wiphy *wiphy,
> struct regulatory_request *request)
> @@ -106,12 +131,6 @@ void mt7921_regd_notifier(struct wiphy *wiphy,
> if (pm->suspended)
> return;
>
> - dev->regd_in_progress = true;
> -
> - mt792x_mutex_acquire(dev);
> - mt7921_regd_update(dev);
> - mt792x_mutex_release(dev);
> -
> - dev->regd_in_progress = false;
> - wake_up(&dev->wait);
> + mt7921_mcu_regd_update(dev, request->alpha2,
> + request->country_ie_env);
> }
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> index 74bc2fdd532c..da5bd4450312 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -6,7 +6,8 @@
>
> #include "mt7921.h"
>
> -void mt7921_regd_update(struct mt792x_dev *dev);
> +int mt7921_mcu_regd_update(struct mt792x_dev *dev, u8 *alpha2,
> + enum environment_cap country_ie_env);
> void mt7921_regd_notifier(struct wiphy *wiphy,
> struct regulatory_request *request);
> bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
> --
> 2.45.2
>
>
^ permalink raw reply
* [PATCH ath-next] wifi: ath9k: use kmemdup and kcalloc
From: Rosen Penev @ 2026-02-23 22:44 UTC (permalink / raw)
To: linux-wireless; +Cc: Toke Høiland-Jørgensen, open list
Simplifies the code slightly by removing temporary variables.
multiplication overflow is also gained for free.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/wireless/ath/ath9k/ar9002_hw.c | 6 +++---
drivers/net/wireless/ath/ath9k/common-init.c | 8 ++------
drivers/net/wireless/ath/ath9k/init.c | 8 +++-----
drivers/net/wireless/ath/ath9k/recv.c | 4 +---
4 files changed, 9 insertions(+), 17 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ar9002_hw.c b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
index b26224480041..0f24539b75ec 100644
--- a/drivers/net/wireless/ath/ath9k/ar9002_hw.c
+++ b/drivers/net/wireless/ath/ath9k/ar9002_hw.c
@@ -80,14 +80,14 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
/* iniAddac needs to be modified for these chips */
if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) {
struct ar5416IniArray *addac = &ah->iniAddac;
- u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns;
+ u32 n = addac->ia_rows * addac->ia_columns;
u32 *data;
- data = devm_kzalloc(ah->dev, size, GFP_KERNEL);
+ data = devm_kmemdup_array(ah->dev, addac->ia_array, n, sizeof(u32),
+ GFP_KERNEL);
if (!data)
return -ENOMEM;
- memcpy(data, addac->ia_array, size);
addac->ia_array = data;
if (!AR_SREV_5416_22_OR_LATER(ah)) {
diff --git a/drivers/net/wireless/ath/ath9k/common-init.c b/drivers/net/wireless/ath/ath9k/common-init.c
index da102c791712..52e02e0752d8 100644
--- a/drivers/net/wireless/ath/ath9k/common-init.c
+++ b/drivers/net/wireless/ath/ath9k/common-init.c
@@ -133,13 +133,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
ATH9K_NUM_CHANNELS);
if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
- channels = devm_kzalloc(ah->dev,
+ channels = devm_kmemdup(ah->dev, ath9k_2ghz_chantable,
sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
if (!channels)
return -ENOMEM;
- memcpy(channels, ath9k_2ghz_chantable,
- sizeof(ath9k_2ghz_chantable));
common->sbands[NL80211_BAND_2GHZ].channels = channels;
common->sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ;
common->sbands[NL80211_BAND_2GHZ].n_channels =
@@ -150,13 +148,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
}
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
- channels = devm_kzalloc(ah->dev,
+ channels = devm_kmemdup(ah->dev, ath9k_5ghz_chantable,
sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
if (!channels)
return -ENOMEM;
- memcpy(channels, ath9k_5ghz_chantable,
- sizeof(ath9k_5ghz_chantable));
common->sbands[NL80211_BAND_5GHZ].channels = channels;
common->sbands[NL80211_BAND_5GHZ].band = NL80211_BAND_5GHZ;
common->sbands[NL80211_BAND_5GHZ].n_channels =
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index b52ddb237dcf..e52775dda6a7 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -297,7 +297,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
{
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
u8 *ds;
- int i, bsize, desc_len;
+ int i, desc_len;
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
name, nbuf, ndesc);
@@ -351,8 +351,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
if (is_tx) {
struct ath_buf *bf;
- bsize = sizeof(struct ath_buf) * nbuf;
- bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+ bf = devm_kcalloc(sc->dev, sizeof(*bf), nbuf, GFP_KERNEL);
if (!bf)
return -ENOMEM;
@@ -382,8 +381,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
} else {
struct ath_rxbuf *bf;
- bsize = sizeof(struct ath_rxbuf) * nbuf;
- bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
+ bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbuf, GFP_KERNEL);
if (!bf)
return -ENOMEM;
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index 34c74ed99b7b..93b41a1bb2af 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -202,7 +202,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
struct sk_buff *skb;
struct ath_rxbuf *bf;
int error = 0, i;
- u32 size;
ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
ah->caps.rx_status_len);
@@ -212,8 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
ah->caps.rx_hp_qdepth);
- size = sizeof(struct ath_rxbuf) * nbufs;
- bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
+ bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbufs, GFP_KERNEL);
if (!bf)
return -ENOMEM;
--
2.53.0
^ permalink raw reply related
* [PATCH ath-next] wifi: ath9k: owl: move name into owl_nvmem_probe
From: Rosen Penev @ 2026-02-23 22:42 UTC (permalink / raw)
To: linux-wireless
Cc: Toke Høiland-Jørgensen, Andreas Färber,
Manivannan Sadhasivam,
moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
moderated list:ARM/ACTIONS SEMI ARCHITECTURE, open list
There is no need for dynamic allocation for a simple string.
request_firmware_nowait copies the string internally anyway.
The error message on failure is also wrong. It's an allocation failure,
not a find failure.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
.../wireless/ath/ath9k/ath9k_pci_owl_loader.c | 31 +++++--------------
1 file changed, 7 insertions(+), 24 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
index fe1013a3a588..b9ef34709202 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
+++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
@@ -137,24 +137,6 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
release_firmware(fw);
}
-static const char *owl_get_eeprom_name(struct pci_dev *pdev)
-{
- struct device *dev = &pdev->dev;
- char *eeprom_name;
-
- dev_dbg(dev, "using auto-generated eeprom filename\n");
-
- eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
- if (!eeprom_name)
- return NULL;
-
- /* this should match the pattern used in ath9k/init.c */
- scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
- dev_name(dev));
-
- return eeprom_name;
-}
-
static void owl_nvmem_work(struct work_struct *work)
{
struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
@@ -195,8 +177,9 @@ static int owl_nvmem_probe(struct owl_ctx *ctx)
static int owl_probe(struct pci_dev *pdev,
const struct pci_device_id *id)
{
+ char eeprom_name[EEPROM_FILENAME_LEN];
+ struct device *dev = &pdev->dev;
struct owl_ctx *ctx;
- const char *eeprom_name;
int err = 0;
if (pci_enable_device(pdev))
@@ -215,11 +198,11 @@ static int owl_probe(struct pci_dev *pdev,
if (err <= 0)
return err;
- eeprom_name = owl_get_eeprom_name(pdev);
- if (!eeprom_name) {
- dev_err(&pdev->dev, "no eeprom filename found.\n");
- return -ENODEV;
- }
+ dev_dbg(dev, "using auto-generated eeprom filename\n");
+
+ /* this should match the pattern used in ath9k/init.c */
+ scnprintf(eeprom_name, sizeof(eeprom_name), "ath9k-eeprom-pci-%s.bin",
+ dev_name(dev));
err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
&pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 2/5] wifi: mt76: mt7921: refactor CLC support check flow
From: Sean Wang @ 2026-02-23 22:41 UTC (permalink / raw)
To: JB Tsai
Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-2-jb.tsai@mediatek.com>
HI, JB
On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Move the disable_clc module parameter to regd.c and introduce
> mt7925_regd_clc_supported() to centralize CLC support checks.
typo, s/7925/7921/
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
> drivers/net/wireless/mediatek/mt76/mt7921/mcu.c | 14 ++++++++------
> drivers/net/wireless/mediatek/mt76/mt7921/regd.c | 13 +++++++++++++
> drivers/net/wireless/mediatek/mt76/mt7921/regd.h | 1 +
> 3 files changed, 22 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> index 8442dbd2ee23..1e2afa736cdf 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c
> @@ -4,6 +4,7 @@
> #include <linux/fs.h>
> #include <linux/firmware.h>
> #include "mt7921.h"
> +#include "regd.h"
> #include "mcu.h"
> #include "../mt76_connac2_mac.h"
> #include "../mt792x_trace.h"
> @@ -11,10 +12,6 @@
> #define MT_STA_BFER BIT(0)
> #define MT_STA_BFEE BIT(1)
>
> -static bool mt7921_disable_clc;
> -module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
> -MODULE_PARM_DESC(disable_clc, "disable CLC support");
> -
> int mt7921_mcu_parse_response(struct mt76_dev *mdev, int cmd,
> struct sk_buff *skb, int seq)
> {
> @@ -422,8 +419,7 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
> u8 *clc_base = NULL, hw_encap = 0;
>
> dev->phy.clc_chan_conf = 0xff;
> - if (mt7921_disable_clc ||
> - mt76_is_usb(&dev->mt76))
> + if (!mt7921_regd_clc_supported(dev))
> return 0;
>
> if (mt76_is_mmio(&dev->mt76)) {
> @@ -470,6 +466,9 @@ static int mt7921_load_clc(struct mt792x_dev *dev, const char *fw_name)
> for (offset = 0; offset < len; offset += le32_to_cpu(clc->len)) {
> clc = (const struct mt7921_clc *)(clc_base + offset);
>
> + if (clc->idx >= ARRAY_SIZE(phy->clc))
> + break;
> +
I’d keep the refactor logic-preserving. This bounds check is a
correctness fix (prevents OOB on phy->clc[clc->idx])
so please send it as a separate hardening patch and describe the failure and fix
Also, ARRAY_SIZE() is for compile-time arrays only if phy->clc were a
pointer, it would be invalid/misleading I guess.
> /* do not init buf again if chip reset triggered */
> if (phy->clc[clc->idx])
> continue;
> @@ -1403,6 +1402,9 @@ int mt7921_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2,
> struct mt792x_phy *phy = (struct mt792x_phy *)&dev->phy;
> int i, ret;
>
> + if (!ARRAY_SIZE(phy->clc))
> + return -ESRCH;
> +
Ditto, to keep the refactor logic-preserving, send it as a separate
hardening patch and describe the failure and fix and also
!ARRAY_SIZE(phy->clc) isn’t a meaningful runtime check I guess.
> /* submit all clc config */
> for (i = 0; i < ARRAY_SIZE(phy->clc); i++) {
> ret = __mt7921_mcu_set_clc(dev, alpha2, env_cap,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> index 6e6c81189222..70440ab8ba82 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -7,6 +7,19 @@
> #include "regd.h"
> #include "mcu.h"
>
> +static bool mt7921_disable_clc;
> +module_param_named(disable_clc, mt7921_disable_clc, bool, 0644);
> +MODULE_PARM_DESC(disable_clc, "disable CLC support");
> +
should we explicitly include <linux/moduleparam.h> ?
> +bool mt7921_regd_clc_supported(struct mt792x_dev *dev)
> +{
> + if (mt7921_disable_clc ||
> + mt76_is_usb(&dev->mt76))
> + return false;
> +
> + return true;
> +}
> +
> static void
> mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> {
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> index 0ba6161e1919..74bc2fdd532c 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -9,5 +9,6 @@
> void mt7921_regd_update(struct mt792x_dev *dev);
> void mt7921_regd_notifier(struct wiphy *wiphy,
> struct regulatory_request *request);
> +bool mt7921_regd_clc_supported(struct mt792x_dev *dev);
>
> #endif
> --
> 2.45.2
>
>
^ permalink raw reply
* 6.18.13 iwlwifi deadlock allocating cma while work-item is active.
From: Ben Greear @ 2026-02-23 22:36 UTC (permalink / raw)
To: linux-wireless; +Cc: Korenblit, Miriam Rachel, linux-mm
Hello,
I hit a deadlock related to CMA mem allocation attempting to flush all work
while holding some wifi related mutex, and with a work-queue attempting to process a wifi regdomain
work item. I really don't see any good way to fix this,
it would seem that any code that was holding a mutex that could block a work-queue
cannot safely allocate CMA memory? Hopefully someone else has a better idea.
For whatever reason, my hacked up kernel will print out the sysrq process stack traces I need
to understand this, and my stable 6.18.13 will not. But, the locks-held matches in both cases, so almost
certainly this is same problem. I can reproduce the same problem on both un-modified stable
and my own. The details below are from my modified 6.18.9+ kernel.
I only hit this (reliably?) with a KASAN enabled kernel, likely because it makes things slow enough to
hit the problem and/or causes CMA allocations in a different manner.
General way to reproduce is to have large amounts of intel be200 radios in a system, and bring them
admin up and down.
## From 6.18.13 (un-modified)
40479 Feb 23 14:13:31 ct523c-de7c kernel: 5 locks held by kworker/u32:11/34989:
40480 Feb 23 14:13:31 ct523c-de7c kernel: #0: ffff888120161148 ((wq_completion)events_unbound){+.+.}-{0:0}, at: process_one_work+0xf7a/0x17b0
40481 Feb 23 14:13:31 ct523c-de7c kernel: #1: ffff8881a561fd20 ((work_completion)(&rdev->wiphy_work)){+.+.}-{0:0}, at: process_one_work+0x7ca/0x17b0
40482 Feb 23 14:13:31 ct523c-de7c kernel: #2: ffff88815e618788 (&rdev->wiphy.mtx){+.+.}-{4:4}, at: cfg80211_wiphy_work+0x5c/0x570 [cfg80211]
40483 Feb 23 14:13:31 ct523c-de7c kernel: #3: ffffffff87232e60 (&cma->alloc_mutex){+.+.}-{4:4}, at: __cma_alloc+0x3c5/0xd20
40484 Feb 23 14:13:31 ct523c-de7c kernel: #4: ffffffff8534f668 (lock#5){+.+.}-{4:4}, at: __lru_add_drain_all+0x5f/0x530
40488 Feb 23 14:13:31 ct523c-de7c kernel: 4 locks held by kworker/1:0/39480:
40489 Feb 23 14:13:31 ct523c-de7c kernel: #0: ffff88812006b148 ((wq_completion)events){+.+.}-{0:0}, at: process_one_work+0xf7a/0x17b0
40490 Feb 23 14:13:31 ct523c-de7c kernel: #1: ffff88814087fd20 (reg_work){+.+.}-{0:0}, at: process_one_work+0x7ca/0x17b0
40491 Feb 23 14:13:31 ct523c-de7c kernel: #2: ffffffff85970028 (rtnl_mutex){+.+.}-{4:4}, at: reg_todo+0x18/0x770 [cfg80211]
40492 Feb 23 14:13:31 ct523c-de7c kernel: #3: ffff88815e618788 (&rdev->wiphy.mtx){+.+.}-{4:4}, at: reg_process_self_managed_hints+0x70/0x190 [cfg80211]
## Rest of this is from my 6.18.9+ hacks kernel.
### thread trying to allocate cma is blocked here, trying to flush work.
Type "apropos word" to search for commands related to "word"...
Reading symbols from vmlinux...
(gdb) l *(alloc_contig_range_noprof+0x1de)
0xffffffff8162453e is in alloc_contig_range_noprof (/home2/greearb/git/linux-6.18.dev.y/mm/page_alloc.c:6798).
6793 .reason = MR_CONTIG_RANGE,
6794 };
6795
6796 lru_cache_disable();
6797
6798 while (pfn < end || !list_empty(&cc->migratepages)) {
6799 if (fatal_signal_pending(current)) {
6800 ret = -EINTR;
6801 break;
6802 }
(gdb) l *(__lru_add_drain_all+0x19b)
0xffffffff815ae44b is in __lru_add_drain_all (/home2/greearb/git/linux-6.18.dev.y/mm/swap.c:884).
879 queue_work_on(cpu, mm_percpu_wq, work);
880 __cpumask_set_cpu(cpu, &has_work);
881 }
882 }
883
884 for_each_cpu(cpu, &has_work)
885 flush_work(&per_cpu(lru_add_drain_work, cpu));
886
887 done:
888 mutex_unlock(&lock);
(gdb)
#### and other thread is trying to process a regdom request, and trying to use
# rcu and rtnl???
Type "apropos word" to search for commands related to "word"...
Reading symbols from net/wireless/cfg80211.ko...
(gdb) l *(reg_todo+0x18)
0xe238 is in reg_todo (/home2/greearb/git/linux-6.18.dev.y/net/wireless/reg.c:3107).
3102 */
3103 static void reg_process_pending_hints(void)
3104 {
3105 struct regulatory_request *reg_request, *lr;
3106
3107 lr = get_last_request();
3108
3109 /* When last_request->processed becomes true this will be rescheduled */
3110 if (lr && !lr->processed) {
3111 pr_debug("Pending regulatory request, waiting for it to be processed...\n");
(gdb)
static struct regulatory_request *get_last_request(void)
{
return rcu_dereference_rtnl(last_request);
}
task:kworker/6:0 state:D stack:0 pid:56 tgid:56 ppid:2 task_flags:0x4208060 flags:0x00080000
Workqueue: events reg_todo [cfg80211]
Call Trace:
<TASK>
__schedule+0x526/0x1290
preempt_schedule_notrace+0x35/0x50
preempt_schedule_notrace_thunk+0x16/0x30
rcu_is_watching+0x2a/0x30
lock_acquire+0x26d/0x2c0
schedule+0xac/0x120
? schedule+0x8d/0x120
schedule_preempt_disabled+0x11/0x20
__mutex_lock+0x726/0x1070
? reg_todo+0x18/0x2b0 [cfg80211]
? reg_todo+0x18/0x2b0 [cfg80211]
reg_todo+0x18/0x2b0 [cfg80211]
process_one_work+0x221/0x6d0
worker_thread+0x1e5/0x3b0
? rescuer_thread+0x450/0x450
kthread+0x108/0x220
? kthreads_online_cpu+0x110/0x110
ret_from_fork+0x1c6/0x220
? kthreads_online_cpu+0x110/0x110
ret_from_fork_asm+0x11/0x20
</TASK>
task:ip state:D stack:0 pid:72857 tgid:72857 ppid:72843 task_flags:0x400100 flags:0x00080001
Call Trace:
<TASK>
__schedule+0x526/0x1290
? schedule+0x8d/0x120
? schedule+0xe2/0x120
schedule+0x36/0x120
schedule_timeout+0xf9/0x110
? mark_held_locks+0x40/0x70
__wait_for_common+0xbe/0x1e0
? hrtimer_nanosleep_restart+0x120/0x120
? __flush_work+0x20b/0x530
__flush_work+0x34e/0x530
? flush_workqueue_prep_pwqs+0x160/0x160
? bpf_prog_test_run_tracing+0x160/0x2d0
__lru_add_drain_all+0x19b/0x220
alloc_contig_range_noprof+0x1de/0x8a0
__cma_alloc+0x1f1/0x6a0
__dma_direct_alloc_pages.isra.0+0xcb/0x2f0
dma_direct_alloc+0x7b/0x250
dma_alloc_attrs+0xa1/0x2a0
_iwl_pcie_ctxt_info_dma_alloc_coherent+0x31/0xb0 [iwlwifi]
iwl_pcie_ctxt_info_alloc_dma+0x20/0x50 [iwlwifi]
iwl_pcie_init_fw_sec+0x2fc/0x380 [iwlwifi]
iwl_pcie_ctxt_info_v2_alloc+0x19e/0x530 [iwlwifi]
iwl_trans_pcie_gen2_start_fw+0x2e2/0x820 [iwlwifi]
? lock_is_held_type+0x92/0x100
iwl_trans_start_fw+0x77/0x90 [iwlwifi]
iwl_mld_load_fw_wait_alive+0x97/0x2c0 [iwlmld]
? iwl_mld_mac80211_sta_state+0x780/0x780 [iwlmld]
? lock_is_held_type+0x92/0x100
iwl_mld_load_fw+0x91/0x240 [iwlmld]
? ieee80211_open+0x3d/0xe0 [mac80211]
? lock_is_held_type+0x92/0x100
iwl_mld_start_fw+0x44/0x470 [iwlmld]
iwl_mld_mac80211_start+0x3d/0x1b0 [iwlmld]
drv_start+0x6f/0x1d0 [mac80211]
ieee80211_do_open+0x2d6/0x960 [mac80211]
ieee80211_open+0x62/0xe0 [mac80211]
__dev_open+0x11a/0x2e0
__dev_change_flags+0x1f8/0x280
netif_change_flags+0x22/0x60
do_setlink.isra.0+0xe57/0x11a0
? __mutex_lock+0xb0/0x1070
? __mutex_lock+0x99e/0x1070
? __nla_validate_parse+0x5e/0xcd0
? rtnl_newlink+0x355/0xb50
? cap_capable+0x90/0x100
? security_capable+0x72/0x80
rtnl_newlink+0x7e8/0xb50
? __lock_acquire+0x436/0x2190
? lock_acquire+0xc2/0x2c0
? rtnetlink_rcv_msg+0x97/0x660
? find_held_lock+0x2b/0x80
? do_setlink.isra.0+0x11a0/0x11a0
? rtnetlink_rcv_msg+0x3ea/0x660
? lock_release+0xcc/0x290
? do_setlink.isra.0+0x11a0/0x11a0
rtnetlink_rcv_msg+0x409/0x660
? rtnl_fdb_dump+0x240/0x240
netlink_rcv_skb+0x56/0x100
netlink_unicast+0x1e1/0x2d0
netlink_sendmsg+0x219/0x460
__sock_sendmsg+0x38/0x70
____sys_sendmsg+0x214/0x280
? import_iovec+0x2c/0x30
? copy_msghdr_from_user+0x6c/0xa0
___sys_sendmsg+0x85/0xd0
? __lock_acquire+0x436/0x2190
? find_held_lock+0x2b/0x80
? lock_acquire+0xc2/0x2c0
? mntput_no_expire+0x43/0x460
? find_held_lock+0x2b/0x80
? mntput_no_expire+0x8c/0x460
__sys_sendmsg+0x6b/0xc0
do_syscall_64+0x6b/0x11b0
entry_SYSCALL_64_after_hwframe+0x4b/0x53
Thanks,
Ben
--
Ben Greear <greearb@candelatech.com>
Candela Technologies Inc http://www.candelatech.com
^ permalink raw reply
* [PATCH 25/62] wlcore: Fix a locking bug
From: Bart Van Assche @ 2026-02-23 22:00 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
Marco Elver, Christoph Hellwig, Steven Rostedt, Nick Desaulniers,
Nathan Chancellor, Kees Cook, Jann Horn, Bart Van Assche,
Johannes Berg, Eyal Reizer, Tony Lindgren, Kalle Valo,
linux-wireless
In-Reply-To: <20260223220102.2158611-1-bart.vanassche@linux.dev>
From: Bart Van Assche <bvanassche@acm.org>
Make sure that wl->mutex is locked before it is unlocked. This has been
detected by the Clang thread-safety analyzer.
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: Eyal Reizer <eyalr@ti.com>
Cc: Tony Lindgren <tony@atomide.com>
Cc: Kalle Valo <kvalo@codeaurora.org>
Cc: linux-wireless@vger.kernel.org
Fixes: 45aa7f071b06 ("wlcore: Use generic runtime pm calls for wowlan elp configuration")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/net/wireless/ti/wlcore/main.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c
index 17dd417756f2..1c340a4a0930 100644
--- a/drivers/net/wireless/ti/wlcore/main.c
+++ b/drivers/net/wireless/ti/wlcore/main.c
@@ -1875,6 +1875,8 @@ static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
wl->wow_enabled);
WARN_ON(!wl->wow_enabled);
+ mutex_lock(&wl->mutex);
+
ret = pm_runtime_force_resume(wl->dev);
if (ret < 0) {
wl1271_error("ELP wakeup failure!");
@@ -1891,8 +1893,6 @@ static int __maybe_unused wl1271_op_resume(struct ieee80211_hw *hw)
run_irq_work = true;
spin_unlock_irqrestore(&wl->wl_lock, flags);
- mutex_lock(&wl->mutex);
-
/* test the recovery flag before calling any SDIO functions */
pending_recovery = test_bit(WL1271_FLAG_RECOVERY_IN_PROGRESS,
&wl->flags);
^ permalink raw reply related
* [PATCH 24/62] net/cw1200: Fix locking in error paths
From: Bart Van Assche @ 2026-02-23 22:00 UTC (permalink / raw)
To: Peter Zijlstra
Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
Marco Elver, Christoph Hellwig, Steven Rostedt, Nick Desaulniers,
Nathan Chancellor, Kees Cook, Jann Horn, Bart Van Assche,
Kalle Valo, Johannes Berg, linux-wireless
In-Reply-To: <20260223220102.2158611-1-bart.vanassche@linux.dev>
From: Bart Van Assche <bvanassche@acm.org>
cw1200_wow_suspend() must only return with priv->conf_mutex locked if it
returns zero. This mutex must be unlocked if an error is returned. Add
mutex_unlock() calls to the error paths from which that call is missing.
This has been detected by the Clang thread-safety analyzer.
Cc: Kalle Valo <kvalo@codeaurora.org>
Cc: Johannes Berg <johannes@sipsolutions.net>
Cc: linux-wireless@vger.kernel.org
Fixes: a910e4a94f69 ("cw1200: add driver for the ST-E CW1100 & CW1200 WLAN chipsets")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
drivers/net/wireless/st/cw1200/pm.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c
index 120f0379f81d..84eb15d729c7 100644
--- a/drivers/net/wireless/st/cw1200/pm.c
+++ b/drivers/net/wireless/st/cw1200/pm.c
@@ -264,12 +264,14 @@ int cw1200_wow_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan)
wiphy_err(priv->hw->wiphy,
"PM request failed: %d. WoW is disabled.\n", ret);
cw1200_wow_resume(hw);
+ mutex_unlock(&priv->conf_mutex);
return -EBUSY;
}
/* Force resume if event is coming from the device. */
if (atomic_read(&priv->bh_rx)) {
cw1200_wow_resume(hw);
+ mutex_unlock(&priv->conf_mutex);
return -EAGAIN;
}
^ permalink raw reply related
* [PATCHv2 wireless-next] wifi: rt2x00: use generic nvmem_cell_get
From: Rosen Penev @ 2026-02-23 21:40 UTC (permalink / raw)
To: linux-wireless; +Cc: Stanislaw Gruszka, open list
The library doesn't necessarily depend on OF. This codepath is used by
both soc (OF only) and pci (no such requirement). After this, the only
of specific function is of_get_mac_address, which is needed for nvmem.
Signed-off-by: Rosen Penev <rosenp@gmail.com>
---
drivers/net/wireless/ralink/rt2x00/rt2800lib.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
index 65d0f805459c..93e4ce604171 100644
--- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
+++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c
@@ -10965,13 +10965,13 @@ EXPORT_SYMBOL_GPL(rt2800_read_eeprom_efuse);
int rt2800_read_eeprom_nvmem(struct rt2x00_dev *rt2x00dev)
{
- struct device_node *np = rt2x00dev->dev->of_node;
+ struct device *dev = rt2x00dev->dev;
unsigned int len = rt2x00dev->ops->eeprom_size;
struct nvmem_cell *cell;
const void *data;
size_t retlen;
- cell = of_nvmem_cell_get(np, "eeprom");
+ cell = nvmem_cell_get(dev, "eeprom");
if (IS_ERR(cell))
return PTR_ERR(cell);
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 00/15] AES-CMAC library
From: Eric Biggers @ 2026-02-23 21:28 UTC (permalink / raw)
To: linux-crypto
Cc: linux-kernel, Ard Biesheuvel, Jason A . Donenfeld, Herbert Xu,
linux-arm-kernel, linux-cifs, linux-wireless
In-Reply-To: <20260218213501.136844-1-ebiggers@kernel.org>
On Wed, Feb 18, 2026 at 01:34:46PM -0800, Eric Biggers wrote:
> This series can also be retrieved from:
>
> git fetch https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git aes-cmac-v1
>
> This series adds support for AES CBC-based MACs to the crypto library,
> specifically AES-CMAC, AES-XCBC-MAC, and AES-CBC-MAC. The
> implementation is fully optimized with the existing
> architecture-optimized AES code, either single-block AES en/decryption
> or arm64's neon_aes_mac_update() and ce_aes_mac_update(). As usual,
> optimizations are now enabled by default as well.
>
> AES-CMAC support will be useful for at least the SMB client and server,
> and the bluetooth and mac80211 drivers. Patches 8-15 convert these
> users to use the crypto library API instead of crypto_shash, though
> these patches will likely go in via subsystem trees later. They result
> in some significant simplifications and performance improvements.
>
> As usual, a KUnit test suite, FIPS self-test, and traditional crypto API
> wrapper algorithms are included as well.
>
> Note that I'm also planning to add additional AES modes to the library.
> This is just an initial set of AES modes to get things started.
> Notably, with the SMB client and server already using the SHA* and MD5
> libraries, "cmac(aes)" was the only remaining use of crypto_shash there.
> So it makes sense to take care of that.
>
> Eric Biggers (15):
> lib/crypto: aes: Add support for CBC-based MACs
> crypto: aes - Add cmac, xcbc, and cbcmac algorithms using library
> crypto: arm64/aes - Fix 32-bit aes_mac_update() arg treated as 64-bit
> lib/crypto: arm64/aes: Move assembly code for AES modes into libaes
> lib/crypto: arm64/aes: Migrate optimized CBC-based MACs into library
> lib/crypto: tests: Add KUnit tests for CBC-based MACs
> lib/crypto: aes: Add FIPS self-test for CMAC
> smb: client: Use AES-CMAC library for SMB3 signature calculation
> smb: client: Remove obsolete cmac(aes) allocation
> smb: client: Make generate_key() return void
> smb: client: Drop 'allocate_crypto' arg from smb*_calc_signature()
> ksmbd: Use AES-CMAC library for SMB3 signature calculation
> Bluetooth: SMP: Use AES-CMAC library API
> wifi: mac80211: Use AES-CMAC library in ieee80211_aes_cmac()
> wifi: mac80211: Use AES-CMAC library in aes_s2v()
Applied patches 1-7 and 14-15 to
https://git.kernel.org/pub/scm/linux/kernel/git/ebiggers/linux.git/log/?h=libcrypto-next
Patches 8-13 (smb client, smb server, and bluetooth) can go in via
subsystem trees later.
I edited "lib/crypto: arm64/aes: Move assembly code for AES modes into
libaes" to update the file comments to remove the file paths.
- Eric
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath10k: use non devm for nvmem_cell_get
From: Loic Poulain @ 2026-02-23 20:52 UTC (permalink / raw)
To: Rosen Penev
Cc: linux-wireless, Jeff Johnson,
open list:QUALCOMM ATHEROS ATH10K WIRELESS DRIVER, open list
In-Reply-To: <20260223024854.18910-1-rosenp@gmail.com>
On Mon, Feb 23, 2026 at 3:49 AM Rosen Penev <rosenp@gmail.com> wrote:
>
> There's absolutely no need to extend the lifetime of cell to post
> removal of the driver. It's only used in this function.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
Reviewed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
> drivers/net/wireless/ath/ath10k/core.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c
> index 5c5bd5ef7175..38c5f5f95433 100644
> --- a/drivers/net/wireless/ath/ath10k/core.c
> +++ b/drivers/net/wireless/ath/ath10k/core.c
> @@ -1981,13 +1981,14 @@ static int ath10k_download_cal_nvmem(struct ath10k *ar, const char *cell_name)
> size_t len;
> int ret;
>
> - cell = devm_nvmem_cell_get(ar->dev, cell_name);
> + cell = nvmem_cell_get(ar->dev, cell_name);
> if (IS_ERR(cell)) {
> ret = PTR_ERR(cell);
> return ret;
> }
>
> buf = nvmem_cell_read(cell, &len);
> + nvmem_cell_put(cell);
> if (IS_ERR(buf))
> return PTR_ERR(buf);
>
> --
> 2.53.0
>
>
^ permalink raw reply
* Re: [PATCH 1/5] wifi: mt76: mt7921: refactor regulatory domain handling to regd.[ch]
From: Sean Wang @ 2026-02-23 20:38 UTC (permalink / raw)
To: JB Tsai
Cc: nbd, lorenzo, linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang,
Quan.Zhou, Ryder.Lee, Leon.Yen, litien.chang
In-Reply-To: <20260223073854.2464232-1-jb.tsai@mediatek.com>
Hi JB,
Thanks for the patch just a small nit:
On Mon, Feb 23, 2026 at 1:40 AM JB Tsai <jb.tsai@mediatek.com> wrote:
>
> Move regd logic to regd.c and regd.h files
>
> Signed-off-by: JB Tsai <jb.tsai@mediatek.com>
> ---
> .../wireless/mediatek/mt76/mt7921/Makefile | 2 +-
> .../net/wireless/mediatek/mt76/mt7921/init.c | 98 +----------------
> .../net/wireless/mediatek/mt76/mt7921/main.c | 1 +
> .../wireless/mediatek/mt76/mt7921/mt7921.h | 1 -
> .../net/wireless/mediatek/mt76/mt7921/pci.c | 1 +
> .../net/wireless/mediatek/mt76/mt7921/regd.c | 104 ++++++++++++++++++
> .../net/wireless/mediatek/mt76/mt7921/regd.h | 13 +++
> 7 files changed, 121 insertions(+), 99 deletions(-)
> create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> create mode 100644 drivers/net/wireless/mediatek/mt76/mt7921/regd.h
>
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> index 2ad3c1cc3779..3ef7c9c45386 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/Makefile
> @@ -5,7 +5,7 @@ obj-$(CONFIG_MT7921E) += mt7921e.o
> obj-$(CONFIG_MT7921S) += mt7921s.o
> obj-$(CONFIG_MT7921U) += mt7921u.o
>
> -mt7921-common-y := mac.o mcu.o main.o init.o debugfs.o
> +mt7921-common-y := mac.o mcu.o regd.o main.o init.o debugfs.o
> mt7921-common-$(CONFIG_NL80211_TESTMODE) += testmode.o
> mt7921e-y := pci.o pci_mac.o pci_mcu.o
> mt7921s-y := sdio.o sdio_mac.o sdio_mcu.o
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/init.c b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> index 29732315af1c..1fe2f2bc3881 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/init.c
> @@ -9,6 +9,7 @@
> #include "mt7921.h"
> #include "../mt76_connac2_mac.h"
> #include "mcu.h"
> +#include "regd.h"
>
> static ssize_t mt7921_thermal_temp_show(struct device *dev,
> struct device_attribute *attr,
> @@ -60,103 +61,6 @@ static int mt7921_thermal_init(struct mt792x_phy *phy)
> return PTR_ERR_OR_ZERO(hwmon);
> }
>
> -static void
> -mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> -{
> -#define IS_UNII_INVALID(idx, sfreq, efreq) \
> - (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
> - struct ieee80211_supported_band *sband;
> - struct mt76_dev *mdev = &dev->mt76;
> - struct device_node *np, *band_np;
> - struct ieee80211_channel *ch;
> - int i, cfreq;
> -
> - np = mt76_find_power_limits_node(mdev);
> -
> - sband = wiphy->bands[NL80211_BAND_5GHZ];
> - band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
> - for (i = 0; i < sband->n_channels; i++) {
> - ch = &sband->channels[i];
> - cfreq = ch->center_freq;
> -
> - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> - ch->flags |= IEEE80211_CHAN_DISABLED;
> - continue;
> - }
> -
> - /* UNII-4 */
> - if (IS_UNII_INVALID(0, 5845, 5925))
> - ch->flags |= IEEE80211_CHAN_DISABLED;
> - }
> -
> - sband = wiphy->bands[NL80211_BAND_6GHZ];
> - if (!sband)
> - return;
> -
> - band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
> - for (i = 0; i < sband->n_channels; i++) {
> - ch = &sband->channels[i];
> - cfreq = ch->center_freq;
> -
> - if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> - ch->flags |= IEEE80211_CHAN_DISABLED;
> - continue;
> - }
> -
> - /* UNII-5/6/7/8 */
> - if (IS_UNII_INVALID(1, 5925, 6425) ||
> - IS_UNII_INVALID(2, 6425, 6525) ||
> - IS_UNII_INVALID(3, 6525, 6875) ||
> - IS_UNII_INVALID(4, 6875, 7125))
> - ch->flags |= IEEE80211_CHAN_DISABLED;
> - }
> -}
> -
> -void mt7921_regd_update(struct mt792x_dev *dev)
> -{
> - struct mt76_dev *mdev = &dev->mt76;
> - struct ieee80211_hw *hw = mdev->hw;
> - struct wiphy *wiphy = hw->wiphy;
> -
> - mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
> - mt7921_regd_channel_update(wiphy, dev);
> - mt76_connac_mcu_set_channel_domain(hw->priv);
> - mt7921_set_tx_sar_pwr(hw, NULL);
> -}
> -EXPORT_SYMBOL_GPL(mt7921_regd_update);
> -
> -static void
> -mt7921_regd_notifier(struct wiphy *wiphy,
> - struct regulatory_request *request)
> -{
> - struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> - struct mt792x_dev *dev = mt792x_hw_dev(hw);
> - struct mt76_connac_pm *pm = &dev->pm;
> -
> - memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
> - dev->mt76.region = request->dfs_region;
> - dev->country_ie_env = request->country_ie_env;
> -
> - if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
> - if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
> - wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
> - else
> - wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
> - }
> -
> - if (pm->suspended)
> - return;
> -
> - dev->regd_in_progress = true;
> -
> - mt792x_mutex_acquire(dev);
> - mt7921_regd_update(dev);
> - mt792x_mutex_release(dev);
> -
> - dev->regd_in_progress = false;
> - wake_up(&dev->wait);
> -}
> -
> int mt7921_mac_init(struct mt792x_dev *dev)
> {
> int i;
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> index 42b9514e04e7..00ca3d3f3ef0 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c
> @@ -7,6 +7,7 @@
> #include <linux/module.h>
> #include <net/ipv6.h>
> #include "mt7921.h"
> +#include "regd.h"
> #include "mcu.h"
>
> static int
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> index ad92af98e314..5239ea970d24 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mt7921.h
> @@ -246,7 +246,6 @@ mt7921_l1_rmw(struct mt792x_dev *dev, u32 addr, u32 mask, u32 val)
> #define mt7921_l1_set(dev, addr, val) mt7921_l1_rmw(dev, addr, 0, val)
> #define mt7921_l1_clear(dev, addr, val) mt7921_l1_rmw(dev, addr, val, 0)
>
> -void mt7921_regd_update(struct mt792x_dev *dev);
> int mt7921_mac_init(struct mt792x_dev *dev);
> bool mt7921_mac_wtbl_update(struct mt792x_dev *dev, int idx, u32 mask);
> int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif,
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> index 65c7fe671137..a173a61f2b49 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c
> @@ -12,6 +12,7 @@
> #include "../mt76_connac2_mac.h"
> #include "../dma.h"
> #include "mcu.h"
> +#include "regd.h"
>
> static const struct pci_device_id mt7921_pci_device_table[] = {
> { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7961),
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.c b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> new file mode 100644
> index 000000000000..6e6c81189222
> --- /dev/null
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.c
> @@ -0,0 +1,104 @@
> +/* SPDX-License-Identifier: ISC */
Please align the SPDX identifier in the new files with mt76
(BSD-3-Clause-Clear).
> +/* Copyright (C) 2025 MediaTek Inc. */
> +
> +#include <linux/of_net.h>
> +#include <linux/of.h>
linux/of.h should be sufficient here; we can drop linux/of_net.h.
> +#include "mt7921.h"
> +#include "regd.h"
> +#include "mcu.h"
> +
> +static void
> +mt7921_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev)
> +{
> +#define IS_UNII_INVALID(idx, sfreq, efreq) \
> + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq))
> + struct ieee80211_supported_band *sband;
> + struct mt76_dev *mdev = &dev->mt76;
> + struct device_node *np, *band_np;
> + struct ieee80211_channel *ch;
> + int i, cfreq;
> +
> + np = mt76_find_power_limits_node(mdev);
> +
> + sband = wiphy->bands[NL80211_BAND_5GHZ];
> + band_np = np ? of_get_child_by_name(np, "txpower-5g") : NULL;
> + for (i = 0; i < sband->n_channels; i++) {
> + ch = &sband->channels[i];
> + cfreq = ch->center_freq;
> +
> + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> + ch->flags |= IEEE80211_CHAN_DISABLED;
> + continue;
> + }
> +
> + /* UNII-4 */
> + if (IS_UNII_INVALID(0, 5845, 5925))
> + ch->flags |= IEEE80211_CHAN_DISABLED;
> + }
> +
> + sband = wiphy->bands[NL80211_BAND_6GHZ];
> + if (!sband)
> + return;
> +
> + band_np = np ? of_get_child_by_name(np, "txpower-6g") : NULL;
> + for (i = 0; i < sband->n_channels; i++) {
> + ch = &sband->channels[i];
> + cfreq = ch->center_freq;
> +
> + if (np && (!band_np || !mt76_find_channel_node(band_np, ch))) {
> + ch->flags |= IEEE80211_CHAN_DISABLED;
> + continue;
> + }
> +
> + /* UNII-5/6/7/8 */
> + if (IS_UNII_INVALID(1, 5925, 6425) ||
> + IS_UNII_INVALID(2, 6425, 6525) ||
> + IS_UNII_INVALID(3, 6525, 6875) ||
> + IS_UNII_INVALID(4, 6875, 7125))
> + ch->flags |= IEEE80211_CHAN_DISABLED;
> + }
> +}
> +
> +void mt7921_regd_update(struct mt792x_dev *dev)
> +{
> + struct mt76_dev *mdev = &dev->mt76;
> + struct ieee80211_hw *hw = mdev->hw;
> + struct wiphy *wiphy = hw->wiphy;
> +
> + mt7921_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env);
> + mt7921_regd_channel_update(wiphy, dev);
> + mt76_connac_mcu_set_channel_domain(hw->priv);
> + mt7921_set_tx_sar_pwr(hw, NULL);
> +}
> +EXPORT_SYMBOL_GPL(mt7921_regd_update);
> +
> +void mt7921_regd_notifier(struct wiphy *wiphy,
> + struct regulatory_request *request)
> +{
> + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy);
> + struct mt792x_dev *dev = mt792x_hw_dev(hw);
> + struct mt76_connac_pm *pm = &dev->pm;
> +
> + memcpy(dev->mt76.alpha2, request->alpha2, sizeof(dev->mt76.alpha2));
> + dev->mt76.region = request->dfs_region;
> + dev->country_ie_env = request->country_ie_env;
> +
> + if (request->initiator == NL80211_REGDOM_SET_BY_USER) {
> + if (dev->mt76.alpha2[0] == '0' && dev->mt76.alpha2[1] == '0')
> + wiphy->regulatory_flags &= ~REGULATORY_COUNTRY_IE_IGNORE;
> + else
> + wiphy->regulatory_flags |= REGULATORY_COUNTRY_IE_IGNORE;
> + }
> +
> + if (pm->suspended)
> + return;
> +
> + dev->regd_in_progress = true;
> +
> + mt792x_mutex_acquire(dev);
> + mt7921_regd_update(dev);
> + mt792x_mutex_release(dev);
> +
> + dev->regd_in_progress = false;
> + wake_up(&dev->wait);
> +}
Looks good to me this is a straight move of the code with no extra
logic changes.
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/regd.h b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> new file mode 100644
> index 000000000000..0ba6161e1919
> --- /dev/null
> +++ b/drivers/net/wireless/mediatek/mt76/mt7921/regd.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: ISC */
Please align the SPDX identifier in the new files with mt76
(BSD-3-Clause-Clear).
> +/* Copyright (C) 2025 MediaTek Inc. */
> +
> +#ifndef __MT7921_REGD_H
> +#define __MT7921_REGD_H
> +
> +#include "mt7921.h"
> +
I guess regd.h shouldn’t rely on indirect includes for struct
regulatory_request; please forward-declare it or include the proper
header.
> +void mt7921_regd_update(struct mt792x_dev *dev);
> +void mt7921_regd_notifier(struct wiphy *wiphy,
> + struct regulatory_request *request);
> +
> +#endif
> --
> 2.45.2
>
>
^ permalink raw reply
* [BUG] iwlwifi oops in iwl_pcie_rx_handle on resume
From: Chris Bainbridge @ 2026-02-23 18:43 UTC (permalink / raw)
To: miriam.rachel.korenblit
Cc: johannes.berg, emmanuel.grumbach, linux-intel-wifi,
linux-wireless, linux-kernel
Hi,
I just got an oops with v7.0-rc1 shortly after resume:
<6>[ 9761.194306] ACPI: EC: interrupt unblocked
<6>[ 9761.336806] amdgpu 0000:03:00.0: [drm] PCIE GART of 1024M enabled.
<6>[ 9761.336816] amdgpu 0000:03:00.0: [drm] PTB located at 0x000000F41FC00000
<6>[ 9761.337043] amdgpu 0000:03:00.0: SMU is resuming...
<6>[ 9761.338126] amdgpu 0000:03:00.0: dpm has been disabled
<6>[ 9761.338943] amdgpu 0000:03:00.0: SMU is resumed successfully!
<6>[ 9761.439301] [drm] DM_MST: Differing MST start on aconnector: 000000001b6f89b3 [id: 116]
<6>[ 9761.441525] amdgpu 0000:03:00.0: ring gfx uses VM inv eng 0 on hub 0
<6>[ 9761.441530] amdgpu 0000:03:00.0: ring comp_1.0.0 uses VM inv eng 1 on hub 0
<6>[ 9761.441532] amdgpu 0000:03:00.0: ring comp_1.1.0 uses VM inv eng 4 on hub 0
<6>[ 9761.441534] amdgpu 0000:03:00.0: ring comp_1.2.0 uses VM inv eng 5 on hub 0
<6>[ 9761.441536] amdgpu 0000:03:00.0: ring comp_1.3.0 uses VM inv eng 6 on hub 0
Oops#1 Part6
<6>[ 9761.441538] amdgpu 0000:03:00.0: ring comp_1.0.1 uses VM inv eng 7 on hub 0
<6>[ 9761.441540] amdgpu 0000:03:00.0: ring comp_1.1.1 uses VM inv eng 8 on hub 0
<6>[ 9761.441542] amdgpu 0000:03:00.0: ring comp_1.2.1 uses VM inv eng 9 on hub 0
<6>[ 9761.441544] amdgpu 0000:03:00.0: ring comp_1.3.1 uses VM inv eng 10 on hub 0
<6>[ 9761.441546] amdgpu 0000:03:00.0: ring kiq_0.2.1.0 uses VM inv eng 11 on hub 0
<6>[ 9761.441548] amdgpu 0000:03:00.0: ring sdma0 uses VM inv eng 0 on hub 8
<6>[ 9761.441550] amdgpu 0000:03:00.0: ring vcn_dec uses VM inv eng 1 on hub 8
<6>[ 9761.441552] amdgpu 0000:03:00.0: ring vcn_enc0 uses VM inv eng 4 on hub 8
<6>[ 9761.441554] amdgpu 0000:03:00.0: ring vcn_enc1 uses VM inv eng 5 on hub 8
<6>[ 9761.441556] amdgpu 0000:03:00.0: ring jpeg_dec uses VM inv eng 6 on hub 8
<6>[ 9761.568907] usb 1-1.3.2.4: reset high-speed USB device number 12 using xhci_hcd
<6>[ 9761.767481] nvme nvme0: 8/0/0 default/read/poll queues
<6>[ 9761.922505] iwlwifi 0000:01:00.0: WFPM_UMAC_PD_NOTIFICATION: 0x20
<6>[ 9761.922592] iwlwifi 0000:01:00.0: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
<6>[ 9761.922715] iwlwifi 0000:01:00.0: WFPM_AUTH_KEY_0: 0x90
<6>[ 9761.922787] iwlwifi 0000:01:00.0: CNVI_SCU_SEQ_DATA_DW9: 0x0
<6>[ 9762.052938] OOM killer enabled.
<6>[ 9762.052942] Restarting tasks: Starting
<6>[ 9762.054800] Restarting tasks: Done
<6>[ 9762.054976] efivarfs: resyncing variable state
<6>[ 9762.066314] efivarfs: finished resyncing variable state
<5>[ 9762.066477] random: crng reseeded on system resumption
<6>[ 9762.160714] PM: suspend exit
<6>[ 9762.739294] iwlwifi 0000:01:00.0: WFPM_UMAC_PD_NOTIFICATION: 0x20
Oops#1 Part5
<6>[ 9762.739367] iwlwifi 0000:01:00.0: WFPM_LMAC2_PD_NOTIFICATION: 0x1f
<6>[ 9762.739435] iwlwifi 0000:01:00.0: WFPM_AUTH_KEY_0: 0x90
<6>[ 9762.739451] iwlwifi 0000:01:00.0: CNVI_SCU_SEQ_DATA_DW9: 0x0
<6>[ 9766.455338] wlp1s0: authenticate with d6:92:5e:eb:ee:15 (local address=c8:15:4e:63:1d:e8)
<6>[ 9766.456638] wlp1s0: send auth to d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.518885] wlp1s0: authenticate with d6:92:5e:eb:ee:15 (local address=c8:15:4e:63:1d:e8)
<6>[ 9766.518900] wlp1s0: send auth to d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.522553] wlp1s0: authenticated
<6>[ 9766.524038] wlp1s0: associate with d6:92:5e:eb:ee:15 (try 1/3)
<6>[ 9766.532772] wlp1s0: RX AssocResp from d6:92:5e:eb:ee:15 (capab=0x1011 status=0 aid=21)
<6>[ 9766.549780] wlp1s0: associated
<7>[ 9766.618488] wlp1s0: Limiting TX power to 23 (23 - 0) dBm as advertised by d6:92:5e:eb:ee:15
<4>[ 9809.544787] Oops: general protection fault, probably for non-canonical address 0xe0000d1060000000: 0000 [#1] SMP KASAN
<1>[ 9809.544818] KASAN: maybe wild-memory-access in range [0x0000888300000000-0x0000888300000007]
<4>[ 9809.544825] CPU: 3 UID: 0 PID: 996 Comm: irq/75-iwlwifi: Not tainted 7.0.0-rc1 #444 PREEMPT(lazy)
<4>[ 9809.544832] Hardware name: HP HP Pavilion Aero Laptop 13-be0xxx/8916, BIOS F.17 12/18/2024
<4>[ 9809.544835] RIP: 0010:detach_if_pending (./include/linux/list.h:992 (discriminator 2) kernel/time/timer.c:891 (discriminator 2) kernel/time/timer.c:910 (discriminator 2))
<4>[ 9809.544845] Code: df 4c 89 ea 48 8b 2b 48 c1 ea 03 80 3c 02 00 0f 85 40 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 6b 08 4c 89 ea 48 c1 ea 03 <80> 3c 02 00 0f 85 14 02 00 00 49 89 6d 00 48 85 ed 74 23 48 b8 00
All code
========
0: df 4c 89 ea fisttps -0x16(%rcx,%rcx,4)
4: 48 8b 2b mov (%rbx),%rbp
7: 48 c1 ea 03 shr $0x3,%rdx
b: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
f: 0f 85 40 02 00 00 jne 0x255
15: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
1c: fc ff df
1f: 4c 8b 6b 08 mov 0x8(%rbx),%r13
23: 4c 89 ea mov %r13,%rdx
26: 48 c1 ea 03 shr $0x3,%rdx
2a:* 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping instruction
2e: 0f 85 14 02 00 00 jne 0x248
34: 49 89 6d 00 mov %rbp,0x0(%r13)
38: 48 85 ed test %rbp,%rbp
3b: 74 23 je 0x60
3d: 48 rex.W
3e: b8 .byte 0xb8
...
Code starting with the faulting instruction
===========================================
0: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
4: 0f 85 14 02 00 00 jne 0x21e
a: 49 89 6d 00 mov %rbp,0x0(%r13)
e: 48 85 ed test %rbp,%rbp
11: 74 23 je 0x36
13: 48 rex.W
14: b8 .byte 0xb8
...
<4>[ 9809.544849] RSP: 0018:ffffc90000708850 EFLAGS: 00010016
Oops#1 Part4
<4>[ 9809.544855] RAX: dffffc0000000000 RBX: ffff888107e710e0 RCX: ffffffff86373dde
<4>[ 9809.544859] RDX: 0000111060000000 RSI: ffffffff888b8ec0 RDI: ffffffff88f25138
<4>[ 9809.544862] RBP: dead000000000122 R08: 0000000000000000 R09: fffffbfff139f90c
<4>[ 9809.544865] R10: ffffffff89cfc867 R11: 0000000000000001 R12: 0000000000000000
<4>[ 9809.544868] R13: 0000888300000000 R14: ffff888107e710e8 R15: ffff88838d6af9c0
<4>[ 9809.544872] FS: 0000000000000000(0000) GS:ffff888403005000(0000) knlGS:0000000000000000
<4>[ 9809.544875] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[ 9809.544878] CR2: 00007fad9435741c CR3: 0000000076a91000 CR4: 0000000000750ef0
<4>[ 9809.544882] PKRU: 55555554
<4>[ 9809.544885] Call Trace:
<4>[ 9809.544888] <IRQ>
<4>[ 9809.544893] __mod_timer (kernel/time/timer.c:1097)
<4>[ 9809.544899] ? __get_next_timer_interrupt (kernel/time/timer.c:1019)
<4>[ 9809.544904] ? _raw_spin_unlock_irqrestore (./arch/x86/include/asm/paravirt.h:529 ./arch/x86/include/asm/irqflags.h:159 ./include/linux/spinlock_api_smp.h:178 kernel/locking/spinlock.c:194)
<4>[ 9809.544909] ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4472 (discriminator 4))
<4>[ 9809.544914] ? iommu_dma_free_iova (drivers/iommu/dma-iommu.c:245 drivers/iommu/dma-iommu.c:804)
<4>[ 9809.544921] __iommu_dma_unmap (drivers/iommu/dma-iommu.c:814)
<4>[ 9809.544927] ? iommu_get_msi_cookie (drivers/iommu/dma-iommu.c:814)
<4>[ 9809.544932] ? __lock_acquire (kernel/locking/lockdep.c:4674 (discriminator 1) kernel/locking/lockdep.c:5191 (discriminator 1))
<4>[ 9809.544941] iommu_dma_unmap_phys (./include/linux/swiotlb.h:145 ./include/linux/swiotlb.h:252 drivers/iommu/dma-iommu.c:1250 drivers/iommu/dma-iommu.c:1231)
<4>[ 9809.544947] ? do_raw_spin_lock (./arch/x86/include/asm/atomic.h:107 (discriminator 1) ./include/linux/atomic/atomic-arch-fallback.h:2170 (discriminator 1) ./include/linux/atomic/atomic-instrumented.h:1302 (discriminator 1) ./include/asm-generic/qspinlock.h:111 (discriminator 1) kernel/locking/spinlock_debug.c:116 (discriminator 1))
<4>[ 9809.544952] dma_unmap_phys (kernel/dma/mapping.c:212)
<4>[ 9809.544960] iwl_pcie_rx_handle (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1311 drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1565) iwlwifi
<4>[ 9809.544980] ? kasan_save_stack (mm/kasan/common.c:59)
<4>[ 9809.544985] ? kasan_save_stack (mm/kasan/common.c:58)
<4>[ 9809.544990] ? __kasan_slab_free (mm/kasan/common.c:287)
<4>[ 9809.544994] ? kmem_cache_free (mm/slub.c:6124 (discriminator 3) mm/slub.c:6254 (discriminator 3))
<4>[ 9809.544999] ? handle_softirqs (./arch/x86/include/asm/jump_label.h:37 ./include/trace/events/irq.h:142 kernel/softirq.c:623)
<4>[ 9809.545003] ? __irq_exit_rcu (kernel/softirq.c:657 kernel/softirq.c:496 kernel/softirq.c:723)
Oops#1 Part3
<4>[ 9809.545007] ? irq_exit_rcu (kernel/softirq.c:741)
<4>[ 9809.545012] ? cpu_startup_entry (kernel/sched/idle.c:429)
<4>[ 9809.545017] ? start_secondary (arch/x86/kernel/smpboot.c:200 (discriminator 10) arch/x86/kernel/smpboot.c:280 (discriminator 10))
<4>[ 9809.545025] ? iwl_pcie_rxq_alloc_rbs (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1499) iwlwifi
<4>[ 9809.545043] iwl_pcie_napi_poll_msix (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1042) iwlwifi
<4>[ 9809.545058] __napi_poll.constprop.0 (net/core/dev.c:7684)
<4>[ 9809.545065] net_rx_action (net/core/dev.c:7749 net/core/dev.c:7899)
<4>[ 9809.545072] ? run_backlog_napi (net/core/dev.c:7861)
<4>[ 9809.545079] ? rcu_momentary_eqs (kernel/rcu/tree.c:2541)
<4>[ 9809.545086] ? mark_held_locks (kernel/locking/lockdep.c:4325 (discriminator 1))
<4>[ 9809.545091] ? handle_softirqs (./arch/x86/include/asm/paravirt.h:529 kernel/softirq.c:606)
<4>[ 9809.545096] handle_softirqs (./arch/x86/include/asm/jump_label.h:37 ./include/trace/events/irq.h:142 kernel/softirq.c:623)
<4>[ 9809.545103] ? tasklet_unlock_wait (kernel/softirq.c:580)
<4>[ 9809.545108] ? tasklet_unlock_wait (kernel/softirq.c:580)
<4>[ 9809.545112] ? tick_nohz_stop_idle (./include/linux/seqlock.h:453 ./include/linux/seqlock.h:525 kernel/time/tick-sched.c:771)
<4>[ 9809.545119] ? iwl_pcie_irq_rx_msix_handler (./include/linux/bottom_half.h:33 (discriminator 1) drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1666 (discriminator 1)) iwlwifi
<4>[ 9809.545134] ? iwl_pcie_irq_rx_msix_handler (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1660 (discriminator 1)) iwlwifi
<4>[ 9809.545148] do_softirq.part.0 (kernel/softirq.c:523 (discriminator 20))
<4>[ 9809.545153] </IRQ>
<4>[ 9809.545156] <TASK>
<4>[ 9809.545158] __local_bh_enable_ip (kernel/softirq.c:515 (discriminator 1) kernel/softirq.c:450 (discriminator 1))
<4>[ 9809.545163] ? iwl_pcie_irq_rx_msix_handler (./include/linux/bottom_half.h:33 (discriminator 1) drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1666 (discriminator 1)) iwlwifi
<4>[ 9809.545177] iwl_pcie_irq_rx_msix_handler (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1668 (discriminator 1)) iwlwifi
<4>[ 9809.545192] ? iwl_pcie_rx_free (drivers/net/wireless/intel/iwlwifi/pcie/gen1_2/rx.c:1640) iwlwifi
<4>[ 9809.545206] ? irq_thread (kernel/irq/manage.c:1058 (discriminator 3) kernel/irq/manage.c:1268 (discriminator 3))
<4>[ 9809.545211] irq_thread_fn (kernel/irq/manage.c:1143)
<4>[ 9809.545216] irq_thread (kernel/irq/manage.c:1272)
<4>[ 9809.545220] ? find_held_lock (kernel/locking/lockdep.c:5350 (discriminator 1))
<4>[ 9809.545225] ? __kthread_parkme (./include/linux/instrumented.h:82 ./include/asm-generic/bitops/instrumented-non-atomic.h:141 kernel/kthread.c:290)
<4>[ 9809.545230] ? irq_copy_pending.isra.0 (kernel/irq/manage.c:1142)
<4>[ 9809.545235] ? irq_forced_thread_fn (kernel/irq/manage.c:1245)
<4>[ 9809.545239] ? _raw_spin_unlock_irqrestore (./arch/x86/include/asm/paravirt.h:529 ./arch/x86/include/asm/irqflags.h:159 ./include/linux/spinlock_api_smp.h:178 kernel/locking/spinlock.c:194)
Oops#1 Part2
<4>[ 9809.545243] ? lockdep_hardirqs_on (kernel/locking/lockdep.c:4472 (discriminator 4))
<4>[ 9809.545247] ? irq_has_action (kernel/irq/manage.c:1179)
<4>[ 9809.545252] ? __kthread_parkme (./arch/x86/include/asm/bitops.h:202 ./arch/x86/include/asm/bitops.h:232 ./include/asm-generic/bitops/instrumented-non-atomic.h:142 kernel/kthread.c:290)
<4>[ 9809.545263] ? irq_forced_thread_fn (kernel/irq/manage.c:1245)
<4>[ 9809.545270] kthread (kernel/kthread.c:467)
<4>[ 9809.545277] ? _raw_spin_unlock_irq (./arch/x86/include/asm/paravirt.h:529 ./include/linux/spinlock_api_smp.h:187 kernel/locking/spinlock.c:202)
<4>[ 9809.545284] ? kthread_affine_node (kernel/kthread.c:412)
<4>[ 9809.545292] ret_from_fork (arch/x86/kernel/process.c:164)
<4>[ 9809.545302] ? exit_thread (arch/x86/kernel/process.c:153)
<4>[ 9809.545312] ? __switch_to (./arch/x86/include/asm/cpufeature.h:101 arch/x86/kernel/process_64.c:377 arch/x86/kernel/process_64.c:665)
<4>[ 9809.545320] ? kthread_affine_node (kernel/kthread.c:412)
<4>[ 9809.545330] ret_from_fork_asm (arch/x86/entry/entry_64.S:255)
<4>[ 9809.545351] </TASK>
<4>[ 9809.545357] Modules linked in: snd_seq_dummy snd_hrtimer snd_seq xt_conntrack nft_chain_nat xt_MASQUERADE nf_nat nf_conntrack_netlink nf_conntrack nf_defrag_ipv6 nf_defrag_ipv4 xfrm_user xfrm_algo xt_addrtype nft_compat x_tables nf_tables br_netfilter bridge stp llc ccm overlay qrtr rfcomm cmac algif_hash algif_skcipher af_alg bnep binfmt_misc ext4 mbcache jbd2 nls_ascii nls_cp437 vfat fat snd_acp3x_rn snd_acp3x_pdm_dma snd_soc_dmic intel_rapl_msr snd_soc_core snd_compress snd_hda_codec_generic amd_atl intel_rapl_common iwlmvm uvcvideo videobuf2_vmalloc btusb mac80211 snd_usb_audio snd_hda_codec_hdmi videobuf2_memops kvm_amd libarc4 btrtl uvc snd_usbmidi_lib snd_pci_acp6x btintel videobuf2_v4l2 snd_rawmidi btbcm videodev kvm snd_seq_device btmtk snd_hda_intel irqbypass snd_pci_acp5x videobuf2_common snd_hda_codec bluetooth snd_intel_dspcfg rapl mc snd_hwdep ecdh_generic ecc wmi_bmof snd_hda_core snd_rn_pci_acp3x iwlwifi snd_pcm snd_acp_config pcspkr ee1004 k10temp snd_soc_acpi snd_timer ac cfg80211 battery snd
Oops#1 Part1
<4>[ 9809.545588] snd_pci_acp3x rfkill soundcore ccp joydev button amd_pmc sg acpi_tad evdev msr parport_pc ppdev lp parport efi_pstore nvme_fabrics fuse configfs nfnetlink efivarfs autofs4 btrfs xor libblake2b raid6_pq dm_crypt dm_mod r8153_ecm cdc_ether usbnet sd_mod hid_microsoft ff_memless hid_cmedia uas r8152 usb_storage mii scsi_mod libphy mdio_bus usbhid scsi_common amdgpu drm_client_lib i2c_algo_bit drm_ttm_helper ttm drm_exec drm_suballoc_helper drm_buddy drm_panel_backlight_quirks gpu_sched amdxcp hid_multitouch drm_display_helper sp5100_tco ucsi_acpi hid_generic drm_kms_helper xhci_pci watchdog video typec_ucsi cec i2c_hid_acpi roles i2c_piix4 xhci_hcd ghash_clmulni_intel nvme rc_core serio_raw i2c_hid amd_sfh typec i2c_smbus usbcore crc16 nvme_core hid thunderbolt fan usb_common wmi drm aesni_intel
<4>[ 9809.545854] ---[ end trace 0000000000000000 ]---
<4>[ 9809.764012] RIP: 0010:detach_if_pending (./include/linux/list.h:992 (discriminator 2) kernel/time/timer.c:891 (discriminator 2) kernel/time/timer.c:910 (discriminator 2))
<4>[ 9809.764021] Code: df 4c 89 ea 48 8b 2b 48 c1 ea 03 80 3c 02 00 0f 85 40 02 00 00 48 b8 00 00 00 00 00 fc ff df 4c 8b 6b 08 4c 89 ea 48 c1 ea 03 <80> 3c 02 00 0f 85 14 02 00 00 49 89 6d 00 48 85 ed 74 23 48 b8 00
All code
========
0: df 4c 89 ea fisttps -0x16(%rcx,%rcx,4)
4: 48 8b 2b mov (%rbx),%rbp
7: 48 c1 ea 03 shr $0x3,%rdx
b: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
f: 0f 85 40 02 00 00 jne 0x255
15: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
1c: fc ff df
1f: 4c 8b 6b 08 mov 0x8(%rbx),%r13
23: 4c 89 ea mov %r13,%rdx
26: 48 c1 ea 03 shr $0x3,%rdx
2a:* 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1) <-- trapping instruction
2e: 0f 85 14 02 00 00 jne 0x248
34: 49 89 6d 00 mov %rbp,0x0(%r13)
38: 48 85 ed test %rbp,%rbp
3b: 74 23 je 0x60
3d: 48 rex.W
3e: b8 .byte 0xb8
...
Code starting with the faulting instruction
===========================================
0: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
4: 0f 85 14 02 00 00 jne 0x21e
a: 49 89 6d 00 mov %rbp,0x0(%r13)
e: 48 85 ed test %rbp,%rbp
11: 74 23 je 0x36
13: 48 rex.W
14: b8 .byte 0xb8
...
<4>[ 9809.764025] RSP: 0018:ffffc90000708850 EFLAGS: 00010016
<4>[ 9809.764031] RAX: dffffc0000000000 RBX: ffff888107e710e0 RCX: ffffffff86373dde
<4>[ 9809.764033] RDX: 0000111060000000 RSI: ffffffff888b8ec0 RDI: ffffffff88f25138
<4>[ 9809.764036] RBP: dead000000000122 R08: 0000000000000000 R09: fffffbfff139f90c
<4>[ 9809.764038] R10: ffffffff89cfc867 R11: 0000000000000001 R12: 0000000000000000
<4>[ 9809.764040] R13: 0000888300000000 R14: ffff888107e710e8 R15: ffff88838d6af9c0
<4>[ 9809.764043] FS: 0000000000000000(0000) GS:ffff888403005000(0000) knlGS:0000000000000000
<4>[ 9809.764046] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
<4>[ 9809.764048] CR2: 00007fad9435741c CR3: 0000000076a91000 CR4: 0000000000750ef0
<4>[ 9809.764051] PKRU: 55555554
<0>[ 9809.764054] Kernel panic - not syncing: Fatal exception in interrupt
<0>[ 9811.126715] Shutting down cpus with NMI
<0>[ 9811.126752] Kernel Offset: 0x4c00000 from 0xffffffff81000000 (relocation range: 0xffffffff80000000-0xffffffffbfffffff)
This could possibly be related to a similar oops I saw last week, in
that case the stack trace didn't point to iwlwifi, but the oops appeared
to be the result of memory corruption:
https://lkml.org/lkml/2026/2/18/486
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath9k: use non devm for nvmem_cell_get
From: Toke Høiland-Jørgensen @ 2026-02-23 15:21 UTC (permalink / raw)
To: Rosen Penev, linux-wireless; +Cc: open list
In-Reply-To: <20260223025021.19008-1-rosenp@gmail.com>
Rosen Penev <rosenp@gmail.com> writes:
> There's absolutely no need to extend the lifetime of cell to post
> removal of the driver. It's only used in this function.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
Right, makes sense.
Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>
^ permalink raw reply
* Re: [PATCH v1] wifi: ath9k: Fix typo
From: Toke Høiland-Jørgensen @ 2026-02-23 15:17 UTC (permalink / raw)
To: Alejandro Colomar, linux-wireless
Cc: Alejandro Colomar, Rajkumar Manoharan, John W. Linville
In-Reply-To: <6ab107cf786f9d05dc4d84ea4e2d1b219ce108c0.1766355822.git.alx@kernel.org>
Alejandro Colomar <alx@kernel.org> writes:
> This only worked by chance, because all callers of this macro used the
> same identifiers that were expected by the macro.
>
> $ grep -rn ath_for_each_chanctx
> drivers/net/wireless/ath/ath9k/main.c:1576: ath_for_each_chanctx(sc, ctx)
> drivers/net/wireless/ath/ath9k/main.c:2554: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:165: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:291: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/channel.c:861: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/debug.c:717: ath_for_each_chanctx(sc, ctx) {
> drivers/net/wireless/ath/ath9k/ath9k.h:446:#define ath_for_each_chanctx(_sc, _ctx) \
>
> Fixes: c4dc0d040e35 (2014-06-19; "ath9k: Fetch appropriate operating channel context")
> Cc: Rajkumar Manoharan <rmanohar@qti.qualcomm.com>
> Cc: John W. Linville <linville@tuxdriver.com>
> Cc: Toke Høiland-Jørgensen <toke@toke.dk>
> Signed-off-by: Alejandro Colomar <alx@kernel.org>
Yeah, looks reasonable - thanks!
Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>
^ permalink raw reply
* Re: [PATCH ath-next] wifi: ath9k: simplify eeprom format strings
From: Toke Høiland-Jørgensen @ 2026-02-23 15:05 UTC (permalink / raw)
To: Rosen Penev, linux-wireless
Cc: Andreas Färber, Manivannan Sadhasivam, open list,
moderated list:ARM/ACTIONS SEMI ARCHITECTURE,
moderated list:ARM/ACTIONS SEMI ARCHITECTURE
In-Reply-To: <20250821033908.638871-1-rosenp@gmail.com>
Rosen Penev <rosenp@gmail.com> writes:
> devm is already used here so might as well simplify the whole function
> with devm_kasprintf.
>
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
(sorry for not getting around to replying to this before)
> ---
> drivers/net/wireless/ath/ath9k/ath9k.h | 2 +-
> drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c | 11 +----------
> drivers/net/wireless/ath/ath9k/init.c | 7 +++----
> drivers/net/wireless/ath/ath9k/rng.c | 4 ++--
> 4 files changed, 7 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
> index 6e38aa7351e3..60aac2c50409 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k.h
> +++ b/drivers/net/wireless/ath/ath9k/ath9k.h
> @@ -1076,7 +1076,7 @@ struct ath_softc {
> #ifdef CONFIG_ATH9K_HWRNG
> struct hwrng rng_ops;
> u32 rng_last;
> - char rng_name[sizeof("ath9k_65535")];
> + const char *rng_name;
Changing this fixed buffer to a devm-managed pointer makes no sense: it
doesn't help with any lifetime issues, and just adds overhead.
> #endif
> };
>
> diff --git a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> index fe1013a3a588..c4f8d1f98369 100644
> --- a/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> +++ b/drivers/net/wireless/ath/ath9k/ath9k_pci_owl_loader.c
> @@ -140,19 +140,10 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
> static const char *owl_get_eeprom_name(struct pci_dev *pdev)
> {
> struct device *dev = &pdev->dev;
> - char *eeprom_name;
>
> dev_dbg(dev, "using auto-generated eeprom filename\n");
>
> - eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
> - if (!eeprom_name)
> - return NULL;
> -
> - /* this should match the pattern used in ath9k/init.c */
> - scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
> - dev_name(dev));
> -
> - return eeprom_name;
> + return devm_kasprintf(dev, GFP_KERNEL, "ath9k-eeprom-pci-%s.bin", dev_name(dev));
This change sort of makes sense, given that devm is already used. But
really, it shouldn't be; the name is only used to pass it to
request_firmware_nowait(), so the devm management is totally
superfluous. Better to change it to an on-stack buffer like the other
call instead (maybe just by getting rid of the owl_get_eeprom_name()
helper entirely, and moving the scnprintf() into the caller).
> }
>
> static void owl_nvmem_work(struct work_struct *work)
> diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
> index ee951493e993..04903b5c611f 100644
> --- a/drivers/net/wireless/ath/ath9k/init.c
> +++ b/drivers/net/wireless/ath/ath9k/init.c
> @@ -625,7 +625,7 @@ static int ath9k_of_init(struct ath_softc *sc)
> struct ath_hw *ah = sc->sc_ah;
> struct ath_common *common = ath9k_hw_common(ah);
> enum ath_bus_type bus_type = common->bus_ops->ath_bus_type;
> - char eeprom_name[100];
> + const char *eeprom_name;
Changing an on-stack buffer to a devm-managed one also doesn't make any
sense.
-Toke
^ permalink raw reply
* [PATCH ath-next v4] wifi: ath12k: add basic hwmon temperature reporting
From: Maharaja Kennadyrajan @ 2026-02-23 13:26 UTC (permalink / raw)
To: ath12k; +Cc: linux-wireless, Maharaja Kennadyrajan, Aishwarya R
Add initial thermal support by wiring up a per-radio (pdev) hwmon temperature
sensor backed by the existing WMI pdev temperature command and event.
When userspace reads the sysfs file temp1_input, the driver sends
WMI_PDEV_GET_TEMPERATURE_CMDID (tag WMI_TAG_PDEV_GET_TEMPERATURE_CMD) and waits
for the corresponding WMI_PDEV_TEMPERATURE_EVENTID
(tag WMI_TAG_PDEV_TEMPERATURE_EVENT) to get the temperature and pdev_id.
Export the reported value in millidegrees Celsius as required by hwmon.
The temperature reported is per-radio (pdev). In a multi-radio wiphy under a
single phy, a separate hwmon device is created for each radio.
Sample command and output:
$ cat /sys/devices/pci0000:00/.../ieee80211/phyX/hwmonY/temp1_input
$ 50000
Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.6-01243-QCAHKSWPL_SILICONZ-1
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202602160145.YQdvbqYY-lkp@intel.com/
Co-developed-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Aishwarya R <aishwarya.r@oss.qualcomm.com>
Signed-off-by: Maharaja Kennadyrajan <maharaja.kennadyrajan@oss.qualcomm.com>
---
v4: Addressed Nicolas comment on extra mutex_lock removal.
v3: Addressed the Pablo's comment on looping the ab->pdevs in the function
ath12k_thermal_register() while handling the failure case.
Renamed mutex_lock to generic name from thermal.read_lock to
thermal.lock.
v2: Fixed the kernel test robot reported build test error.
drivers/net/wireless/ath/ath12k/Makefile | 1 +
drivers/net/wireless/ath/ath12k/core.c | 13 +++
drivers/net/wireless/ath/ath12k/core.h | 3 +
drivers/net/wireless/ath/ath12k/mac.c | 4 +
drivers/net/wireless/ath/ath12k/thermal.c | 124 ++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/thermal.h | 40 +++++++
drivers/net/wireless/ath/ath12k/wmi.c | 57 +++++-----
7 files changed, 211 insertions(+), 31 deletions(-)
create mode 100644 drivers/net/wireless/ath/ath12k/thermal.c
create mode 100644 drivers/net/wireless/ath/ath12k/thermal.h
diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile
index 3ba1236956cc..3b39b2c33307 100644
--- a/drivers/net/wireless/ath/ath12k/Makefile
+++ b/drivers/net/wireless/ath/ath12k/Makefile
@@ -32,6 +32,7 @@ ath12k-$(CONFIG_ATH12K_TRACING) += trace.o
ath12k-$(CONFIG_PM) += wow.o
ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o
ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o
+ath12k-$(CONFIG_THERMAL) += thermal.o
# for tracing framework to find trace.h
CFLAGS_trace.o := -I$(src)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 9d6c50a94e64..9dca1a0af73e 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -863,11 +863,22 @@ static int ath12k_core_pdev_create(struct ath12k_base *ab)
return ret;
}
+ ret = ath12k_thermal_register(ab);
+ if (ret) {
+ ath12k_err(ab, "could not register thermal device: %d\n", ret);
+ goto err_dp_pdev_free;
+ }
+
return 0;
+
+err_dp_pdev_free:
+ ath12k_dp_pdev_free(ab);
+ return ret;
}
static void ath12k_core_pdev_destroy(struct ath12k_base *ab)
{
+ ath12k_thermal_unregister(ab);
ath12k_dp_pdev_free(ab);
}
@@ -1361,6 +1372,7 @@ static int ath12k_core_reconfigure_on_crash(struct ath12k_base *ab)
mutex_lock(&ab->core_lock);
ath12k_link_sta_rhash_tbl_destroy(ab);
+ ath12k_thermal_unregister(ab);
ath12k_dp_pdev_free(ab);
ath12k_ce_cleanup_pipes(ab);
ath12k_wmi_detach(ab);
@@ -1502,6 +1514,7 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
complete(&ar->vdev_delete_done);
complete(&ar->bss_survey_done);
complete_all(&ar->regd_update_completed);
+ complete_all(&ar->thermal.wmi_sync);
wake_up(&ar->dp.tx_empty_waitq);
idr_for_each(&ar->txmgmt_idr,
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 990934ec92fc..760c76d6f0f4 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -36,6 +36,7 @@
#include "coredump.h"
#include "cmn_defs.h"
#include "dp_cmn.h"
+#include "thermal.h"
#define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK)
@@ -757,6 +758,8 @@ struct ath12k {
s8 max_allowed_tx_power;
struct ath12k_pdev_rssi_offsets rssi_info;
+
+ struct ath12k_thermal thermal;
};
struct ath12k_hw {
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 68431a0e128e..b9091f3f723f 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -14792,6 +14792,10 @@ static void ath12k_mac_setup(struct ath12k *ar)
init_completion(&ar->mlo_setup_done);
init_completion(&ar->completed_11d_scan);
init_completion(&ar->regd_update_completed);
+ init_completion(&ar->thermal.wmi_sync);
+
+ ar->thermal.temperature = 0;
+ ar->thermal.hwmon_dev = NULL;
INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work);
wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work);
diff --git a/drivers/net/wireless/ath/ath12k/thermal.c b/drivers/net/wireless/ath/ath12k/thermal.c
new file mode 100644
index 000000000000..a764d2112a3c
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.c
@@ -0,0 +1,124 @@
+// SPDX-License-Identifier: BSD-3-Clause-Clear
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#include <linux/device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/sysfs.h>
+#include <linux/thermal.h>
+#include "core.h"
+#include "debug.h"
+
+static ssize_t ath12k_thermal_temp_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ath12k *ar = dev_get_drvdata(dev);
+ unsigned long time_left;
+ int ret, temperature;
+
+ guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy);
+
+ if (ar->ah->state != ATH12K_HW_STATE_ON)
+ return -ENETDOWN;
+
+ reinit_completion(&ar->thermal.wmi_sync);
+ ret = ath12k_wmi_send_pdev_temperature_cmd(ar);
+ if (ret) {
+ ath12k_warn(ar->ab, "failed to read temperature %d\n", ret);
+ return ret;
+ }
+
+ if (test_bit(ATH12K_FLAG_CRASH_FLUSH, &ar->ab->dev_flags))
+ return -ESHUTDOWN;
+
+ time_left = wait_for_completion_timeout(&ar->thermal.wmi_sync,
+ ATH12K_THERMAL_SYNC_TIMEOUT_HZ);
+ if (!time_left) {
+ ath12k_warn(ar->ab, "failed to synchronize thermal read\n");
+ return -ETIMEDOUT;
+ }
+
+ spin_lock_bh(&ar->data_lock);
+ temperature = ar->thermal.temperature;
+ spin_unlock_bh(&ar->data_lock);
+
+ /* display in millidegree celsius */
+ return sysfs_emit(buf, "%d\n", temperature * 1000);
+}
+
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature)
+{
+ spin_lock_bh(&ar->data_lock);
+ ar->thermal.temperature = temperature;
+ spin_unlock_bh(&ar->data_lock);
+ complete_all(&ar->thermal.wmi_sync);
+}
+
+static SENSOR_DEVICE_ATTR_RO(temp1_input, ath12k_thermal_temp, 0);
+
+static struct attribute *ath12k_hwmon_attrs[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ NULL,
+};
+ATTRIBUTE_GROUPS(ath12k_hwmon);
+
+int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ struct ath12k *ar;
+ int i, j, ret;
+
+ if (!IS_REACHABLE(CONFIG_HWMON))
+ return 0;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ continue;
+
+ ar->thermal.hwmon_dev =
+ hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
+ "ath12k_hwmon", ar,
+ ath12k_hwmon_groups);
+ if (IS_ERR(ar->thermal.hwmon_dev)) {
+ ret = PTR_ERR(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
+ ret);
+ for (j = i - 1; j >= 0; j--) {
+ ar = ab->pdevs[j].ar;
+ if (!ar)
+ continue;
+
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ }
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+ struct ath12k *ar;
+ int i;
+
+ if (!IS_REACHABLE(CONFIG_HWMON))
+ return;
+
+ for (i = 0; i < ab->num_radios; i++) {
+ ar = ab->pdevs[i].ar;
+ if (!ar)
+ continue;
+
+ if (ar->thermal.hwmon_dev) {
+ hwmon_device_unregister(ar->thermal.hwmon_dev);
+ ar->thermal.hwmon_dev = NULL;
+ }
+ }
+}
diff --git a/drivers/net/wireless/ath/ath12k/thermal.h b/drivers/net/wireless/ath/ath12k/thermal.h
new file mode 100644
index 000000000000..9d84056188e1
--- /dev/null
+++ b/drivers/net/wireless/ath/ath12k/thermal.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: BSD-3-Clause-Clear */
+/*
+ * Copyright (c) 2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
+ */
+
+#ifndef _ATH12K_THERMAL_
+#define _ATH12K_THERMAL_
+
+#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
+
+struct ath12k_thermal {
+ struct completion wmi_sync;
+
+ /* temperature value in Celsius degree protected by data_lock. */
+ int temperature;
+ struct device *hwmon_dev;
+};
+
+#if IS_REACHABLE(CONFIG_THERMAL)
+int ath12k_thermal_register(struct ath12k_base *ab);
+void ath12k_thermal_unregister(struct ath12k_base *ab);
+void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
+#else
+static inline int ath12k_thermal_register(struct ath12k_base *ab)
+{
+ return 0;
+}
+
+static inline void ath12k_thermal_unregister(struct ath12k_base *ab)
+{
+}
+
+static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
+ int temperature)
+{
+}
+
+#endif
+#endif /* _ATH12K_THERMAL_ */
diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c
index 7617fc3a2479..69f2dbcfca63 100644
--- a/drivers/net/wireless/ath/ath12k/wmi.c
+++ b/drivers/net/wireless/ath/ath12k/wmi.c
@@ -6778,31 +6778,6 @@ static int ath12k_pull_peer_assoc_conf_ev(struct ath12k_base *ab, struct sk_buff
return 0;
}
-static int
-ath12k_pull_pdev_temp_ev(struct ath12k_base *ab, struct sk_buff *skb,
- const struct wmi_pdev_temperature_event *ev)
-{
- const void **tb;
- int ret;
-
- tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
- if (IS_ERR(tb)) {
- ret = PTR_ERR(tb);
- ath12k_warn(ab, "failed to parse tlv: %d\n", ret);
- return ret;
- }
-
- ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
- if (!ev) {
- ath12k_warn(ab, "failed to fetch pdev temp ev");
- kfree(tb);
- return -EPROTO;
- }
-
- kfree(tb);
- return 0;
-}
-
static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab)
{
/* try to send pending beacons first. they take priority */
@@ -8811,25 +8786,45 @@ static void
ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
struct sk_buff *skb)
{
+ const struct wmi_pdev_temperature_event *ev;
struct ath12k *ar;
- struct wmi_pdev_temperature_event ev = {};
+ const void **tb;
+ int temp;
+ u32 pdev_id;
+
+ tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC);
+ if (IS_ERR(tb)) {
+ ath12k_warn(ab, "failed to parse tlv: %ld\n", PTR_ERR(tb));
+ return;
+ }
- if (ath12k_pull_pdev_temp_ev(ab, skb, &ev) != 0) {
- ath12k_warn(ab, "failed to extract pdev temperature event");
+ ev = tb[WMI_TAG_PDEV_TEMPERATURE_EVENT];
+ if (!ev) {
+ ath12k_warn(ab, "failed to fetch pdev temp ev\n");
+ kfree(tb);
return;
}
+ temp = a_sle32_to_cpu(ev->temp);
+ pdev_id = le32_to_cpu(ev->pdev_id);
+
+ kfree(tb);
+
ath12k_dbg(ab, ATH12K_DBG_WMI,
- "pdev temperature ev temp %d pdev_id %d\n", ev.temp, ev.pdev_id);
+ "pdev temperature ev temp %d pdev_id %u\n",
+ temp, pdev_id);
rcu_read_lock();
- ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev.pdev_id));
+ ar = ath12k_mac_get_ar_by_pdev_id(ab, pdev_id);
if (!ar) {
- ath12k_warn(ab, "invalid pdev id in pdev temperature ev %d", ev.pdev_id);
+ ath12k_warn(ab, "invalid pdev id %u in pdev temperature ev\n",
+ pdev_id);
goto exit;
}
+ ath12k_thermal_event_temperature(ar, temp);
+
exit:
rcu_read_unlock();
}
base-commit: 37a93dd5c49b5fda807fd204edf2547c3493319c
--
2.17.1
^ permalink raw reply related
* Re: [PATCH wireless] wifi: mac80211: Fix ADDBA update when HW supports reordering
From: Pablo MARTIN-GOMEZ @ 2026-02-23 13:25 UTC (permalink / raw)
To: Johannes Berg, Remi Pommarel; +Cc: linux-wireless, linux-kernel
In-Reply-To: <0f0703e2749185f9a334b3435ffe247b42e6923b.camel@sipsolutions.net>
Hello,
On 23/02/2026 12:50, Johannes Berg wrote:
> On Sun, 2026-02-22 at 17:06 +0100, Remi Pommarel wrote:
>>>> That does make sense. However, if I understand correctly, it means that
>>>> even if we end up storing the timeout for drivers that support
>>>> reordering, a new IEEE80211_AMPDU_RX_UPDATE should still be introduced,
>>>> right?
>>>
>>> I guess in order to do a no-op update that doesn't change the timeout,
>>> we could? But not sure it's all worth it?
>>
>> I was going to say it would not be a no-op for a buf_size update but,
>> if I understand correctly, even when SUPPORTS_REORDERING_BUFFER is not
>> set the buf_size update is ignored completely and the reorder_buf is
>> not resized yet a successful addba response is sent. Don't we need to
>> check for buf_size change as well as timeout also?
>
> I was going to say that I thought buf_size is not allowed to change, but
> (re)reading the spec doesn't seem to bear that out.
For once, the standard (802.11-2024) is really clear on this (10.25.2):
A block ack agreement may be modified by the originator by sending an
ADDBA Request frame (see 11.5.2, except that MLME-ADDBA primitives are
not used by the originator). All parameters of the agreement may be
modified except for the TID. If the request is not successful, the
existing agreement is not modified. If the request is successful, the
behavior is as if a DELBA frame for the block ack agreement had been
transmitted by the originator and received by the recipient immediately
prior to the ADDBA Request frame.
>
> I guess we could just unconditionally reject any updates. I'm not sure
> now why we ever added the update in the first place. Perhaps some
> implementation was doing no-op updates and failing on negative
> responses?
>
If the originator is compliant and trying to update, unconditionally
rejecing any update shouldn't have any side-effects. But if the
originator doesn't receive the ADDBA response, it is going to resend an
ADDBA request; for the originator, it is just a retry, but for the
recipient it's an update, so the originator is going to receive a non
success. And unless the originator sends a DELBA "by default" at some
point, it won't be able to create a new BA (for a given TID) with the
recipient.
[...]
> johannes
Pablo MG
^ permalink raw reply
* [RFC PATCH v2 8/8] wifi: mac80211: pass error station if non-STA transmit was requested
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
When cfg80211 requested a transmit without a station, pass an error
station to ieee80211_tx_skb_tid instead of the correct one.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
net/mac80211/offchannel.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index 0a8b4c5e8c12..24a55186b87f 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -857,8 +857,10 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
need_offchan = true;
rcu_read_lock();
- sta = sta_info_get_bss(sdata, mgmt->da);
- mlo_sta = sta && sta->sta.mlo;
+ if (!params->no_sta) {
+ sta = sta_info_get_bss(sdata, mgmt->da);
+ mlo_sta = sta && sta->sta.mlo;
+ }
if (!ieee80211_is_action(mgmt->frame_control) ||
mgmt->u.action.category == WLAN_CATEGORY_PUBLIC ||
@@ -887,7 +889,8 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
local->ops->remain_on_channel &&
memcmp(sdata->vif.cfg.ap_addr, mgmt->bssid, ETH_ALEN))) {
need_offchan = true;
- } else if (sdata->u.mgd.associated &&
+ } else if (!params->no_sta &&
+ sdata->u.mgd.associated &&
ether_addr_equal(sdata->vif.cfg.ap_addr, mgmt->da)) {
sta = sta_info_get_bss(sdata, mgmt->da);
mlo_sta = sta && sta->sta.mlo;
@@ -1026,7 +1029,9 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
}
if (!need_offchan) {
- ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
+ ieee80211_tx_skb_tid(sdata, skb,
+ sta ? sta : ERR_PTR(-ENOENT),
+ 7, link_id);
ret = 0;
goto out_unlock;
}
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 7/8] wifi: mac80211: pass station to ieee80211_tx_skb_tid
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
The station may be relevant for queuing and will also generally be
resolved in some cases. However, we want to be able to prevent looking
up the station based on the address.
Add a station parameter, which can be set to the correct station, to an
error value to prevent station lookup or to NULL to get the old
behaviour where the address is used to find the appropriate station.
Also disable the station lookup for ieee80211_tx_skb_tid_band already as
it does not make any sense to find a station when doing an off-channel
transmit.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
net/mac80211/agg-tx.c | 6 +++---
net/mac80211/ht.c | 4 ++--
net/mac80211/ieee80211_i.h | 14 ++++++++------
net/mac80211/offchannel.c | 2 +-
net/mac80211/rx.c | 2 +-
net/mac80211/tdls.c | 4 ++--
net/mac80211/tx.c | 8 +++++---
7 files changed, 22 insertions(+), 18 deletions(-)
diff --git a/net/mac80211/agg-tx.c b/net/mac80211/agg-tx.c
index d981b0fc57bf..6a5754351f08 100644
--- a/net/mac80211/agg-tx.c
+++ b/net/mac80211/agg-tx.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright(c) 2015-2017 Intel Deutschland GmbH
- * Copyright (C) 2018 - 2024 Intel Corporation
+ * Copyright (C) 2018 - 2024, 2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -97,7 +97,7 @@ static void ieee80211_send_addba_request(struct sta_info *sta, u16 tid,
if (sta->sta.deflink.he_cap.has_he)
ieee80211_add_addbaext(skb, 0, agg_size);
- ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+ ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
}
void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
@@ -126,7 +126,7 @@ void ieee80211_send_bar(struct ieee80211_vif *vif, u8 *ra, u16 tid, u16 ssn)
IEEE80211_SKB_CB(skb)->flags |= IEEE80211_TX_INTFL_DONT_ENCRYPT |
IEEE80211_TX_CTL_REQ_TX_STATUS;
- ieee80211_tx_skb_tid(sdata, skb, tid, -1);
+ ieee80211_tx_skb_tid(sdata, skb, NULL, tid, -1);
}
EXPORT_SYMBOL(ieee80211_send_bar);
diff --git a/net/mac80211/ht.c b/net/mac80211/ht.c
index 1c82a28b03de..f98f5a9a2ebe 100644
--- a/net/mac80211/ht.c
+++ b/net/mac80211/ht.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2007-2010, Intel Corporation
* Copyright 2017 Intel Deutschland GmbH
- * Copyright(c) 2020-2025 Intel Corporation
+ * Copyright(c) 2020-2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -571,7 +571,7 @@ int ieee80211_send_smps_action(struct ieee80211_sub_if_data *sdata,
info->status_data = IEEE80211_STATUS_TYPE_SMPS |
u16_encode_bits(status_link_id << 2 | smps,
IEEE80211_STATUS_SUBDATA_MASK);
- ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+ ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
return 0;
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index e60b814dd89e..793331c1d748 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -2393,7 +2393,8 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
struct sta_info *sta, struct sk_buff *skb);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid, int link_id,
+ struct sk_buff *skb, struct sta_info *sta,
+ int tid, int link_id,
enum nl80211_band band);
static inline bool ieee80211_require_encrypted_assoc(__le16 fc,
@@ -2411,22 +2412,23 @@ int ieee80211_lookup_ra_sta(struct ieee80211_sub_if_data *sdata,
static inline void
ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid,
- enum nl80211_band band)
+ struct sk_buff *skb, int tid, enum nl80211_band band)
{
rcu_read_lock();
- __ieee80211_tx_skb_tid_band(sdata, skb, tid, -1, band);
+ __ieee80211_tx_skb_tid_band(sdata, skb, ERR_PTR(-ENOENT),
+ tid, -1, band);
rcu_read_unlock();
}
void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid, int link_id);
+ struct sk_buff *skb, struct sta_info *sta,
+ int tid, int link_id);
static inline void ieee80211_tx_skb(struct ieee80211_sub_if_data *sdata,
struct sk_buff *skb)
{
/* Send all internal mgmt frames on VO. Accordingly set TID to 7. */
- ieee80211_tx_skb_tid(sdata, skb, 7, -1);
+ ieee80211_tx_skb_tid(sdata, skb, NULL, 7, -1);
}
/**
diff --git a/net/mac80211/offchannel.c b/net/mac80211/offchannel.c
index ae82533e3c02..0a8b4c5e8c12 100644
--- a/net/mac80211/offchannel.c
+++ b/net/mac80211/offchannel.c
@@ -1026,7 +1026,7 @@ int ieee80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
}
if (!need_offchan) {
- ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+ ieee80211_tx_skb_tid(sdata, skb, NULL, 7, link_id);
ret = 0;
goto out_unlock;
}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index ec718b7f3e16..5109791546a7 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4072,7 +4072,7 @@ ieee80211_rx_h_action_return(struct ieee80211_rx_data *rx)
local->hw.offchannel_tx_hw_queue;
}
- __ieee80211_tx_skb_tid_band(rx->sdata, nskb, 7, -1,
+ __ieee80211_tx_skb_tid_band(rx->sdata, nskb, rx->sta, 7, -1,
status->band);
}
diff --git a/net/mac80211/tdls.c b/net/mac80211/tdls.c
index dbbfe2d6842f..39a880ab7edb 100644
--- a/net/mac80211/tdls.c
+++ b/net/mac80211/tdls.c
@@ -6,7 +6,7 @@
* Copyright 2014, Intel Corporation
* Copyright 2014 Intel Mobile Communications GmbH
* Copyright 2015 - 2016 Intel Deutschland GmbH
- * Copyright (C) 2019, 2021-2025 Intel Corporation
+ * Copyright (C) 2019, 2021-2026 Intel Corporation
*/
#include <linux/ieee80211.h>
@@ -1067,7 +1067,7 @@ ieee80211_tdls_prep_mgmt_packet(struct wiphy *wiphy, struct net_device *dev,
}
if (action_code == WLAN_PUB_ACTION_TDLS_DISCOVER_RES) {
- ieee80211_tx_skb_tid(sdata, skb, 7, link_id);
+ ieee80211_tx_skb_tid(sdata, skb, sta, 7, link_id);
return 0;
}
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 007f5a368d41..c788d48ef365 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -6235,7 +6235,8 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *pubsta, u8 tid)
EXPORT_SYMBOL(ieee80211_unreserve_tid);
void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid, int link_id,
+ struct sk_buff *skb, struct sta_info *sta,
+ int tid, int link_id,
enum nl80211_band band)
{
const struct ieee80211_hdr *hdr = (void *)skb->data;
@@ -6292,7 +6293,8 @@ void __ieee80211_tx_skb_tid_band(struct ieee80211_sub_if_data *sdata,
}
void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
- struct sk_buff *skb, int tid, int link_id)
+ struct sk_buff *skb, struct sta_info *sta,
+ int tid, int link_id)
{
struct ieee80211_chanctx_conf *chanctx_conf;
enum nl80211_band band;
@@ -6317,7 +6319,7 @@ void ieee80211_tx_skb_tid(struct ieee80211_sub_if_data *sdata,
band = 0;
}
- __ieee80211_tx_skb_tid_band(sdata, skb, tid, link_id, band);
+ __ieee80211_tx_skb_tid_band(sdata, skb, sta, tid, link_id, band);
rcu_read_unlock();
}
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 6/8] wifi: mac80211: report to cfg80211 when no STA is known for a frame
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
This is relevant for hostapd to know whether address translation was
done on a received management frame.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
net/mac80211/rx.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index d76e8ee1a607..ec718b7f3e16 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -3948,6 +3948,7 @@ ieee80211_rx_h_userspace_mgmt(struct ieee80211_rx_data *rx)
.len = rx->skb->len,
.link_id = rx->link_id,
.have_link_id = rx->link_id >= 0,
+ .no_sta = !rx->sta,
};
/* skip known-bad action frames and return them in the next handler */
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 5/8] wifi: cfg80211: add attribute for TX/RX denoting there is no station
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
For MLD stations, userspace may need to explicitly transmit a frame to
a specific link address. In that case, it needs to ensure that no
address translation happens.
In the reverse case of an RX, userspace may need to know the link
address for a frame. By passing the information whether a STA is known
for the frame, userspace knows whether link translation happened and
can do the reverse lookup when needed.
This is important for flows where a STA is still registered but the
connection has been lost and it is returning again.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
include/net/cfg80211.h | 4 ++++
include/uapi/linux/nl80211.h | 7 +++++++
net/wireless/nl80211.c | 8 +++++++-
3 files changed, 18 insertions(+), 1 deletion(-)
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index fc01de19c798..5063911cba56 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3919,6 +3919,7 @@ struct cfg80211_update_ft_ies_params {
* @link_id: for MLO, the link ID to transmit on, -1 if not given; note
* that the link ID isn't validated (much), it's in range but the
* link might not exist (or be used by the receiver STA)
+ * @no_sta: set if the frame should not be transmitted using an existing STA
*/
struct cfg80211_mgmt_tx_params {
struct ieee80211_channel *chan;
@@ -3931,6 +3932,7 @@ struct cfg80211_mgmt_tx_params {
int n_csa_offsets;
const u16 *csa_offsets;
int link_id;
+ bool no_sta;
};
/**
@@ -9025,6 +9027,7 @@ void cfg80211_conn_failed(struct net_device *dev, const u8 *mac_addr,
* @flags: flags, as defined in &enum nl80211_rxmgmt_flags
* @rx_tstamp: Hardware timestamp of frame RX in nanoseconds
* @ack_tstamp: Hardware timestamp of ack TX in nanoseconds
+ * @no_sta: set if no station is known for the frame (relevant for MLD)
*/
struct cfg80211_rx_info {
int freq;
@@ -9036,6 +9039,7 @@ struct cfg80211_rx_info {
u32 flags;
u64 rx_tstamp;
u64 ack_tstamp;
+ bool no_sta;
};
/**
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index b63f71850906..1466c043974c 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -2984,6 +2984,11 @@ enum nl80211_commands {
* this feature during association. This is a flag attribute.
* Currently only supported in mac80211 drivers.
*
+ * @NL80211_ATTR_FRAME_CMD_NO_STA: Valid for NL80211_CMD_FRAME to denote that
+ * the kernel had no station for a received frame or should not use a
+ * known station to transmit a frame. This is relevant to know whether
+ * MLD address translation happened or to disable it when sending a frame.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -3557,6 +3562,8 @@ enum nl80211_attrs {
NL80211_ATTR_UHR_CAPABILITY,
NL80211_ATTR_DISABLE_UHR,
+ NL80211_ATTR_FRAME_CMD_NO_STA,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 6e58b238a1f8..56a9c63ddd76 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -946,6 +946,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_UHR_CAPABILITY] =
NLA_POLICY_VALIDATE_FN(NLA_BINARY, validate_uhr_capa, 255),
[NL80211_ATTR_DISABLE_UHR] = { .type = NLA_FLAG },
+ [NL80211_ATTR_FRAME_CMD_NO_STA] = { .type = NLA_FLAG },
};
/* policy for the key attributes */
@@ -14020,6 +14021,9 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
!(wdev->valid_links & BIT(params.link_id)))
return -EINVAL;
+ params.no_sta =
+ nla_get_flag(info->attrs[NL80211_ATTR_FRAME_CMD_NO_STA]);
+
params.buf = nla_data(info->attrs[NL80211_ATTR_FRAME]);
params.len = nla_len(info->attrs[NL80211_ATTR_FRAME]);
@@ -20581,7 +20585,9 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
(info->ack_tstamp && nla_put_u64_64bit(msg,
NL80211_ATTR_TX_HW_TIMESTAMP,
info->ack_tstamp,
- NL80211_ATTR_PAD)))
+ NL80211_ATTR_PAD)) ||
+ (info->no_sta &&
+ nla_put_flag(msg, NL80211_ATTR_FRAME_CMD_NO_STA)))
goto nla_put_failure;
genlmsg_end(msg, hdr);
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 4/8] wifi: mac80211: rework RX packet handling
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
The code has grown over time and it was not correctly handling all
cases. Examples of issues are that for management frames we would match
the received address against the MLD address even though we should not
resolve the station in that case. Another issue was that for data frames
we would not correctly fall back to use link addresses when the driver
does not provide the pubsta already.
The new code still uses the same approach as before. For data frames,
assume that we a valid station exists and interfaces do not need to be
iterated. On the other hand, for non-data frames (or if a data frame
without a station is received) all interfaces must be iterated.
Note that this rework makes mac80211 slightly more strict. For example,
previously mac80211 would have incorrectly accepted a data frame that
was transmitted on the wrong link.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
net/mac80211/rx.c | 282 +++++++++++++++++++++++++++-------------------
1 file changed, 169 insertions(+), 113 deletions(-)
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 1b8ec391991f..d76e8ee1a607 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -4284,9 +4284,33 @@ static bool
ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
{
if (sta->mlo)
- return !!(sta->valid_links & BIT(link_id));
- else
- return sta->deflink.link_id == link_id;
+ return sta->valid_links & BIT(link_id);
+
+ return sta->deflink.link_id == link_id;
+}
+
+static bool ieee80211_rx_data_set_link_sta(struct ieee80211_rx_data *rx,
+ struct link_sta_info *link_sta)
+{
+ struct sta_info *sta;
+
+ if (WARN_ON_ONCE(!link_sta) ||
+ !ieee80211_rx_is_valid_sta_link_id(&link_sta->sta->sta,
+ link_sta->link_id))
+ return false;
+
+ sta = link_sta->sta;
+
+ rx->sta = sta;
+ rx->local = sta->sdata->local;
+ rx->link = rcu_dereference(sta->sdata->link[link_sta->link_id]);
+ if (!rx->link)
+ return false;
+
+ rx->link_id = rx->link->link_id;
+ rx->link_sta = link_sta;
+
+ return true;
}
static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -5192,53 +5216,18 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
dev_kfree_skb(skb);
}
-static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
- struct sk_buff *skb, bool consume)
+static bool ieee80211_rx_valid_freq(int freq, struct ieee80211_link_data *link)
{
- struct link_sta_info *link_sta;
- struct ieee80211_hdr *hdr = (void *)skb->data;
- struct sta_info *sta;
- int link_id = -1;
+ struct ieee80211_chanctx_conf *conf;
- /*
- * FIXME: Here we can assume that link addresses have not
- * been translated.
- *
- * Look up link station first, in case there's a
- * chance that they might have a link address that
- * is identical to the MLD address, that way we'll
- * have the link information if needed.
- */
- link_sta = link_sta_info_get_bss(rx->sdata, hdr->addr2);
- if (link_sta) {
- sta = link_sta->sta;
- link_id = link_sta->link_id;
- } else {
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
- sta = sta_info_get_bss(rx->sdata, hdr->addr2);
- if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
- status->freq) {
- struct ieee80211_link_data *link;
- struct ieee80211_chanctx_conf *conf;
-
- for_each_link_data_rcu(rx->sdata, link) {
- conf = rcu_dereference(link->conf->chanctx_conf);
- if (!conf || !conf->def.chan)
- continue;
-
- if (status->freq == conf->def.chan->center_freq) {
- link_id = link->link_id;
- break;
- }
- }
- }
- }
+ if (!freq || link->sdata->vif.type == NL80211_IFTYPE_NAN)
+ return true;
- if (!ieee80211_rx_data_set_sta(rx, sta, link_id))
+ conf = rcu_dereference(link->conf->chanctx_conf);
+ if (!conf || !conf->def.chan)
return false;
- return ieee80211_prepare_and_rx_handle(rx, skb, consume);
+ return freq == conf->def.chan->center_freq;
}
/*
@@ -5252,11 +5241,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
{
struct ieee80211_local *local = hw_to_local(hw);
struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr;
+ struct link_sta_info *link_sta;
+ struct sta_info *sta;
__le16 fc;
struct ieee80211_rx_data rx;
- struct ieee80211_sub_if_data *prev;
struct rhlist_head *tmp;
+ bool rx_data_pending;
int err = 0;
fc = ((struct ieee80211_hdr *)skb->data)->frame_control;
@@ -5292,92 +5284,102 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
ieee80211_is_s1g_beacon(hdr->frame_control)))
ieee80211_scan_rx(local, skb);
+ /*
+ * RX of a data frame should only happen to existing stations.
+ * It is therefore more efficient to use the one provided by the driver
+ * or search based on the station's address.
+ *
+ * Note we will fall through and handle a spurious data frame in the
+ * same way as a management frame.
+ */
if (ieee80211_is_data(fc)) {
- struct sta_info *sta, *prev_sta;
-
if (link_pubsta) {
- sta = container_of(link_pubsta->sta,
- struct sta_info, sta);
- if (!ieee80211_rx_data_set_sta(&rx, sta,
- link_pubsta->link_id))
- goto out;
+ sta = container_of(link_pubsta->sta, struct sta_info,
+ sta);
+ link_sta = rcu_dereference(sta->link[link_pubsta->link_id]);
- if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
+ rx.sdata = sta->sdata;
+ if (ieee80211_rx_data_set_link_sta(&rx, link_sta) &&
+ ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
+
goto out;
}
- prev_sta = NULL;
+ rx_data_pending = false;
+ /* Search for stations on non-MLD interfaces */
for_each_sta_info(local, hdr->addr2, sta, tmp) {
- int link_id;
+ struct ieee80211_link_data *link;
- if (!prev_sta) {
- prev_sta = sta;
+ if (ieee80211_vif_is_mld(&sta->sdata->vif))
continue;
- }
-
- rx.sdata = prev_sta->sdata;
-
- /*
- * FIXME: This is not correct as the addr2 cannot be a
- * link address if the loop itself is iterated.
- */
- if (prev_sta->sta.mlo) {
- struct link_sta_info *link_sta;
- link_sta = link_sta_info_get_bss(rx.sdata,
- hdr->addr2);
- if (!link_sta)
- continue;
+ link = &sta->sdata->deflink;
+ if (!ieee80211_rx_valid_freq(status->freq, link))
+ continue;
- link_id = link_sta->link_id;
- } else {
- link_id = sta->deflink.link_id;
+ if (rx_data_pending) {
+ ieee80211_prepare_and_rx_handle(&rx, skb,
+ false);
+ rx_data_pending = false;
}
- if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
- goto out;
-
- ieee80211_prepare_and_rx_handle(&rx, skb, false);
+ rx.sdata = sta->sdata;
+ if (!ieee80211_rx_data_set_link_sta(&rx, &sta->deflink))
+ continue;
- prev_sta = sta;
+ rx_data_pending = true;
}
- if (prev_sta) {
- int link_id;
+ /* And search stations on MLD interfaces */
+ for_each_link_sta_info(local, hdr->addr2, link_sta, tmp) {
+ struct ieee80211_link_data *link;
- rx.sdata = prev_sta->sdata;
+ sta = link_sta->sta;
+ sdata = sta->sdata;
+ link = rcu_dereference(sdata->link[link_sta->link_id]);
- /*
- * FIXME: This is not correct as the addr2 cannot be a
- * link address if the loop itself is iterated.
- */
- if (prev_sta->sta.mlo) {
- struct link_sta_info *link_sta;
-
- link_sta = link_sta_info_get_bss(rx.sdata,
- hdr->addr2);
- if (!link_sta)
- goto out;
+ if (!ieee80211_rx_valid_freq(status->freq, link))
+ continue;
- link_id = link_sta->link_id;
- } else {
- link_id = sta->deflink.link_id;
+ if (rx_data_pending) {
+ ieee80211_prepare_and_rx_handle(&rx, skb,
+ false);
+ rx_data_pending = false;
}
- if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
- goto out;
+ rx.sdata = sta->sdata;
+ if (!ieee80211_rx_data_set_link_sta(&rx, link_sta))
+ continue;
+
+ rx_data_pending = true;
+ }
+ if (rx_data_pending) {
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
+
goto out;
}
+
+ /* fall through, e.g. for spurious frame notification */
}
- prev = NULL;
+ /*
+ * This is a management frame (or a data frame without a station) and
+ * it will be delivered independent of whether a station exists,
+ * so iterate the interfaces.
+ */
+ rx_data_pending = false;
+
+ /* We expect the driver to provide frequency information */
+ if (WARN_ON_ONCE(!local->emulate_chanctx && !status->freq))
+ goto out;
list_for_each_entry_rcu(sdata, &local->interfaces, list) {
+ struct ieee80211_link_data *link = NULL;
+
if (!ieee80211_sdata_running(sdata))
continue;
@@ -5385,30 +5387,84 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
sdata->vif.type == NL80211_IFTYPE_AP_VLAN)
continue;
+ /* Try to resolve the station */
+ if (!ieee80211_vif_is_mld(&sdata->vif)) {
+ sta = sta_info_get_bss(sdata, hdr->addr2);
+
+ if (sta) {
+ link_sta = &sta->deflink;
+ link = &sdata->deflink;
+ }
+ } else {
+ link_sta = link_sta_info_get_bss(sdata, hdr->addr2);
+
+ if (link_sta) {
+ sta = link_sta->sta;
+ link = rcu_dereference(sdata->link[link_sta->link_id]);
+ }
+ }
+
/*
- * frame is destined for this interface, but if it's
- * not also for the previous one we handle that after
- * the loop to avoid copying the SKB once too much
+ * If we found a STA and it has a valid link information, then
+ * RX using the station.
*/
+ if (link_sta && link &&
+ ieee80211_rx_valid_freq(status->freq, link)) {
+ if (rx_data_pending) {
+ ieee80211_prepare_and_rx_handle(&rx, skb, false);
+ rx_data_pending = false;
+ }
+
+ /* No valid_links check as we need to RX beacons */
+
+ rx.sdata = sdata;
+ if (ieee80211_rx_data_set_link_sta(&rx, link_sta))
+ rx_data_pending = true;
- if (!prev) {
- prev = sdata;
continue;
}
- rx.sdata = prev;
- ieee80211_rx_for_interface(&rx, skb, false);
+ /* No station, try to resolve the link and RX */
+ if (ieee80211_vif_is_mld(&sdata->vif)) {
+ struct ieee80211_chanctx_conf *conf;
+ bool found = false;
- prev = sdata;
- }
+ for_each_link_data_rcu(sdata, link) {
+ conf = rcu_dereference(link->conf->chanctx_conf);
+ if (!conf || !conf->def.chan)
+ continue;
+
+ if (status->freq == conf->def.chan->center_freq) {
+ found = true;
+ break;
+ }
+ }
- if (prev) {
- rx.sdata = prev;
+ if (!found)
+ link = &sdata->deflink;
+ } else {
+ link = &sdata->deflink;
+ }
- if (ieee80211_rx_for_interface(&rx, skb, true))
- return;
+ if (rx_data_pending) {
+ ieee80211_prepare_and_rx_handle(&rx, skb, false);
+ rx_data_pending = false;
+ }
+
+ rx.sdata = sdata;
+ rx.local = sdata->local;
+ rx.link = link;
+ rx.link_id = link->link_id;
+ rx.sta = NULL;
+ rx.link_sta = NULL;
+
+ rx_data_pending = true;
}
+ if (rx_data_pending &&
+ ieee80211_prepare_and_rx_handle(&rx, skb, true))
+ return;
+
out:
dev_kfree_skb(skb);
}
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 3/8] wifi: mac80211: refactor RX link_id and station handling
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
The link ID for one frequency may be different between VIFs. As such,
the sensible thing is to set the link ID later in the flow once the SKB
is duplicated for each VIF. As the link ID is not passed in from outside
mac80211 it is always valid and the corresponding bit can be dropped.
Also switch a few more places to pass the link STA as that is a natural
way to pass the link information around.
This fixes the per-link statistics when frames are reordered.
Note that this patch deliberately leaves some places unchanged and even
incorrect as this will be addressed by further refactorings.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
include/net/mac80211.h | 9 +-
net/mac80211/eht.c | 3 -
net/mac80211/iface.c | 7 +-
net/mac80211/mlme.c | 9 +-
net/mac80211/rx.c | 209 ++++++++++++++++++-----------------------
net/mac80211/scan.c | 10 +-
6 files changed, 105 insertions(+), 142 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 4d9dbd35369b..6db15227d4e1 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -1655,10 +1655,9 @@ enum mac80211_rx_encoding {
* @ampdu_reference: A-MPDU reference number, must be a different value for
* each A-MPDU but the same for each subframe within one A-MPDU
* @zero_length_psdu_type: radiotap type of the 0-length PSDU
- * @link_valid: if the link which is identified by @link_id is valid. This flag
- * is set only when connection is MLO.
- * @link_id: id of the link used to receive the packet. This is used along with
- * @link_valid.
+ * @link_id: id of the link used to receive the packet. Set and used by
+ * mac80211 internally, it uses @freq set by the driver to identify the
+ * correct link per vif.
*/
struct ieee80211_rx_status {
u64 mactime;
@@ -1698,7 +1697,7 @@ struct ieee80211_rx_status {
u8 chains;
s8 chain_signal[IEEE80211_MAX_CHAINS];
u8 zero_length_psdu_type;
- u8 link_valid:1, link_id:4;
+ u8 link_id:4;
};
static inline u32
diff --git a/net/mac80211/eht.c b/net/mac80211/eht.c
index 75096b2195d2..f49c4729e8e8 100644
--- a/net/mac80211/eht.c
+++ b/net/mac80211/eht.c
@@ -174,9 +174,6 @@ void ieee80211_rx_eml_op_mode_notif(struct ieee80211_sub_if_data *sdata,
if (!ift_ext_capa)
return;
- if (!status->link_valid)
- return;
-
sta = sta_info_get_bss(sdata, mgmt->sa);
if (!sta)
return;
diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
index 676b2a43c9f2..ddb384d7cfa2 100644
--- a/net/mac80211/iface.c
+++ b/net/mac80211/iface.c
@@ -1611,11 +1611,8 @@ static void ieee80211_iface_process_skb(struct ieee80211_local *local,
break;
status = IEEE80211_SKB_RXCB(skb);
- if (!status->link_valid)
- link_sta = &sta->deflink;
- else
- link_sta = rcu_dereference_protected(sta->link[status->link_id],
- lockdep_is_held(&local->hw.wiphy->mtx));
+ link_sta = wiphy_dereference(local->hw.wiphy,
+ sta->link[status->link_id]);
if (link_sta)
ieee80211_ht_handle_chanwidth_notif(local, sdata, sta,
link_sta, chanwidth,
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index e83582b2c377..4190c2830ac5 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -8273,12 +8273,9 @@ void ieee80211_sta_rx_queued_mgmt(struct ieee80211_sub_if_data *sdata,
mgmt = (struct ieee80211_mgmt *) skb->data;
fc = le16_to_cpu(mgmt->frame_control);
- if (rx_status->link_valid) {
- link = sdata_dereference(sdata->link[rx_status->link_id],
- sdata);
- if (!link)
- return;
- }
+ link = sdata_dereference(sdata->link[rx_status->link_id], sdata);
+ if (!link)
+ return;
switch (fc & IEEE80211_FCTL_STYPE) {
case IEEE80211_STYPE_BEACON:
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4098f63ec824..1b8ec391991f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -222,43 +222,21 @@ ieee80211_rx_radiotap_hdrlen(struct ieee80211_local *local,
}
static void __ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
- int link_id,
- struct sta_info *sta,
+ struct link_sta_info *link_sta,
struct sk_buff *skb)
{
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
-
- if (link_id >= 0) {
- status->link_valid = 1;
- status->link_id = link_id;
- } else {
- status->link_valid = 0;
- }
-
skb_queue_tail(&sdata->skb_queue, skb);
wiphy_work_queue(sdata->local->hw.wiphy, &sdata->work);
- if (sta) {
- struct link_sta_info *link_sta_info;
-
- if (link_id >= 0) {
- link_sta_info = rcu_dereference(sta->link[link_id]);
- if (!link_sta_info)
- return;
- } else {
- link_sta_info = &sta->deflink;
- }
-
- link_sta_info->rx_stats.packets++;
- }
+ if (link_sta)
+ link_sta->rx_stats.packets++;
}
static void ieee80211_queue_skb_to_iface(struct ieee80211_sub_if_data *sdata,
- int link_id,
- struct sta_info *sta,
+ struct link_sta_info *link_sta,
struct sk_buff *skb)
{
skb->protocol = 0;
- __ieee80211_queue_skb_to_iface(sdata, link_id, sta, skb);
+ __ieee80211_queue_skb_to_iface(sdata, link_sta, skb);
}
static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
@@ -301,7 +279,7 @@ static void ieee80211_handle_mu_mimo_mon(struct ieee80211_sub_if_data *sdata,
if (!skb)
return;
- ieee80211_queue_skb_to_iface(sdata, -1, NULL, skb);
+ ieee80211_queue_skb_to_iface(sdata, NULL, skb);
}
/*
@@ -1496,7 +1474,7 @@ static void ieee80211_rx_reorder_ampdu(struct ieee80211_rx_data *rx,
/* if this mpdu is fragmented - terminate rx aggregation session */
sc = le16_to_cpu(hdr->seq_ctrl);
if (sc & IEEE80211_SCTL_FRAG) {
- ieee80211_queue_skb_to_iface(rx->sdata, rx->link_id, NULL, skb);
+ ieee80211_queue_skb_to_iface(rx->sdata, NULL, skb);
return;
}
@@ -2498,7 +2476,7 @@ ieee80211_rx_h_defragment(struct ieee80211_rx_data *rx)
out:
ieee80211_led_rx(rx->local);
- if (rx->sta)
+ if (rx->link_sta)
rx->link_sta->rx_stats.packets++;
return RX_CONTINUE;
}
@@ -3309,8 +3287,8 @@ ieee80211_rx_h_data(struct ieee80211_rx_data *rx)
(tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_REQUEST ||
tf->action_code == WLAN_TDLS_CHANNEL_SWITCH_RESPONSE)) {
rx->skb->protocol = cpu_to_be16(ETH_P_TDLS);
- __ieee80211_queue_skb_to_iface(sdata, rx->link_id,
- rx->sta, rx->skb);
+ __ieee80211_queue_skb_to_iface(sdata, rx->link_sta,
+ rx->skb);
return RX_QUEUED;
}
}
@@ -3956,7 +3934,7 @@ ieee80211_rx_h_action(struct ieee80211_rx_data *rx)
return RX_QUEUED;
queue:
- ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
return RX_QUEUED;
}
@@ -4113,7 +4091,7 @@ ieee80211_rx_h_ext(struct ieee80211_rx_data *rx)
return RX_DROP_U_UNEXPECTED_EXT_FRAME;
/* for now only beacons are ext, so queue them */
- ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
return RX_QUEUED;
}
@@ -4170,7 +4148,7 @@ ieee80211_rx_h_mgmt(struct ieee80211_rx_data *rx)
return RX_DROP_U_UNHANDLED_MGMT_STYPE;
}
- ieee80211_queue_skb_to_iface(sdata, rx->link_id, rx->sta, rx->skb);
+ ieee80211_queue_skb_to_iface(sdata, rx->link_sta, rx->skb);
return RX_QUEUED;
}
@@ -4214,12 +4192,28 @@ static void ieee80211_rx_handlers(struct ieee80211_rx_data *rx,
spin_lock_bh(&rx->local->rx_path_lock);
while ((skb = __skb_dequeue(frames))) {
+ struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
+
/*
- * all the other fields are valid across frames
- * that belong to an aMPDU since they are on the
- * same TID from the same station
+ * Most fields are valid across frames. However,
+ * in the case of reordering frames may have arrived
+ * on different links, so adjust the link_id, link
+ * and link_sta accordingly.
*/
rx->skb = skb;
+ if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
+ unlikely(rx->link_id != status->link_id)) {
+ if (rx->sta) {
+ rx->link_sta =
+ rcu_dereference(rx->sta->link[rx->link_id]);
+
+ /* Link got disabled, just use deflink */
+ if (!rx->link_sta)
+ rx->link_sta = &rx->sta->deflink;
+ }
+ rx->link_id = status->link_id;
+ rx->link = rcu_dereference(rx->sdata->link[status->link_id]);
+ }
if (WARN_ON_ONCE(!rx->link)) {
res = RX_DROP_U_NO_LINK;
@@ -4289,7 +4283,10 @@ static void ieee80211_invoke_rx_handlers(struct ieee80211_rx_data *rx)
static bool
ieee80211_rx_is_valid_sta_link_id(struct ieee80211_sta *sta, u8 link_id)
{
- return !!(sta->valid_links & BIT(link_id));
+ if (sta->mlo)
+ return !!(sta->valid_links & BIT(link_id));
+ else
+ return sta->deflink.link_id == link_id;
}
static bool ieee80211_rx_data_set_link(struct ieee80211_rx_data *rx,
@@ -4326,11 +4323,13 @@ static bool ieee80211_rx_data_set_sta(struct ieee80211_rx_data *rx,
if (link_id < 0) {
if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
- sta && !sta->sta.valid_links)
+ sta && !sta->sta.valid_links) {
rx->link =
rcu_dereference(rx->sdata->link[sta->deflink.link_id]);
- else
+ rx->link_sta = rcu_dereference(rx->sta->link[link_id]);
+ } else {
rx->link = &rx->sdata->deflink;
+ }
} else if (!ieee80211_rx_data_set_link(rx, link_id)) {
return false;
}
@@ -4353,7 +4352,7 @@ void ieee80211_release_reorder_timeout(struct sta_info *sta, int tid)
struct tid_ampdu_rx *tid_agg_rx;
int link_id = -1;
- /* FIXME: statistics won't be right with this */
+ /* NOTE: the correct link STA for the frame is set later */
if (sta->sta.valid_links)
link_id = ffs(sta->sta.valid_links) - 1;
@@ -4824,20 +4823,14 @@ static void ieee80211_rx_8023(struct ieee80211_rx_data *rx,
{
struct ieee80211_sta_rx_stats *stats;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(rx->skb);
- struct sta_info *sta = rx->sta;
- struct link_sta_info *link_sta;
+ struct link_sta_info *link_sta = rx->link_sta;
struct sk_buff *skb = rx->skb;
void *sa = skb->data + ETH_ALEN;
void *da = skb->data;
- if (rx->link_id >= 0) {
- link_sta = rcu_dereference(sta->link[rx->link_id]);
- if (WARN_ON_ONCE(!link_sta)) {
- dev_kfree_skb(rx->skb);
- return;
- }
- } else {
- link_sta = &sta->deflink;
+ if (WARN_ON_ONCE(!link_sta)) {
+ dev_kfree_skb(rx->skb);
+ return;
}
stats = &link_sta->rx_stats;
@@ -5060,6 +5053,9 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
goto drop;
}
+ status = IEEE80211_SKB_RXCB(skb);
+ status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
ieee80211_rx_8023(rx, fast_rx, orig_len);
return true;
@@ -5084,6 +5080,7 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
struct ieee80211_hdr *hdr = (void *)skb->data;
struct link_sta_info *link_sta = rx->link_sta;
struct ieee80211_link_data *link = rx->link;
+ struct ieee80211_rx_status *status;
rx->skb = skb;
@@ -5127,6 +5124,9 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
hdr = (struct ieee80211_hdr *)rx->skb->data;
}
+ status = IEEE80211_SKB_RXCB(rx->skb);
+ status->link_id = rx->link_id < 0 ? 0 : rx->link_id;
+
if (unlikely(rx->sta && rx->sta->sta.mlo) &&
is_unicast_ether_addr(hdr->addr1) &&
!ieee80211_is_probe_resp(hdr->frame_control) &&
@@ -5152,22 +5152,19 @@ static bool ieee80211_prepare_and_rx_handle(struct ieee80211_rx_data *rx,
}
static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
- struct ieee80211_sta *pubsta,
+ struct ieee80211_link_sta *link_pubsta,
struct sk_buff *skb,
struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_fast_rx *fast_rx;
struct ieee80211_rx_data rx;
struct sta_info *sta;
- int link_id = -1;
memset(&rx, 0, sizeof(rx));
rx.skb = skb;
rx.local = local;
rx.list = list;
- rx.link_id = -1;
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -5175,23 +5172,15 @@ static void __ieee80211_rx_handle_8023(struct ieee80211_hw *hw,
if (skb->len < sizeof(struct ethhdr))
goto drop;
- if (!pubsta)
+ if (!link_pubsta)
goto drop;
- if (status->link_valid)
- link_id = status->link_id;
-
- /*
- * TODO: Should the frame be dropped if the right link_id is not
- * available? Or may be it is fine in the current form to proceed with
- * the frame processing because with frame being in 802.3 format,
- * link_id is used only for stats purpose and updating the stats on
- * the deflink is fine?
- */
- sta = container_of(pubsta, struct sta_info, sta);
- if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
+ sta = container_of(link_pubsta->sta, struct sta_info, sta);
+ if (!ieee80211_rx_data_set_sta(&rx, sta, link_pubsta->link_id))
goto drop;
+ rx.link_id = link_pubsta->link_id;
+
fast_rx = rcu_dereference(rx.sta->fast_rx);
if (!fast_rx)
goto drop;
@@ -5212,6 +5201,9 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
int link_id = -1;
/*
+ * FIXME: Here we can assume that link addresses have not
+ * been translated.
+ *
* Look up link station first, in case there's a
* chance that they might have a link address that
* is identical to the MLD address, that way we'll
@@ -5225,10 +5217,8 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
sta = sta_info_get_bss(rx->sdata, hdr->addr2);
- if (status->link_valid) {
- link_id = status->link_id;
- } else if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
- status->freq) {
+ if (ieee80211_vif_is_mld(&rx->sdata->vif) &&
+ status->freq) {
struct ieee80211_link_data *link;
struct ieee80211_chanctx_conf *conf;
@@ -5256,12 +5246,11 @@ static bool ieee80211_rx_for_interface(struct ieee80211_rx_data *rx,
* be called with rcu_read_lock protection.
*/
static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
- struct ieee80211_sta *pubsta,
+ struct ieee80211_link_sta *link_pubsta,
struct sk_buff *skb,
struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
- struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_sub_if_data *sdata;
struct ieee80211_hdr *hdr;
__le16 fc;
@@ -5275,7 +5264,6 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
rx.skb = skb;
rx.local = local;
rx.list = list;
- rx.link_id = -1;
if (ieee80211_is_data(fc) || ieee80211_is_mgmt(fc))
I802_DEBUG_INC(local->dot11ReceivedFragmentCount);
@@ -5306,35 +5294,14 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
if (ieee80211_is_data(fc)) {
struct sta_info *sta, *prev_sta;
- int link_id = -1;
-
- if (status->link_valid)
- link_id = status->link_id;
- if (pubsta) {
- sta = container_of(pubsta, struct sta_info, sta);
- if (!ieee80211_rx_data_set_sta(&rx, sta, link_id))
+ if (link_pubsta) {
+ sta = container_of(link_pubsta->sta,
+ struct sta_info, sta);
+ if (!ieee80211_rx_data_set_sta(&rx, sta,
+ link_pubsta->link_id))
goto out;
- /*
- * In MLO connection, fetch the link_id using addr2
- * when the driver does not pass link_id in status.
- * When the address translation is already performed by
- * driver/hw, the valid link_id must be passed in
- * status.
- */
-
- if (!status->link_valid && pubsta->mlo) {
- struct link_sta_info *link_sta;
-
- link_sta = link_sta_info_get_bss(rx.sdata,
- hdr->addr2);
- if (!link_sta)
- goto out;
-
- ieee80211_rx_data_set_link(&rx, link_sta->link_id);
- }
-
if (ieee80211_prepare_and_rx_handle(&rx, skb, true))
return;
goto out;
@@ -5343,13 +5310,20 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
prev_sta = NULL;
for_each_sta_info(local, hdr->addr2, sta, tmp) {
+ int link_id;
+
if (!prev_sta) {
prev_sta = sta;
continue;
}
rx.sdata = prev_sta->sdata;
- if (!status->link_valid && prev_sta->sta.mlo) {
+
+ /*
+ * FIXME: This is not correct as the addr2 cannot be a
+ * link address if the loop itself is iterated.
+ */
+ if (prev_sta->sta.mlo) {
struct link_sta_info *link_sta;
link_sta = link_sta_info_get_bss(rx.sdata,
@@ -5358,6 +5332,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
continue;
link_id = link_sta->link_id;
+ } else {
+ link_id = sta->deflink.link_id;
}
if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
@@ -5369,8 +5345,15 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
}
if (prev_sta) {
+ int link_id;
+
rx.sdata = prev_sta->sdata;
- if (!status->link_valid && prev_sta->sta.mlo) {
+
+ /*
+ * FIXME: This is not correct as the addr2 cannot be a
+ * link address if the loop itself is iterated.
+ */
+ if (prev_sta->sta.mlo) {
struct link_sta_info *link_sta;
link_sta = link_sta_info_get_bss(rx.sdata,
@@ -5379,6 +5362,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
goto out;
link_id = link_sta->link_id;
+ } else {
+ link_id = sta->deflink.link_id;
}
if (!ieee80211_rx_data_set_sta(&rx, prev_sta, link_id))
@@ -5441,7 +5426,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw,
struct ieee80211_supported_band *sband;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
- struct ieee80211_sta *pubsta;
WARN_ON_ONCE(softirq_count() == 0);
@@ -5564,16 +5548,6 @@ void ieee80211_rx_list(struct ieee80211_hw *hw,
}
}
- /* FIXME: Emulate the old driver behaviour for now */
- if (link_pubsta) {
- status->link_valid = 1;
- status->link_id = link_pubsta->link_id;
- pubsta = link_pubsta->sta;
- } else {
- status->link_valid = 0;
- pubsta = NULL;
- }
-
status->rx_flags = 0;
kcov_remote_start_common(skb_get_kcov_handle(skb));
@@ -5592,9 +5566,10 @@ void ieee80211_rx_list(struct ieee80211_hw *hw,
ieee80211_tpt_led_trig_rx(local, skb->len);
if (status->flag & RX_FLAG_8023)
- __ieee80211_rx_handle_8023(hw, pubsta, skb, list);
+ __ieee80211_rx_handle_8023(hw, link_pubsta, skb, list);
else
- __ieee80211_rx_handle_packet(hw, pubsta, skb, list);
+ __ieee80211_rx_handle_packet(hw, link_pubsta, skb,
+ list);
}
kcov_remote_stop();
diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
index 4823c8d45639..bcbae67e2f49 100644
--- a/net/mac80211/scan.c
+++ b/net/mac80211/scan.c
@@ -9,7 +9,7 @@
* Copyright 2007, Michael Wu <flamingice@sourmilk.net>
* Copyright 2013-2015 Intel Mobile Communications GmbH
* Copyright 2016-2017 Intel Deutschland GmbH
- * Copyright (C) 2018-2025 Intel Corporation
+ * Copyright (C) 2018-2026 Intel Corporation
*/
#include <linux/if_arp.h>
@@ -204,12 +204,10 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
* an indication on which of the links the frame was received
*/
if (ieee80211_vif_is_mld(&scan_sdata->vif)) {
- if (rx_status->link_valid) {
- s8 link_id = rx_status->link_id;
+ s8 link_id = rx_status->link_id;
- link_conf =
- rcu_dereference(scan_sdata->vif.link_conf[link_id]);
- }
+ link_conf =
+ rcu_dereference(scan_sdata->vif.link_conf[link_id]);
} else {
link_conf = &scan_sdata->vif.bss_conf;
}
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 2/8] wifi: mac80211: change public RX API to use link stations
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
If a station is passed then the link ID also needs to be known. As such,
it is a more natural API to simply pass the link station directly rather
than pushing the link information into the RX status.
Furthermore, having the link ID in the RX status is not actually correct
because the link IDs are VIF specific and there may be multiple VIFs. In
the case of a station this relationship is clear, but then one may as
well use the link station.
This patch only changes the API and emulates the old (incorrect)
behaviour for now. The mac80211 RX code will be updated in later
patches.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
drivers/net/wireless/ath/ath11k/dp_rx.c | 2 +-
drivers/net/wireless/ath/ath12k/dp_mon.c | 18 +++++++--------
drivers/net/wireless/ath/ath12k/dp_rx.c | 15 +++++++++----
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 7 +-----
drivers/net/wireless/intel/iwlwifi/mvm/rx.c | 2 +-
drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c | 6 ++---
drivers/net/wireless/mediatek/mt76/mac80211.c | 22 +++++++++++--------
drivers/net/wireless/realtek/rtw89/core.c | 6 -----
drivers/net/wireless/virtual/mac80211_hwsim.c | 3 ---
include/net/mac80211.h | 16 ++++++++++----
net/mac80211/rx.c | 20 ++++++++++++-----
11 files changed, 65 insertions(+), 52 deletions(-)
diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c
index b9e976ddcbbf..911e40178234 100644
--- a/drivers/net/wireless/ath/ath11k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath11k/dp_rx.c
@@ -2500,7 +2500,7 @@ static void ath11k_dp_rx_deliver_msdu(struct ath11k *ar, struct napi_struct *nap
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
rx_status->flag |= RX_FLAG_8023;
- ieee80211_rx_napi(ar->hw, pubsta, msdu, napi);
+ ieee80211_rx_napi(ar->hw, &pubsta->deflink, msdu, napi);
}
static int ath11k_dp_rx_process_msdu(struct ath11k *ar,
diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c
index 737287a9aa46..71f14f2e15bd 100644
--- a/drivers/net/wireless/ath/ath12k/dp_mon.c
+++ b/drivers/net/wireless/ath/ath12k/dp_mon.c
@@ -508,7 +508,7 @@ 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 ieee80211_link_sta *link_pubsta = NULL;
struct ath12k_dp_link_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct hal_rx_desc_data rx_info;
@@ -517,8 +517,6 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
struct hal_rx_desc *rx_desc = (struct hal_rx_desc *)msdu->data;
u8 addr[ETH_ALEN] = {};
- status->link_valid = 0;
-
if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) &&
!(status->flag & RX_FLAG_SKIP_MONITOR)) {
he = skb_push(msdu, sizeof(known));
@@ -532,12 +530,11 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
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;
- }
+ if (peer->sta->valid_links)
+ link_pubsta =
+ rcu_dereference(peer->sta->link[peer->link_id]);
+ else
+ link_pubsta = &peer->sta->deflink;
}
spin_unlock_bh(&dp->dp_lock);
@@ -583,7 +580,8 @@ void ath12k_dp_mon_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev,
!(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);
+ ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+ napi);
}
EXPORT_SYMBOL(ath12k_dp_mon_rx_deliver_msdu);
diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c
index a32ee9f8061a..49b161c5a878 100644
--- a/drivers/net/wireless/ath/ath12k/dp_rx.c
+++ b/drivers/net/wireless/ath/ath12k/dp_rx.c
@@ -1329,6 +1329,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
struct ath12k_dp *dp = dp_pdev->dp;
struct ieee80211_rx_status *rx_status;
struct ieee80211_sta *pubsta;
+ struct ieee80211_link_sta *link_pubsta = NULL;
struct ath12k_dp_peer *peer;
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
struct ieee80211_rx_status *status = rx_info->rx_status;
@@ -1340,9 +1341,14 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
pubsta = peer ? peer->sta : NULL;
- if (pubsta && pubsta->valid_links) {
- status->link_valid = 1;
- status->link_id = peer->hw_links[rxcb->hw_link_id];
+ if (pubsta) {
+ if (pubsta->valid_links) {
+ u8 link_id = peer->hw_links[rxcb->hw_link_id];
+ link_pubsta =
+ rcu_dereference(pubsta->link[link_id]);
+ } else {
+ link_pubsta = &pubsta->deflink;
+ }
}
ath12k_dbg(dp->ab, ATH12K_DBG_DATA,
@@ -1388,7 +1394,8 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
!(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);
+ ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), link_pubsta, msdu,
+ napi);
}
EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index de2feeb74009..2a12ae412bfd 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -131,7 +131,7 @@ void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
return;
}
- ieee80211_rx_napi(mld->hw, link_sta->sta, skb, napi);
+ ieee80211_rx_napi(mld->hw, link_sta, skb, napi);
}
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
@@ -1765,11 +1765,6 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
rx_status = IEEE80211_SKB_RXCB(skb);
- if (link_sta && sta->valid_links) {
- rx_status->link_valid = true;
- rx_status->link_id = link_sta->link_id;
- }
-
/* fill checksum */
if (ieee80211_is_data(hdr->frame_control) &&
pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) {
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
index d0c0faae0122..a83bede06487 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c
@@ -90,7 +90,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
fraglen, rxb->truesize);
}
- ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+ ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
}
/*
diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
index 7f0b4f5daa21..fe5a2d0a798b 100644
--- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
+++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2012-2014, 2018-2025 Intel Corporation
+ * Copyright (C) 2012-2014, 2018-2026 Intel Corporation
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
* Copyright (C) 2015-2017 Intel Deutschland GmbH
*/
@@ -243,7 +243,7 @@ static void iwl_mvm_pass_packet_to_mac80211(struct iwl_mvm *mvm,
return;
}
- ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+ ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
}
static bool iwl_mvm_used_average_energy(struct iwl_mvm *mvm,
@@ -2528,7 +2528,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi,
}
rcu_read_lock();
- ieee80211_rx_napi(mvm->hw, sta, skb, napi);
+ ieee80211_rx_napi(mvm->hw, &sta->deflink, skb, napi);
rcu_read_unlock();
}
diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c
index 75772979f438..a0b887c83591 100644
--- a/drivers/net/wireless/mediatek/mt76/mac80211.c
+++ b/drivers/net/wireless/mediatek/mt76/mac80211.c
@@ -1232,7 +1232,7 @@ EXPORT_SYMBOL(mt76_rx_signal);
static void
mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
struct ieee80211_hw **hw,
- struct ieee80211_sta **sta)
+ struct ieee80211_link_sta **link_sta)
{
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = mt76_skb_get_hdr(skb);
@@ -1279,11 +1279,15 @@ mt76_rx_convert(struct mt76_dev *dev, struct sk_buff *skb,
sizeof(mstat.chain_signal));
if (mstat.wcid) {
- status->link_valid = mstat.wcid->link_valid;
- status->link_id = mstat.wcid->link_id;
+ struct ieee80211_sta *sta = wcid_to_sta(mstat.wcid);
+
+ if (mstat.wcid->link_valid)
+ *link_sta =
+ rcu_dereference(sta->link[mstat.wcid->link_id]);
+ else
+ *link_sta = &sta->deflink;
}
- *sta = wcid_to_sta(mstat.wcid);
*hw = mt76_phy_hw(dev, mstat.phy_idx);
}
@@ -1507,7 +1511,7 @@ mt76_check_sta(struct mt76_dev *dev, struct sk_buff *skb)
void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
struct napi_struct *napi)
{
- struct ieee80211_sta *sta;
+ struct ieee80211_link_sta *link_sta;
struct ieee80211_hw *hw;
struct sk_buff *skb, *tmp;
LIST_HEAD(list);
@@ -1518,8 +1522,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
mt76_check_ccmp_pn(skb);
skb_shinfo(skb)->frag_list = NULL;
- mt76_rx_convert(dev, skb, &hw, &sta);
- ieee80211_rx_list(hw, sta, skb, &list);
+ mt76_rx_convert(dev, skb, &hw, &link_sta);
+ ieee80211_rx_list(hw, link_sta, skb, &list);
/* subsequent amsdu frames */
while (nskb) {
@@ -1527,8 +1531,8 @@ void mt76_rx_complete(struct mt76_dev *dev, struct sk_buff_head *frames,
nskb = nskb->next;
skb->next = NULL;
- mt76_rx_convert(dev, skb, &hw, &sta);
- ieee80211_rx_list(hw, sta, skb, &list);
+ mt76_rx_convert(dev, skb, &hw, &link_sta);
+ ieee80211_rx_list(hw, link_sta, skb, &list);
}
}
spin_unlock(&dev->rx_lock);
diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c
index 6e77522bcd8f..f6bcf309936d 100644
--- a/drivers/net/wireless/realtek/rtw89/core.c
+++ b/drivers/net/wireless/realtek/rtw89/core.c
@@ -2953,7 +2953,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat;
struct rtw89_rx_desc_info *desc_info = iter_data->desc_info;
struct sk_buff *skb = iter_data->skb;
- struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu;
bool is_mld = ieee80211_vif_is_mld(vif);
@@ -2988,11 +2987,6 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac,
if (!ether_addr_equal(target_bssid, bssid))
goto out;
- if (is_mld) {
- rx_status->link_valid = true;
- rx_status->link_id = rtwvif_link->link_id;
- }
-
if (ieee80211_is_beacon(hdr->frame_control)) {
if (vif->type == NL80211_IFTYPE_STATION &&
!test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) {
diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 4d9f5f87e814..7d529aa129f8 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -1755,9 +1755,6 @@ static void mac80211_hwsim_rx(struct mac80211_hwsim_data *data,
sp->active_links_rx &= ~BIT(link_id);
else
sp->active_links_rx |= BIT(link_id);
-
- rx_status->link_valid = true;
- rx_status->link_id = link_id;
}
rcu_read_unlock();
}
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 7f9d96939a4e..4d9dbd35369b 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -5207,14 +5207,18 @@ void ieee80211_restart_hw(struct ieee80211_hw *hw);
* mixed for a single hardware. Must not run concurrently with
* ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
*
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
* This function must be called with BHs disabled and RCU read lock
*
* @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
* @skb: the buffer to receive, owned by mac80211 after this call
* @list: the destination list
*/
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+ struct ieee80211_link_sta *link_sta,
struct sk_buff *skb, struct list_head *list);
/**
@@ -5232,14 +5236,18 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
* mixed for a single hardware. Must not run concurrently with
* ieee80211_tx_status_skb() or ieee80211_tx_status_ni().
*
+ * For data frames, when hardware has done address translation, a link station
+ * has to be provided and the frequency information may be skipped.
+ *
* This function must be called with BHs disabled.
*
* @hw: the hardware this frame came in on
- * @sta: the station the frame was received from, or %NULL
+ * @link_sta: the link station the data frame was received from, or %NULL
* @skb: the buffer to receive, owned by mac80211 after this call
* @napi: the NAPI context
*/
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+ struct ieee80211_link_sta *link_sta,
struct sk_buff *skb, struct napi_struct *napi);
/**
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 11d6c56c9d7e..4098f63ec824 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -5432,7 +5432,8 @@ static void __ieee80211_rx_handle_packet(struct ieee80211_hw *hw,
* This is the receive path handler. It is called by a low level driver when an
* 802.11 MPDU is received from the hardware.
*/
-void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_list(struct ieee80211_hw *hw,
+ struct ieee80211_link_sta *link_pubsta,
struct sk_buff *skb, struct list_head *list)
{
struct ieee80211_local *local = hw_to_local(hw);
@@ -5440,6 +5441,7 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
struct ieee80211_supported_band *sband;
struct ieee80211_rx_status *status = IEEE80211_SKB_RXCB(skb);
struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data;
+ struct ieee80211_sta *pubsta;
WARN_ON_ONCE(softirq_count() == 0);
@@ -5562,8 +5564,15 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
}
}
- if (WARN_ON_ONCE(status->link_id >= IEEE80211_LINK_UNSPECIFIED))
- goto drop;
+ /* FIXME: Emulate the old driver behaviour for now */
+ if (link_pubsta) {
+ status->link_valid = 1;
+ status->link_id = link_pubsta->link_id;
+ pubsta = link_pubsta->sta;
+ } else {
+ status->link_valid = 0;
+ pubsta = NULL;
+ }
status->rx_flags = 0;
@@ -5595,7 +5604,8 @@ void ieee80211_rx_list(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
}
EXPORT_SYMBOL(ieee80211_rx_list);
-void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
+void ieee80211_rx_napi(struct ieee80211_hw *hw,
+ struct ieee80211_link_sta *link_pubsta,
struct sk_buff *skb, struct napi_struct *napi)
{
struct sk_buff *tmp;
@@ -5608,7 +5618,7 @@ void ieee80211_rx_napi(struct ieee80211_hw *hw, struct ieee80211_sta *pubsta,
* receive processing
*/
rcu_read_lock();
- ieee80211_rx_list(hw, pubsta, skb, &list);
+ ieee80211_rx_list(hw, link_pubsta, skb, &list);
rcu_read_unlock();
if (!napi) {
--
2.53.0
^ permalink raw reply related
* [RFC PATCH v2 1/8] wifi: iwlwifi: use link_sta internally to the driver
From: Benjamin Berg @ 2026-02-23 12:38 UTC (permalink / raw)
To: linux-wireless; +Cc: Rameshkumar Sundaram, Ramasamy Kaliappan, Benjamin Berg
In-Reply-To: <20260223123818.384184-10-benjamin@sipsolutions.net>
From: Benjamin Berg <benjamin.berg@intel.com>
Using the link_sta is a natural way to pass both the STA and the link ID
information at the same time. Use that internally to the driver in
preparation to mac80211 changing its API and adopting the same method.
Signed-off-by: Benjamin Berg <benjamin.berg@intel.com>
---
drivers/net/wireless/intel/iwlwifi/mld/agg.c | 21 +++++----
drivers/net/wireless/intel/iwlwifi/mld/agg.h | 4 +-
drivers/net/wireless/intel/iwlwifi/mld/rx.c | 45 ++++++++++---------
drivers/net/wireless/intel/iwlwifi/mld/rx.h | 2 +-
.../wireless/intel/iwlwifi/mld/tests/agg.c | 7 +--
5 files changed, 42 insertions(+), 37 deletions(-)
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
index 3bf36f8f6874..c4f3552bd2ab 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c
@@ -1,13 +1,14 @@
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include "agg.h"
#include "sta.h"
#include "hcmd.h"
static void
-iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
+iwl_mld_reorder_release_frames(struct iwl_mld *mld,
+ struct ieee80211_link_sta *link_sta,
struct napi_struct *napi,
struct iwl_mld_baid_data *baid_data,
struct iwl_mld_reorder_buffer *reorder_buf,
@@ -32,7 +33,7 @@ iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta,
while ((skb = __skb_dequeue(skb_list))) {
iwl_mld_pass_packet_to_mac80211(mld, napi, skb,
reorder_buf->queue,
- sta);
+ link_sta);
reorder_buf->num_stored--;
}
}
@@ -71,7 +72,7 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
reorder_buf = &ba_data->reorder_buf[queue];
- iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data,
+ iwl_mld_reorder_release_frames(mld, link_sta, napi, ba_data,
reorder_buf, nssn);
out_unlock:
rcu_read_unlock();
@@ -174,7 +175,7 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
reorder_buf = &ba_data->reorder_buf[queue];
/* release all frames that are in the reorder buffer to the stack */
- iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL,
+ iwl_mld_reorder_release_frames(mld, link_sta, NULL,
ba_data, reorder_buf,
ieee80211_sn_add(reorder_buf->head_sn,
ba_data->buf_size));
@@ -187,14 +188,14 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
*/
enum iwl_mld_reorder_result
iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
- int queue, struct ieee80211_sta *sta,
+ int queue, struct ieee80211_link_sta *link_sta,
struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc)
{
struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
struct iwl_mld_baid_data *baid_data;
struct iwl_mld_reorder_buffer *buffer;
struct iwl_mld_reorder_buf_entry *entries;
- struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
+ struct iwl_mld_sta *mld_sta;
struct iwl_mld_link_sta *mld_link_sta;
u32 reorder = le32_to_cpu(desc->reorder_data);
bool amsdu, last_subframe, is_old_sn, is_dup;
@@ -217,10 +218,12 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
return IWL_MLD_PASS_SKB;
/* no sta yet */
- if (WARN_ONCE(!sta,
+ if (WARN_ONCE(!link_sta,
"Got valid BAID without a valid station assigned\n"))
return IWL_MLD_PASS_SKB;
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
+
/* not a data packet */
if (!ieee80211_is_data_qos(hdr->frame_control) ||
is_multicast_ether_addr(hdr->addr1))
@@ -310,7 +313,7 @@ iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
* will be released when the frame release notification arrives.
*/
if (!amsdu || last_subframe)
- iwl_mld_reorder_release_frames(mld, sta, napi, baid_data,
+ iwl_mld_reorder_release_frames(mld, link_sta, napi, baid_data,
buffer, nssn);
else if (buffer->num_stored == 1)
buffer->head_sn = nssn;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
index 651c80d1c7cd..c6cd5fa219be 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/agg.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h
@@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
/*
- * Copyright (C) 2024 Intel Corporation
+ * Copyright (C) 2024, 2026 Intel Corporation
*/
#ifndef __iwl_agg_h__
#define __iwl_agg_h__
@@ -106,7 +106,7 @@ int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta,
enum iwl_mld_reorder_result
iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi,
- int queue, struct ieee80211_sta *sta,
+ int queue, struct ieee80211_link_sta *link_sta,
struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc);
void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
index 214dcfde2fb4..de2feeb74009 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c
@@ -48,7 +48,8 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
}
static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
- int queue, struct ieee80211_sta *sta)
+ int queue,
+ struct ieee80211_link_sta *link_sta)
{
struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb);
struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb);
@@ -72,13 +73,13 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
return 0;
/* if we are here - this for sure is either CCMP or GCMP */
- if (!sta) {
+ if (!link_sta) {
IWL_DEBUG_DROP(mld,
"expected hw-decrypted unicast frame for station\n");
return -1;
}
- mld_sta = iwl_mld_sta_from_mac80211(sta);
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control);
keyidx = extiv[3] >> 6;
@@ -120,17 +121,17 @@ static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb,
void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
struct napi_struct *napi,
struct sk_buff *skb, int queue,
- struct ieee80211_sta *sta)
+ struct ieee80211_link_sta *link_sta)
{
KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211,
- mld, napi, skb, queue, sta);
+ mld, napi, skb, queue, link_sta);
- if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) {
+ if (unlikely(iwl_mld_check_pn(mld, skb, queue, link_sta))) {
kfree_skb(skb);
return;
}
- ieee80211_rx_napi(mld->hw, sta, skb, napi);
+ ieee80211_rx_napi(mld->hw, link_sta->sta, skb, napi);
}
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211);
@@ -1728,7 +1729,7 @@ static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid)
* Sets *drop to true if the packet should be dropped.
* Returns the station if found, or NULL otherwise.
*/
-static struct ieee80211_sta *
+static struct ieee80211_link_sta *
iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
struct sk_buff *skb,
const struct iwl_rx_mpdu_desc *mpdu_desc,
@@ -1803,10 +1804,10 @@ iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr,
queue);
}
- return sta;
+ return link_sta;
}
-static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
+static int iwl_mld_rx_mgmt_prot(struct ieee80211_link_sta *link_sta,
struct ieee80211_hdr *hdr,
struct ieee80211_rx_status *rx_status,
u32 mpdu_status,
@@ -1820,7 +1821,6 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
struct ieee80211_key_conf *key;
const u8 *frame = (void *)hdr;
const u8 *mmie;
- u8 link_id;
if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) ==
IWL_RX_MPDU_STATUS_SEC_NONE)
@@ -1836,10 +1836,10 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
if (!ieee80211_is_beacon(hdr->frame_control))
return 0;
- if (!sta)
+ if (!link_sta)
return -1;
- mld_sta = iwl_mld_sta_from_mac80211(sta);
+ mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta);
mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
/* key mismatch - will also report !MIC_OK but we shouldn't count it */
@@ -1853,8 +1853,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
return 0;
}
- link_id = rx_status->link_valid ? rx_status->link_id : 0;
- link = rcu_dereference(mld_vif->link[link_id]);
+ link = rcu_dereference(mld_vif->link[link_sta->link_id]);
if (WARN_ON_ONCE(!link))
return -1;
@@ -1905,7 +1904,7 @@ static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta,
}
static int iwl_mld_rx_crypto(struct iwl_mld *mld,
- struct ieee80211_sta *sta,
+ struct ieee80211_link_sta *link_sta,
struct ieee80211_hdr *hdr,
struct ieee80211_rx_status *rx_status,
struct iwl_rx_mpdu_desc *desc, int queue,
@@ -1915,7 +1914,7 @@ static int iwl_mld_rx_crypto(struct iwl_mld *mld,
if (unlikely(ieee80211_is_mgmt(hdr->frame_control) &&
!ieee80211_has_protected(hdr->frame_control)))
- return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status,
+ return iwl_mld_rx_mgmt_prot(link_sta, hdr, rx_status, status,
le16_to_cpu(desc->mpdu_len));
if (!ieee80211_has_protected(hdr->frame_control) ||
@@ -2023,7 +2022,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
struct iwl_rx_packet *pkt = rxb_addr(rxb);
struct iwl_mld_rx_phy_data phy_data = {};
struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data;
- struct ieee80211_sta *sta;
+ struct ieee80211_link_sta *link_sta;
struct ieee80211_hdr *hdr;
struct sk_buff *skb;
size_t mpdu_desc_size = sizeof(*mpdu_desc);
@@ -2086,7 +2085,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
rcu_read_lock();
- sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop);
+ link_sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue,
+ &drop);
if (drop)
goto drop;
@@ -2127,7 +2127,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
iwl_mld_rx_fill_status(mld, link_id, hdr, skb, &phy_data);
- if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue,
+ if (iwl_mld_rx_crypto(mld, link_sta, hdr, rx_status, mpdu_desc, queue,
le32_to_cpu(pkt->len_n_flags), &crypto_len))
goto drop;
@@ -2140,7 +2140,8 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2))
goto out;
- reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc);
+ reorder_res = iwl_mld_reorder(mld, napi, queue, link_sta, skb,
+ mpdu_desc);
switch (reorder_res) {
case IWL_MLD_PASS_SKB:
break;
@@ -2153,7 +2154,7 @@ void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi,
goto drop;
}
- iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta);
+ iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, link_sta);
goto out;
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
index 09dddbd40f55..a54f1a6146ee 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/rx.h
+++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h
@@ -64,7 +64,7 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
struct napi_struct *napi,
struct sk_buff *skb, int queue,
- struct ieee80211_sta *sta);
+ struct ieee80211_link_sta *link_sta);
void iwl_mld_handle_phy_air_sniffer_notif(struct iwl_mld *mld,
struct napi_struct *napi,
diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
index 29b0248cec3d..e9efe2996f07 100644
--- a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
+++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c
@@ -2,7 +2,7 @@
/*
* KUnit tests for channel helper functions
*
- * Copyright (C) 2024-2025 Intel Corporation
+ * Copyright (C) 2024-2026 Intel Corporation
*/
#include <kunit/test.h>
#include <kunit/static_stub.h>
@@ -441,7 +441,7 @@ static void
fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
struct napi_struct *napi,
struct sk_buff *skb, int queue,
- struct ieee80211_sta *sta)
+ struct ieee80211_link_sta *link_sta)
{
__skb_queue_tail(&g_released_skbs, skb);
g_num_released_skbs++;
@@ -630,7 +630,8 @@ static void test_reorder_buffer(struct kunit *test)
mpdu_desc = setup_mpdu_desc();
rcu_read_lock();
- reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc);
+ reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, &sta->deflink, skb,
+ mpdu_desc);
rcu_read_unlock();
KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res);
--
2.53.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox