Linux wireless drivers development
 help / color / mirror / Atom feed
* Re: [PATCH v5 5/9] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-15 13:06 UTC (permalink / raw)
  To: Loic Poulain
  Cc: Bartosz Golaszewski, linux-mmc, devicetree, linux-kernel,
	linux-arm-msm, linux-block, linux-wireless, ath10k,
	linux-bluetooth, netdev, daniel, Ulf Hansson, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Bjorn Andersson, Konrad Dybcio,
	Jens Axboe, Johannes Berg, Jeff Johnson, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <CAFEp6-0oxBEdfH-fqhdM18pt4JewLwrMOON9qpQgLFh8KS0hDg@mail.gmail.com>

On Mon, 15 Jun 2026 11:33:22 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> On Mon, Jun 15, 2026 at 11:28 AM Loic Poulain
> <loic.poulain@oss.qualcomm.com> wrote:
>>
>
> Also we cannot safely return -EPROBE_DEFER from add_disk_final()
> either. The NVMEM registration point is late in the sequence, too much
> has already happened to easily unwind. The easiest is that the NVMEM
> simply won't be available if registration fails, which looks
> acceptable?
>

I'd argue that it's a problem with subsystem code then as unwinding should
work fine no matter the point in the sequence when it's initiated but I guess
this isn't really an issue in your patches.

I suppose we shouldn't typically run into probe deferral here so I'm fine just
ignoring the return value.

Bart

^ permalink raw reply

* [PATCH 5.15/6.1/6.6/6.12] wifi: iwlwifi: fix 22000 series SMEM parsing
From: Nazar Kalashnikov @ 2026-06-15 12:20 UTC (permalink / raw)
  To: stable, Greg Kroah-Hartman
  Cc: Nazar Kalashnikov, Miri Korenblit, Kalle Valo, Johannes Berg,
	linux-wireless, linux-kernel, Gregory Greenman, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, netdev, Luca Coelho,
	Kalle Valo, lvc-project

From: Johannes Berg <johannes.berg@intel.com>

commit 58192b9ce09b0f0f86e2036683bd542130b91a98 upstream.

If the firmware were to report three LMACs (which doesn't
exist in hardware) then using "fwrt->smem_cfg.lmac[2]" is
an overrun of the array. Reject such and use IWL_FW_CHECK
instead of WARN_ON in this function.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Miri Korenblit <miriam.rachel.korenblit@intel.com>
Link: https://patch.msgid.link/20251110150012.16e8c2d70c26.Iadfcc1aedf43c5175b3f0757bea5aa232454f1ac@changeid
Signed-off-by: Nazar Kalashnikov <sivartiwe@gmail.com>
---
Backport fix for CVE-2026-43172
 drivers/net/wireless/intel/iwlwifi/fw/smem.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c
index 3f1272014daf..e2fc2f5de442 100644
--- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c
+++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c
@@ -6,6 +6,7 @@
  */
 #include "iwl-drv.h"
 #include "runtime.h"
+#include "dbg.h"
 #include "fw/api/commands.h"
 
 static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
@@ -17,7 +18,9 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
 	u8 api_ver = iwl_fw_lookup_notif_ver(fwrt->fw, SYSTEM_GROUP,
 					     SHARED_MEM_CFG_CMD, 0);
 
-	if (WARN_ON(lmac_num > ARRAY_SIZE(mem_cfg->lmac_smem)))
+	/* Note: notification has 3 entries, but we only expect 2 */
+	if (IWL_FW_CHECK(fwrt, lmac_num > ARRAY_SIZE(fwrt->smem_cfg.lmac),
+			 "FW advertises %d LMACs\n", lmac_num))
 		return;
 
 	fwrt->smem_cfg.num_lmacs = lmac_num;
@@ -26,7 +29,8 @@ static void iwl_parse_shared_mem_22000(struct iwl_fw_runtime *fwrt,
 	fwrt->smem_cfg.rxfifo2_size = le32_to_cpu(mem_cfg->rxfifo2_size);
 
 	if (api_ver >= 4 &&
-	    !WARN_ON_ONCE(iwl_rx_packet_payload_len(pkt) < sizeof(*mem_cfg))) {
+	    !IWL_FW_CHECK(fwrt, iwl_rx_packet_payload_len(pkt) < sizeof(*mem_cfg),
+			  "bad shared mem notification size\n")) {
 		fwrt->smem_cfg.rxfifo2_control_size =
 			le32_to_cpu(mem_cfg->rxfifo2_control_size);
 	}
-- 
2.47.3

^ permalink raw reply related

* [PATCH v2] ath12k: fix NULL pointer dereference in rhash table destroy
From: Jose Ignacio Tornos Martinez @ 2026-06-15 11:21 UTC (permalink / raw)
  To: jjohnson
  Cc: linux-wireless, ath12k, linux-kernel,
	Jose Ignacio Tornos Martinez, stable

When unbinding the ath12k driver, kernel NULL pointer dereferences
occur in irq_work_sync() called from rhashtable_destroy().

Two hash tables are affected:
1. ath12k_link_sta hash table in ath12k_base
2. ath12k_dp_link_peer hash table in ath12k_dp

The issue happens because the destroy functions are called unconditionally
in cleanup paths, but the hash tables are only initialized late in their
respective init functions. If the device was never fully started or if the
init functions failed before initializing the hash tables, the pointers
will be NULL. The issues are always reproducible from a VM because the MSI
addressing initialization is failing.

Call trace for ath12k_link_sta_rhash_tbl_destroy:
 RIP: irq_work_sync+0x1e/0x70
 rhashtable_destroy+0x12/0x60
 ath12k_link_sta_rhash_tbl_destroy+0x19/0x40 [ath12k]
 ath12k_core_stop+0xe/0x80 [ath12k]
 ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k]
 ath12k_pci_remove+0x60/0x110 [ath12k]

Call trace for ath12k_dp_link_peer_rhash_tbl_destroy:
 RIP: irq_work_sync+0x1e/0x70
 rhashtable_destroy+0x12/0x60
 ath12k_dp_link_peer_rhash_tbl_destroy+0x29/0x50 [ath12k]
 ath12k_dp_cmn_device_deinit+0x21/0x140 [ath12k]
 ath12k_core_hw_group_cleanup+0x6b/0xb0 [ath12k]
 ath12k_pci_remove+0x60/0x110 [ath12k]

Fix this by adding NULL checks before calling rhashtable_destroy() in
both destroy functions.

The NULL check approach was chosen because the rhashtable pointer
serves as the initialization state indicator. The init can fail at
various points, leaving some components uninitialized. Checking the
pointer directly is simpler than adding separate state flags that
would need synchronization.

Fixes: 57ccca410237 ("wifi: ath12k: Add hash table for ath12k_link_sta in ath12k_base")
Fixes: a88cf5f71adf ("wifi: ath12k: Add hash table for ath12k_dp_link_peer")
Cc: stable@vger.kernel.org
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v2: - Use guard(mutex) instead of manual mutex_lock/unlock with goto in
    ath12k_dp_link_peer_rhash_tbl_destroy (suggested by Jeff Johnson)
    - Add rationale paragraph in commit message explaining why NULL check
    approach was chosen over state flags
v1: https://lore.kernel.org/all/20260604071032.659009-1-jtornosm@redhat.com/ 

 drivers/net/wireless/ath/ath12k/dp_peer.c | 7 +++++--
 drivers/net/wireless/ath/ath12k/peer.c    | 3 +++
 2 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index a1100782d45e..ab3e3e107782 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -274,11 +274,14 @@ int ath12k_dp_link_peer_rhash_tbl_init(struct ath12k_dp *dp)
 
 void ath12k_dp_link_peer_rhash_tbl_destroy(struct ath12k_dp *dp)
 {
-	mutex_lock(&dp->link_peer_rhash_tbl_lock);
+	guard(mutex)(&dp->link_peer_rhash_tbl_lock);
+
+	if (!dp->rhead_peer_addr)
+		return;
+
 	rhashtable_destroy(dp->rhead_peer_addr);
 	kfree(dp->rhead_peer_addr);
 	dp->rhead_peer_addr = NULL;
-	mutex_unlock(&dp->link_peer_rhash_tbl_lock);
 }
 
 static int ath12k_dp_link_peer_rhash_insert(struct ath12k_dp *dp,
diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c
index 2e875176baaa..80fee2ce68f1 100644
--- a/drivers/net/wireless/ath/ath12k/peer.c
+++ b/drivers/net/wireless/ath/ath12k/peer.c
@@ -444,6 +444,9 @@ int ath12k_link_sta_rhash_tbl_init(struct ath12k_base *ab)
 
 void ath12k_link_sta_rhash_tbl_destroy(struct ath12k_base *ab)
 {
+	if (!ab->rhead_sta_addr)
+		return;
+
 	rhashtable_destroy(ab->rhead_sta_addr);
 	kfree(ab->rhead_sta_addr);
 	ab->rhead_sta_addr = NULL;
-- 
2.54.0


^ permalink raw reply related

* Re: [PATCH v2 1/1] wifi: mac80211_hwsim: skip sta_add rc update on sub-20 chanctx
From: Lachlan Hodges @ 2026-06-15 10:56 UTC (permalink / raw)
  To: meihaipeng
  Cc: Johannes Berg, Andrei Otcheretianski, linux-wireless,
	linux-kernel, syzbot+c0472dd80bb8f668625f
In-Reply-To: <20260615100321.22208-1-meihaipeng@uniontech.com>

On Mon, Jun 15, 2026 at 06:03:21PM +0800, meihaipeng wrote:
> mac80211_hwsim_sta_add() eagerly calls mac80211_hwsim_sta_rc_update()
> for a new STA.
> 
> The warning that syzbot hits comes from that add-time call. For sub-20
> MHz channel contexts, ieee80211_sta_rx_bandwidth cannot represent the
> configured width, so the synthetic sta_add()-time rc_update() trips a
> false warning before any real rc_update() path is involved.
> 
> Do the same thing that was done for S1G: keep the rc_update() logic
> itself unchanged and simply skip the sta_add()-time call when any active
> link uses a sub-20 MHz channel context.

FYI 5/10MHz channels have already been removed and the S1G case
has been handled. These changes are just not yet in Linus' tree.

lachlan

^ permalink raw reply

* Re: [PATCH 05/10] [v2] mips: select legacy gpiolib interfaces where used
From: Thomas Bogendoerfer @ 2026-06-15 10:33 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-gpio, linux-kernel, Arnd Bergmann, Christian Lamparter,
	Johannes Berg, Aaro Koskinen, Andreas Kemnade, Kevin Hilman,
	Roger Quadros, Tony Lindgren, John Paul Adrian Glaubitz,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Lee Jones, Pavel Machek, Matti Vaittinen,
	Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-wireless, linux-omap, linux-arm-kernel, linux-mips,
	linux-sh, linux-input, linux-leds, netdev, Bartosz Golaszewski
In-Reply-To: <20260520183815.2510387-6-arnd@kernel.org>

On Wed, May 20, 2026 at 08:38:10PM +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> A few old machines have not been converted away from the old-style
> gpiolib interfaces. Make these select the new CONFIG_GPIOLIB_LEGACY
> symbol so the code still works where it is needed but can be left
> out otherwise.
> 
> This is the list of all gpio_request() calls in mips:
> 
>   arch/mips/alchemy/devboards/db1000.c:           gpio_request(19, "sd0_cd");
>   arch/mips/alchemy/devboards/db1000.c:           gpio_request(20, "sd1_cd");
>   arch/mips/alchemy/devboards/db1200.c:   gpio_request(215, "otg-vbus");
>   arch/mips/bcm47xx/workarounds.c:        err = gpio_request_one(usb_power, GPIOF_OUT_INIT_HIGH, "usb_power");
>   arch/mips/bcm63xx/boards/board_bcm963xx.c:              gpio_request_one(board.ephy_reset_gpio,
>   arch/mips/txx9/rbtx4927/setup.c:        gpio_request(15, "sio-dtr");
> 
> Most of these should be easy enough to change to modern gpio descriptors
> or remove if they are no longer in use.
> 
> Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> v2: no changes. There was no discussion on this, but the patch
>     has so far not made it into the linux-mips tree, so I'm including
>     it for completeness.
> ---
>  arch/mips/Kconfig         | 5 +++++
>  arch/mips/alchemy/Kconfig | 1 -
>  arch/mips/txx9/Kconfig    | 1 +
>  3 files changed, 6 insertions(+), 1 deletion(-)

applied to mips-next

Thomas.

-- 
Crap can work. Given enough thrust pigs will fly, but it's not necessarily a
good idea.                                                [ RFC1925, 2.3 ]

^ permalink raw reply

* [PATCH v2 1/1] wifi: mac80211_hwsim: skip sta_add rc update on sub-20 chanctx
From: meihaipeng @ 2026-06-15 10:03 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Andrei Otcheretianski, linux-wireless, linux-kernel, meihaipeng,
	syzbot+c0472dd80bb8f668625f

mac80211_hwsim_sta_add() eagerly calls mac80211_hwsim_sta_rc_update()
for a new STA.

The warning that syzbot hits comes from that add-time call. For sub-20
MHz channel contexts, ieee80211_sta_rx_bandwidth cannot represent the
configured width, so the synthetic sta_add()-time rc_update() trips a
false warning before any real rc_update() path is involved.

Do the same thing that was done for S1G: keep the rc_update() logic
itself unchanged and simply skip the sta_add()-time call when any active
link uses a sub-20 MHz channel context.

Fixes: aea9a6088ae46 ("wifi: mac80211_hwsim: do rc update per link")
Reported-by: syzbot+c0472dd80bb8f668625f@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=c0472dd80bb8f668625f
Signed-off-by: meihaipeng <meihaipeng@uniontech.com>

---
 drivers/net/wireless/virtual/mac80211_hwsim.c | 47 ++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c
index 1fcf5d0d2e13..4640b5d6cfa3 100644
--- a/drivers/net/wireless/virtual/mac80211_hwsim.c
+++ b/drivers/net/wireless/virtual/mac80211_hwsim.c
@@ -1022,6 +1022,46 @@ static int hwsim_get_chanwidth(enum nl80211_chan_width bw)
 	return INT_MAX;
 }
 
+static bool hwsim_sta_uses_sub20_chanctx(struct ieee80211_hw *hw,
+					 struct ieee80211_vif *vif,
+					 struct ieee80211_sta *sta)
+{
+	struct mac80211_hwsim_data *data = hw->priv;
+	int link_id;
+
+	rcu_read_lock();
+	for (link_id = 0; link_id < ARRAY_SIZE(sta->link); link_id++) {
+		struct ieee80211_link_sta *link_sta;
+		enum nl80211_chan_width confbw = data->bw;
+		struct ieee80211_bss_conf *vif_conf;
+		struct ieee80211_chanctx_conf *chanctx_conf;
+
+		link_sta = rcu_dereference(sta->link[link_id]);
+		if (!link_sta)
+			continue;
+
+		if (data->use_chanctx) {
+			vif_conf = rcu_dereference(vif->link_conf[link_id]);
+			if (!vif_conf)
+				continue;
+
+			chanctx_conf = rcu_dereference(vif_conf->chanctx_conf);
+			if (!chanctx_conf)
+				continue;
+
+			confbw = chanctx_conf->def.width;
+		}
+
+		if (hwsim_get_chanwidth(confbw) < 20) {
+			rcu_read_unlock();
+			return true;
+		}
+	}
+	rcu_read_unlock();
+
+	return false;
+}
+
 static void mac80211_hwsim_tx_frame(struct ieee80211_hw *hw,
 				    struct sk_buff *skb,
 				    struct ieee80211_channel *chan);
@@ -2846,7 +2886,12 @@ static int mac80211_hwsim_sta_add(struct ieee80211_hw *hw,
 
 	hwsim_check_magic(vif);
 	hwsim_set_sta_magic(sta);
-	mac80211_hwsim_sta_rc_update(hw, vif, &sta->deflink, 0);
+	/*
+	 * Like S1G, sub-20 widths aren't handled by this add-time
+	 * rc_update() call.
+	 */
+	if (!hwsim_sta_uses_sub20_chanctx(hw, vif, sta))
+		mac80211_hwsim_sta_rc_update(hw, vif, &sta->deflink, 0);
 
 	if (sta->valid_links) {
 		WARN(hweight16(sta->valid_links) > 1,
-- 
2.20.1


^ permalink raw reply related

* Re: [PATCH wireless-next v1] wifi: mwifiex: fix permanently busy scans after multiple roam iterations
From: Francesco Dolcini @ 2026-06-15  9:43 UTC (permalink / raw)
  To: Jeff Chen
  Cc: Francesco Dolcini, Rafael Beims, Brian Norris, Rafael Beims,
	linux-wireless, linux-kernel, stable
In-Reply-To: <ai/G/C7VxxzDVWXW@nxpwireless-Inspiron-14-Plus-7440>

On Mon, Jun 15, 2026 at 05:33:48PM +0800, Jeff Chen wrote:
> On Fri, Jun 12, 2026 at 03:06:28 PM +0200, Francesco Dolcini wrote:
> > On Fri, Jun 12, 2026 at 09:25:46AM -0300, Rafael Beims wrote:
> > > From: Rafael Beims <rafael.beims@toradex.com>
> > > 
> > > In order for the firmware to sleep, the driver has to confirm a
> > > previously received sleep request. The normal sequence of evets goes
> > > like this:
> > > EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm
> > > -> SLEEP -> EVENT_AWAKE -> AWAKE.
> > > Before sending the sleep-confirm command, the driver must make sure
> > > there are no commands either running or waiting to be completed.
> > > 
> > > mwifiex_ret_802_11_associate() unconditionally sets
> > > ps_state = PS_STATE_AWAKE when it processes the association command
> > > response, outside of the normal powersave management flow. If
> > > EVENT_SLEEP arrives while the association command is in flight,
> > > ps_state is PRE_SLEEP when the association command response is parsed,
> > > and the forced AWAKE overwrites it. The deferred sleep-confirm is
> > > never sent.
...
> > 
> > Jeff: same issue in the nxpwifi driver.
> > 
> I looked into this for nxpwifi. The nxpwifi driver currently only
> supports IW61x.

Up to you, but maybe is worth applying the same change there. I assume
you are going to add support for more Wi-Fi chips over time, and you do
not want to have to debug this issue again.

Francesco


^ permalink raw reply

* Re: [PATCH v5 5/9] block: implement NVMEM provider
From: Loic Poulain @ 2026-06-15  9:33 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
	Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan
In-Reply-To: <CAFEp6-0qsqhcwnSjm3=bG21jsCktzn5-L5sk2pNTZcGuVXaiNA@mail.gmail.com>

On Mon, Jun 15, 2026 at 11:28 AM Loic Poulain
<loic.poulain@oss.qualcomm.com> wrote:
>
> On Mon, Jun 15, 2026 at 10:53 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
> >
> > On Fri, 12 Jun 2026 15:20:57 +0200, Loic Poulain
> > <loic.poulain@oss.qualcomm.com> said:
> > > From: Daniel Golle <daniel@makrotopia.org>
> > >
> > > On embedded devices using an eMMC it is common that one or more partitions
> > > on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> > > data. Allow referencing the partition in device tree for the kernel and
> > > Wi-Fi drivers accessing it via the NVMEM layer.
> > >
> > > For now, NVMEM is only registered for the whole disk block device, as the
> > > OF node is currently only associated to it.
> > >
> > > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > > Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > > Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > > ---
> > >  block/Kconfig             |   9 ++++
> > >  block/Makefile            |   1 +
> > >  block/blk-nvmem.c         | 109 ++++++++++++++++++++++++++++++++++++++++++++++
> > >  block/blk.h               |   8 ++++
> > >  block/genhd.c             |   4 ++
> > >  include/linux/blk_types.h |   3 ++
> > >  include/linux/blkdev.h    |   1 +
> > >  7 files changed, 135 insertions(+)
> > >
> > > diff --git a/block/Kconfig b/block/Kconfig
> > > index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> > > --- a/block/Kconfig
> > > +++ b/block/Kconfig
> > > @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> > >         by falling back to the kernel crypto API when inline
> > >         encryption hardware is not present.
> > >
> > > +config BLK_NVMEM
> > > +     bool "Block device NVMEM provider"
> > > +     depends on OF
> > > +     depends on NVMEM
> > > +     help
> > > +       Allow block devices (or partitions) to act as NVMEM providers,
> > > +       typically used with eMMC to store MAC addresses or Wi-Fi
> > > +       calibration data on embedded devices.
> > > +
> > >  source "block/partitions/Kconfig"
> > >
> > >  config BLK_PM
> > > diff --git a/block/Makefile b/block/Makefile
> > > index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> > > --- a/block/Makefile
> > > +++ b/block/Makefile
> > > @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> > >                                          blk-crypto-sysfs.o
> > >  obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> > >  obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED)        += holder.o
> > > +obj-$(CONFIG_BLK_NVMEM)                += blk-nvmem.o
> > > diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> > > new file mode 100644
> > > index 0000000000000000000000000000000000000000..c005f059d9fe56242ebaef9905673dff902b5686
> > > --- /dev/null
> > > +++ b/block/blk-nvmem.c
> > > @@ -0,0 +1,109 @@
> > > +// SPDX-License-Identifier: GPL-2.0-or-later
> > > +/*
> > > + * block device NVMEM provider
> > > + *
> > > + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> > > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > > + *
> > > + * Useful on devices using a partition on an eMMC for MAC addresses or
> > > + * Wi-Fi calibration EEPROM data.
> > > + */
> > > +
> > > +#include <linux/file.h>
> > > +#include <linux/nvmem-provider.h>
> > > +#include <linux/nvmem-consumer.h>
> > > +#include <linux/of.h>
> > > +#include <linux/pagemap.h>
> > > +#include <linux/property.h>
> > > +
> > > +#include "blk.h"
> > > +
> > > +static int blk_nvmem_reg_read(void *priv, unsigned int from, void *val, size_t bytes)
> > > +{
> > > +     blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> > > +     dev_t devt = (dev_t)(uintptr_t)priv;
> > > +     size_t bytes_left = bytes;
> > > +     loff_t pos = from;
> > > +     int ret = 0;
> > > +
> > > +     struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> > > +     if (IS_ERR(bdev_file))
> > > +             return PTR_ERR(bdev_file);
> > > +
> > > +     while (bytes_left) {
> > > +             pgoff_t f_index = pos >> PAGE_SHIFT;
> > > +             struct folio *folio;
> > > +             size_t folio_off;
> > > +             size_t to_read;
> > > +
> > > +             folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> > > +             if (IS_ERR(folio)) {
> > > +                     ret = PTR_ERR(folio);
> > > +                     break;
> > > +             }
> > > +
> > > +             folio_off = offset_in_folio(folio, pos);
> > > +             to_read = min(bytes_left, folio_size(folio) - folio_off);
> > > +             memcpy_from_folio(val, folio, folio_off, to_read);
> > > +             pos += to_read;
> > > +             bytes_left -= to_read;
> > > +             val += to_read;
> > > +             folio_put(folio);
> > > +     }
> > > +
> > > +     return ret;
> > > +}
> > > +
> > > +void blk_nvmem_add(struct block_device *bdev)
> > > +{
> > > +     struct device *dev = &bdev->bd_device;
> > > +     struct nvmem_config config = {};
> > > +
> > > +     /* skip devices which do not have a device tree node */
> > > +     if (!dev_of_node(dev))
> > > +             return;
> > > +
> > > +     /* skip devices without an nvmem layout defined */
> > > +     struct device_node *child __free(device_node) =
> > > +             of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> > > +     if (!child)
> > > +             return;
> > > +
> > > +     /*
> > > +      * skip block device too large to be represented as NVMEM devices,
> > > +      * the NVMEM reg_read callback uses an unsigned int offset
> > > +      */
> > > +     if (bdev_nr_bytes(bdev) > UINT_MAX) {
> > > +             dev_warn(dev, "block device too large to be an NVMEM provider\n");
> > > +             return;
> > > +     }
> > > +
> > > +     config.id = NVMEM_DEVID_NONE;
> > > +     config.dev = dev;
> > > +     config.name = dev_name(dev);
> > > +     config.owner = THIS_MODULE;
> > > +     config.priv = (void *)(uintptr_t)dev->devt;
> > > +     config.reg_read = blk_nvmem_reg_read;
> > > +     config.size = bdev_nr_bytes(bdev);
> > > +     config.word_size = 1;
> > > +     config.stride = 1;
> > > +     config.read_only = true;
> > > +     config.root_only = true;
> > > +     config.ignore_wp = true;
> > > +     config.of_node = to_of_node(dev->fwnode);
> > > +
> > > +     bdev->bd_nvmem = nvmem_register(&config);
> > > +     if (IS_ERR(bdev->bd_nvmem)) {
> > > +             dev_err_probe(dev, PTR_ERR(bdev->bd_nvmem),
> > > +                           "Failed to register NVMEM device\n");
> >
> > Using dev_err_probe() only makes sense with a return value. Which makes me
> > think: we won't retry this after a probe deferral. I think we should return
>
> Yes, so here with the nvmem fixed-layout, there is no way to get a
> deferred probe error, but better to be ready to handle this anyway.
>
> > int from this function just for this use-case. Also: if we *do* have
> > a layout, shouldn't we treat a failure to register the nvmem provider as
> > a an error and propagate it up the stack?
>
> From an API perspective we should indeed return the error. From block
> core, Do we want to fail the entire disk addition just because the
> 'companion' NVMEM provider couldn't be registered, or should we only
> abort/return in case of EPROBE_DEFER?

Also we cannot safely return -EPROBE_DEFER from add_disk_final()
either. The NVMEM registration point is late in the sequence, too much
has already happened to easily unwind. The easiest is that the NVMEM
simply won't be available if registration fails, which looks
acceptable?

>
> >
> > > +             bdev->bd_nvmem = NULL;
> > > +     }
> > > +}
> > > +
> > > +void blk_nvmem_del(struct block_device *bdev)
> > > +{
> > > +     if (bdev->bd_nvmem)
> >
> > Nvmem core already performs a NULL check.
>
> Ok, thanks!
>
>
> >
> > > +             nvmem_unregister(bdev->bd_nvmem);
> > > +
> > > +     bdev->bd_nvmem = NULL;
> > > +}
> > > diff --git a/block/blk.h b/block/blk.h
> > > index ec4674cdf2ead4fd259ff5fc42401f591e684ee9..cd3c7ca723391c40be56f1dd4810e641b7c8a2b3 100644
> > > --- a/block/blk.h
> > > +++ b/block/blk.h
> > > @@ -757,4 +757,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q,
> > >       memalloc_noio_restore(memflags);
> > >  }
> > >
> > > +#ifdef CONFIG_BLK_NVMEM
> > > +void blk_nvmem_add(struct block_device *bdev);
> > > +void blk_nvmem_del(struct block_device *bdev);
> > > +#else
> > > +static inline void blk_nvmem_add(struct block_device *bdev) {}
> > > +static inline void blk_nvmem_del(struct block_device *bdev) {}
> > > +#endif
> > > +
> > >  #endif /* BLK_INTERNAL_H */
> > > diff --git a/block/genhd.c b/block/genhd.c
> > > index 7d6854fd28e95ae9134309679a7c6a937f5b7db8..1b2382de6fb30c1e5f60f45c04dc03ed3bf5d5f2 100644
> > > --- a/block/genhd.c
> > > +++ b/block/genhd.c
> > > @@ -421,6 +421,8 @@ static void add_disk_final(struct gendisk *disk)
> > >                */
> > >               dev_set_uevent_suppress(ddev, 0);
> > >               disk_uevent(disk, KOBJ_ADD);
> > > +
> > > +             blk_nvmem_add(disk->part0);
> > >       }
> > >
> > >       blk_apply_bdi_limits(disk->bdi, &disk->queue->limits);
> > > @@ -704,6 +706,8 @@ static void __del_gendisk(struct gendisk *disk)
> > >
> > >       disk_del_events(disk);
> > >
> > > +     blk_nvmem_del(disk->part0);
> > > +
> > >       /*
> > >        * Prevent new openers by unlinked the bdev inode.
> > >        */
> > > diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> > > index 8808ee76e73c09e0ceaac41ba59e86fb0c4efc64..ace6f59b860d0813665b2f62a1c03a1f4be94059 100644
> > > --- a/include/linux/blk_types.h
> > > +++ b/include/linux/blk_types.h
> > > @@ -73,6 +73,9 @@ struct block_device {
> > >       int                     bd_writers;
> > >  #ifdef CONFIG_SECURITY
> > >       void                    *bd_security;
> > > +#endif
> > > +#ifdef CONFIG_BLK_NVMEM
> > > +     struct nvmem_device     *bd_nvmem;
> > >  #endif
> > >       /*
> > >        * keep this out-of-line as it's both big and not needed in the fast
> > > diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> > > index 890128cdea1ce66863c5baa36f3b336ec4550807..f15d2b5bf9e4fd2368b8a70416a978e22c0d4333 100644
> > > --- a/include/linux/blkdev.h
> > > +++ b/include/linux/blkdev.h
> > > @@ -30,6 +30,7 @@
> > >
> > >  struct module;
> > >  struct request_queue;
> > > +struct nvmem_device;
> > >  struct elevator_queue;
> > >  struct blk_trace;
> > >  struct request;
> > >
> > > --
> > > 2.34.1
> > >
> > >
> >
> > I like this approach better than the previous one.
> >
> > Thanks,
> > Bartosz

^ permalink raw reply

* Re: [PATCH wireless-next v1] wifi: mwifiex: fix permanently busy scans after multiple roam iterations
From: Jeff Chen @ 2026-06-15  9:33 UTC (permalink / raw)
  To: Francesco Dolcini
  Cc: Rafael Beims, Brian Norris, Rafael Beims, linux-wireless,
	linux-kernel, stable
In-Reply-To: <20260612130607.GA7651@francesco-nb>

On Fri, Jun 12, 2026 at 03:06:28 PM +0200, Francesco Dolcini wrote:
> On Fri, Jun 12, 2026 at 09:25:46AM -0300, Rafael Beims wrote:
> > From: Rafael Beims <rafael.beims@toradex.com>
> > 
> > In order for the firmware to sleep, the driver has to confirm a
> > previously received sleep request. The normal sequence of evets goes
> > like this:
> > EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm
> > -> SLEEP -> EVENT_AWAKE -> AWAKE.
> > Before sending the sleep-confirm command, the driver must make sure
> > there are no commands either running or waiting to be completed.
> > 
> > mwifiex_ret_802_11_associate() unconditionally sets
> > ps_state = PS_STATE_AWAKE when it processes the association command
> > response, outside of the normal powersave management flow. If
> > EVENT_SLEEP arrives while the association command is in flight,
> > ps_state is PRE_SLEEP when the association command response is parsed,
> > and the forced AWAKE overwrites it. The deferred sleep-confirm is
> > never sent.
> 
> Brian / Jeff: I am not that familiar with this part of the code, it
> sounds correct to me (and I discussed it with Rafael before he did send
> the patch), maybe you can also have a look.
> 
> Jeff: same issue in the nxpwifi driver.
> 
> Francesco
> 

Hi Francesco,

Thanks for the heads up.

I looked into this for nxpwifi. The nxpwifi driver currently only
supports IW61x. Based on our analysis of the IW61x firmware source code,
the firmware stops the auto deep sleep idle timer upon receiving any host
command and restarts it only after the command response is sent,
preventing EVENT_SLEEP from being issued while the association command is
in flight. This means the race condition described in the patch should
not be triggered with IW61x.

Jeff

^ permalink raw reply

* Re: [PATCH wireless-next v1] wifi: mwifiex: fix permanently busy scans after multiple roam iterations
From: Francesco Dolcini @ 2026-06-15  9:30 UTC (permalink / raw)
  To: Jeff Chen
  Cc: Rafael Beims, Brian Norris, Francesco Dolcini, Rafael Beims,
	linux-wireless, linux-kernel, stable
In-Reply-To: <ai+tqAb/kCkwbh7l@nxpwireless-Inspiron-14-Plus-7440>

On Mon, Jun 15, 2026 at 03:45:44PM +0800, Jeff Chen wrote:
> On Fri, Jun 12, 2026 at 09:25:46 AM -0300, Rafael Beims wrote:
> > From: Rafael Beims <rafael.beims@toradex.com>
> > 
> > 
> > After testing on both IW412 and W8997, I could only trigger the bug on
> 
> I don't think there's an IW412 in NXP's lineup — did you mean IW416?

Yes, I can confirm it is IW416.

The commit message should be fixed.

Francesco


^ permalink raw reply

* Re: [PATCH v5 5/9] block: implement NVMEM provider
From: Loic Poulain @ 2026-06-15  9:28 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Marcel Holtmann, Luiz Augusto von Dentz,
	Balakrishna Godavarthi, Rocky Liao, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Srinivas Kandagatla,
	Andrew Lunn, Heiner Kallweit, Russell King, Saravana Kannan
In-Reply-To: <CAMRc=McQkLnz2OS2RREAbcrsp47cL-W3bCduq8LwPBBUcVNyJw@mail.gmail.com>

On Mon, Jun 15, 2026 at 10:53 AM Bartosz Golaszewski <brgl@kernel.org> wrote:
>
> On Fri, 12 Jun 2026 15:20:57 +0200, Loic Poulain
> <loic.poulain@oss.qualcomm.com> said:
> > From: Daniel Golle <daniel@makrotopia.org>
> >
> > On embedded devices using an eMMC it is common that one or more partitions
> > on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> > data. Allow referencing the partition in device tree for the kernel and
> > Wi-Fi drivers accessing it via the NVMEM layer.
> >
> > For now, NVMEM is only registered for the whole disk block device, as the
> > OF node is currently only associated to it.
> >
> > Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> > Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> > ---
> >  block/Kconfig             |   9 ++++
> >  block/Makefile            |   1 +
> >  block/blk-nvmem.c         | 109 ++++++++++++++++++++++++++++++++++++++++++++++
> >  block/blk.h               |   8 ++++
> >  block/genhd.c             |   4 ++
> >  include/linux/blk_types.h |   3 ++
> >  include/linux/blkdev.h    |   1 +
> >  7 files changed, 135 insertions(+)
> >
> > diff --git a/block/Kconfig b/block/Kconfig
> > index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> > --- a/block/Kconfig
> > +++ b/block/Kconfig
> > @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
> >         by falling back to the kernel crypto API when inline
> >         encryption hardware is not present.
> >
> > +config BLK_NVMEM
> > +     bool "Block device NVMEM provider"
> > +     depends on OF
> > +     depends on NVMEM
> > +     help
> > +       Allow block devices (or partitions) to act as NVMEM providers,
> > +       typically used with eMMC to store MAC addresses or Wi-Fi
> > +       calibration data on embedded devices.
> > +
> >  source "block/partitions/Kconfig"
> >
> >  config BLK_PM
> > diff --git a/block/Makefile b/block/Makefile
> > index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> > --- a/block/Makefile
> > +++ b/block/Makefile
> > @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION) += blk-crypto.o blk-crypto-profile.o \
> >                                          blk-crypto-sysfs.o
> >  obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK) += blk-crypto-fallback.o
> >  obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED)        += holder.o
> > +obj-$(CONFIG_BLK_NVMEM)                += blk-nvmem.o
> > diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> > new file mode 100644
> > index 0000000000000000000000000000000000000000..c005f059d9fe56242ebaef9905673dff902b5686
> > --- /dev/null
> > +++ b/block/blk-nvmem.c
> > @@ -0,0 +1,109 @@
> > +// SPDX-License-Identifier: GPL-2.0-or-later
> > +/*
> > + * block device NVMEM provider
> > + *
> > + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> > + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> > + *
> > + * Useful on devices using a partition on an eMMC for MAC addresses or
> > + * Wi-Fi calibration EEPROM data.
> > + */
> > +
> > +#include <linux/file.h>
> > +#include <linux/nvmem-provider.h>
> > +#include <linux/nvmem-consumer.h>
> > +#include <linux/of.h>
> > +#include <linux/pagemap.h>
> > +#include <linux/property.h>
> > +
> > +#include "blk.h"
> > +
> > +static int blk_nvmem_reg_read(void *priv, unsigned int from, void *val, size_t bytes)
> > +{
> > +     blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> > +     dev_t devt = (dev_t)(uintptr_t)priv;
> > +     size_t bytes_left = bytes;
> > +     loff_t pos = from;
> > +     int ret = 0;
> > +
> > +     struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> > +     if (IS_ERR(bdev_file))
> > +             return PTR_ERR(bdev_file);
> > +
> > +     while (bytes_left) {
> > +             pgoff_t f_index = pos >> PAGE_SHIFT;
> > +             struct folio *folio;
> > +             size_t folio_off;
> > +             size_t to_read;
> > +
> > +             folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> > +             if (IS_ERR(folio)) {
> > +                     ret = PTR_ERR(folio);
> > +                     break;
> > +             }
> > +
> > +             folio_off = offset_in_folio(folio, pos);
> > +             to_read = min(bytes_left, folio_size(folio) - folio_off);
> > +             memcpy_from_folio(val, folio, folio_off, to_read);
> > +             pos += to_read;
> > +             bytes_left -= to_read;
> > +             val += to_read;
> > +             folio_put(folio);
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +void blk_nvmem_add(struct block_device *bdev)
> > +{
> > +     struct device *dev = &bdev->bd_device;
> > +     struct nvmem_config config = {};
> > +
> > +     /* skip devices which do not have a device tree node */
> > +     if (!dev_of_node(dev))
> > +             return;
> > +
> > +     /* skip devices without an nvmem layout defined */
> > +     struct device_node *child __free(device_node) =
> > +             of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> > +     if (!child)
> > +             return;
> > +
> > +     /*
> > +      * skip block device too large to be represented as NVMEM devices,
> > +      * the NVMEM reg_read callback uses an unsigned int offset
> > +      */
> > +     if (bdev_nr_bytes(bdev) > UINT_MAX) {
> > +             dev_warn(dev, "block device too large to be an NVMEM provider\n");
> > +             return;
> > +     }
> > +
> > +     config.id = NVMEM_DEVID_NONE;
> > +     config.dev = dev;
> > +     config.name = dev_name(dev);
> > +     config.owner = THIS_MODULE;
> > +     config.priv = (void *)(uintptr_t)dev->devt;
> > +     config.reg_read = blk_nvmem_reg_read;
> > +     config.size = bdev_nr_bytes(bdev);
> > +     config.word_size = 1;
> > +     config.stride = 1;
> > +     config.read_only = true;
> > +     config.root_only = true;
> > +     config.ignore_wp = true;
> > +     config.of_node = to_of_node(dev->fwnode);
> > +
> > +     bdev->bd_nvmem = nvmem_register(&config);
> > +     if (IS_ERR(bdev->bd_nvmem)) {
> > +             dev_err_probe(dev, PTR_ERR(bdev->bd_nvmem),
> > +                           "Failed to register NVMEM device\n");
>
> Using dev_err_probe() only makes sense with a return value. Which makes me
> think: we won't retry this after a probe deferral. I think we should return

Yes, so here with the nvmem fixed-layout, there is no way to get a
deferred probe error, but better to be ready to handle this anyway.

> int from this function just for this use-case. Also: if we *do* have
> a layout, shouldn't we treat a failure to register the nvmem provider as
> a an error and propagate it up the stack?

From an API perspective we should indeed return the error. From block
core, Do we want to fail the entire disk addition just because the
'companion' NVMEM provider couldn't be registered, or should we only
abort/return in case of EPROBE_DEFER?

>
> > +             bdev->bd_nvmem = NULL;
> > +     }
> > +}
> > +
> > +void blk_nvmem_del(struct block_device *bdev)
> > +{
> > +     if (bdev->bd_nvmem)
>
> Nvmem core already performs a NULL check.

Ok, thanks!


>
> > +             nvmem_unregister(bdev->bd_nvmem);
> > +
> > +     bdev->bd_nvmem = NULL;
> > +}
> > diff --git a/block/blk.h b/block/blk.h
> > index ec4674cdf2ead4fd259ff5fc42401f591e684ee9..cd3c7ca723391c40be56f1dd4810e641b7c8a2b3 100644
> > --- a/block/blk.h
> > +++ b/block/blk.h
> > @@ -757,4 +757,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q,
> >       memalloc_noio_restore(memflags);
> >  }
> >
> > +#ifdef CONFIG_BLK_NVMEM
> > +void blk_nvmem_add(struct block_device *bdev);
> > +void blk_nvmem_del(struct block_device *bdev);
> > +#else
> > +static inline void blk_nvmem_add(struct block_device *bdev) {}
> > +static inline void blk_nvmem_del(struct block_device *bdev) {}
> > +#endif
> > +
> >  #endif /* BLK_INTERNAL_H */
> > diff --git a/block/genhd.c b/block/genhd.c
> > index 7d6854fd28e95ae9134309679a7c6a937f5b7db8..1b2382de6fb30c1e5f60f45c04dc03ed3bf5d5f2 100644
> > --- a/block/genhd.c
> > +++ b/block/genhd.c
> > @@ -421,6 +421,8 @@ static void add_disk_final(struct gendisk *disk)
> >                */
> >               dev_set_uevent_suppress(ddev, 0);
> >               disk_uevent(disk, KOBJ_ADD);
> > +
> > +             blk_nvmem_add(disk->part0);
> >       }
> >
> >       blk_apply_bdi_limits(disk->bdi, &disk->queue->limits);
> > @@ -704,6 +706,8 @@ static void __del_gendisk(struct gendisk *disk)
> >
> >       disk_del_events(disk);
> >
> > +     blk_nvmem_del(disk->part0);
> > +
> >       /*
> >        * Prevent new openers by unlinked the bdev inode.
> >        */
> > diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> > index 8808ee76e73c09e0ceaac41ba59e86fb0c4efc64..ace6f59b860d0813665b2f62a1c03a1f4be94059 100644
> > --- a/include/linux/blk_types.h
> > +++ b/include/linux/blk_types.h
> > @@ -73,6 +73,9 @@ struct block_device {
> >       int                     bd_writers;
> >  #ifdef CONFIG_SECURITY
> >       void                    *bd_security;
> > +#endif
> > +#ifdef CONFIG_BLK_NVMEM
> > +     struct nvmem_device     *bd_nvmem;
> >  #endif
> >       /*
> >        * keep this out-of-line as it's both big and not needed in the fast
> > diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> > index 890128cdea1ce66863c5baa36f3b336ec4550807..f15d2b5bf9e4fd2368b8a70416a978e22c0d4333 100644
> > --- a/include/linux/blkdev.h
> > +++ b/include/linux/blkdev.h
> > @@ -30,6 +30,7 @@
> >
> >  struct module;
> >  struct request_queue;
> > +struct nvmem_device;
> >  struct elevator_queue;
> >  struct blk_trace;
> >  struct request;
> >
> > --
> > 2.34.1
> >
> >
>
> I like this approach better than the previous one.
>
> Thanks,
> Bartosz

^ permalink raw reply

* Re: [PATCH v5 5/9] block: implement NVMEM provider
From: Bartosz Golaszewski @ 2026-06-15  8:53 UTC (permalink / raw)
  To: Loic Poulain
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <20260612-block-as-nvmem-v5-5-95e0b30fff90@oss.qualcomm.com>

On Fri, 12 Jun 2026 15:20:57 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> From: Daniel Golle <daniel@makrotopia.org>
>
> On embedded devices using an eMMC it is common that one or more partitions
> on the eMMC are used to store MAC addresses and Wi-Fi calibration EEPROM
> data. Allow referencing the partition in device tree for the kernel and
> Wi-Fi drivers accessing it via the NVMEM layer.
>
> For now, NVMEM is only registered for the whole disk block device, as the
> OF node is currently only associated to it.
>
> Signed-off-by: Daniel Golle <daniel@makrotopia.org>
> Co-developed-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---
>  block/Kconfig             |   9 ++++
>  block/Makefile            |   1 +
>  block/blk-nvmem.c         | 109 ++++++++++++++++++++++++++++++++++++++++++++++
>  block/blk.h               |   8 ++++
>  block/genhd.c             |   4 ++
>  include/linux/blk_types.h |   3 ++
>  include/linux/blkdev.h    |   1 +
>  7 files changed, 135 insertions(+)
>
> diff --git a/block/Kconfig b/block/Kconfig
> index 15027963472d7b40e27b9097a5993c457b5b3054..0b33747e16dc33473683706f75c92bdf8b648f7c 100644
> --- a/block/Kconfig
> +++ b/block/Kconfig
> @@ -209,6 +209,15 @@ config BLK_INLINE_ENCRYPTION_FALLBACK
>  	  by falling back to the kernel crypto API when inline
>  	  encryption hardware is not present.
>
> +config BLK_NVMEM
> +	bool "Block device NVMEM provider"
> +	depends on OF
> +	depends on NVMEM
> +	help
> +	  Allow block devices (or partitions) to act as NVMEM providers,
> +	  typically used with eMMC to store MAC addresses or Wi-Fi
> +	  calibration data on embedded devices.
> +
>  source "block/partitions/Kconfig"
>
>  config BLK_PM
> diff --git a/block/Makefile b/block/Makefile
> index 7dce2e44276c4274c11a0a61121c83d9c43d6e0c..d7ac389e71902bc091a8800ea266190a43b3e63d 100644
> --- a/block/Makefile
> +++ b/block/Makefile
> @@ -36,3 +36,4 @@ obj-$(CONFIG_BLK_INLINE_ENCRYPTION)	+= blk-crypto.o blk-crypto-profile.o \
>  					   blk-crypto-sysfs.o
>  obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK)	+= blk-crypto-fallback.o
>  obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED)	+= holder.o
> +obj-$(CONFIG_BLK_NVMEM)                += blk-nvmem.o
> diff --git a/block/blk-nvmem.c b/block/blk-nvmem.c
> new file mode 100644
> index 0000000000000000000000000000000000000000..c005f059d9fe56242ebaef9905673dff902b5686
> --- /dev/null
> +++ b/block/blk-nvmem.c
> @@ -0,0 +1,109 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +/*
> + * block device NVMEM provider
> + *
> + * Copyright (c) 2024 Daniel Golle <daniel@makrotopia.org>
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + *
> + * Useful on devices using a partition on an eMMC for MAC addresses or
> + * Wi-Fi calibration EEPROM data.
> + */
> +
> +#include <linux/file.h>
> +#include <linux/nvmem-provider.h>
> +#include <linux/nvmem-consumer.h>
> +#include <linux/of.h>
> +#include <linux/pagemap.h>
> +#include <linux/property.h>
> +
> +#include "blk.h"
> +
> +static int blk_nvmem_reg_read(void *priv, unsigned int from, void *val, size_t bytes)
> +{
> +	blk_mode_t mode = BLK_OPEN_READ | BLK_OPEN_RESTRICT_WRITES;
> +	dev_t devt = (dev_t)(uintptr_t)priv;
> +	size_t bytes_left = bytes;
> +	loff_t pos = from;
> +	int ret = 0;
> +
> +	struct file *bdev_file __free(fput) = bdev_file_open_by_dev(devt, mode, priv, NULL);
> +	if (IS_ERR(bdev_file))
> +		return PTR_ERR(bdev_file);
> +
> +	while (bytes_left) {
> +		pgoff_t f_index = pos >> PAGE_SHIFT;
> +		struct folio *folio;
> +		size_t folio_off;
> +		size_t to_read;
> +
> +		folio = read_mapping_folio(bdev_file->f_mapping, f_index, NULL);
> +		if (IS_ERR(folio)) {
> +			ret = PTR_ERR(folio);
> +			break;
> +		}
> +
> +		folio_off = offset_in_folio(folio, pos);
> +		to_read = min(bytes_left, folio_size(folio) - folio_off);
> +		memcpy_from_folio(val, folio, folio_off, to_read);
> +		pos += to_read;
> +		bytes_left -= to_read;
> +		val += to_read;
> +		folio_put(folio);
> +	}
> +
> +	return ret;
> +}
> +
> +void blk_nvmem_add(struct block_device *bdev)
> +{
> +	struct device *dev = &bdev->bd_device;
> +	struct nvmem_config config = {};
> +
> +	/* skip devices which do not have a device tree node */
> +	if (!dev_of_node(dev))
> +		return;
> +
> +	/* skip devices without an nvmem layout defined */
> +	struct device_node *child __free(device_node) =
> +		of_get_child_by_name(dev_of_node(dev), "nvmem-layout");
> +	if (!child)
> +		return;
> +
> +	/*
> +	 * skip block device too large to be represented as NVMEM devices,
> +	 * the NVMEM reg_read callback uses an unsigned int offset
> +	 */
> +	if (bdev_nr_bytes(bdev) > UINT_MAX) {
> +		dev_warn(dev, "block device too large to be an NVMEM provider\n");
> +		return;
> +	}
> +
> +	config.id = NVMEM_DEVID_NONE;
> +	config.dev = dev;
> +	config.name = dev_name(dev);
> +	config.owner = THIS_MODULE;
> +	config.priv = (void *)(uintptr_t)dev->devt;
> +	config.reg_read = blk_nvmem_reg_read;
> +	config.size = bdev_nr_bytes(bdev);
> +	config.word_size = 1;
> +	config.stride = 1;
> +	config.read_only = true;
> +	config.root_only = true;
> +	config.ignore_wp = true;
> +	config.of_node = to_of_node(dev->fwnode);
> +
> +	bdev->bd_nvmem = nvmem_register(&config);
> +	if (IS_ERR(bdev->bd_nvmem)) {
> +		dev_err_probe(dev, PTR_ERR(bdev->bd_nvmem),
> +			      "Failed to register NVMEM device\n");

Using dev_err_probe() only makes sense with a return value. Which makes me
think: we won't retry this after a probe deferral. I think we should return
int from this function just for this use-case. Also: if we *do* have
a layout, shouldn't we treat a failure to register the nvmem provider as
a an error and propagate it up the stack?

> +		bdev->bd_nvmem = NULL;
> +	}
> +}
> +
> +void blk_nvmem_del(struct block_device *bdev)
> +{
> +	if (bdev->bd_nvmem)

Nvmem core already performs a NULL check.

> +		nvmem_unregister(bdev->bd_nvmem);
> +
> +	bdev->bd_nvmem = NULL;
> +}
> diff --git a/block/blk.h b/block/blk.h
> index ec4674cdf2ead4fd259ff5fc42401f591e684ee9..cd3c7ca723391c40be56f1dd4810e641b7c8a2b3 100644
> --- a/block/blk.h
> +++ b/block/blk.h
> @@ -757,4 +757,12 @@ static inline void blk_debugfs_unlock(struct request_queue *q,
>  	memalloc_noio_restore(memflags);
>  }
>
> +#ifdef CONFIG_BLK_NVMEM
> +void blk_nvmem_add(struct block_device *bdev);
> +void blk_nvmem_del(struct block_device *bdev);
> +#else
> +static inline void blk_nvmem_add(struct block_device *bdev) {}
> +static inline void blk_nvmem_del(struct block_device *bdev) {}
> +#endif
> +
>  #endif /* BLK_INTERNAL_H */
> diff --git a/block/genhd.c b/block/genhd.c
> index 7d6854fd28e95ae9134309679a7c6a937f5b7db8..1b2382de6fb30c1e5f60f45c04dc03ed3bf5d5f2 100644
> --- a/block/genhd.c
> +++ b/block/genhd.c
> @@ -421,6 +421,8 @@ static void add_disk_final(struct gendisk *disk)
>  		 */
>  		dev_set_uevent_suppress(ddev, 0);
>  		disk_uevent(disk, KOBJ_ADD);
> +
> +		blk_nvmem_add(disk->part0);
>  	}
>
>  	blk_apply_bdi_limits(disk->bdi, &disk->queue->limits);
> @@ -704,6 +706,8 @@ static void __del_gendisk(struct gendisk *disk)
>
>  	disk_del_events(disk);
>
> +	blk_nvmem_del(disk->part0);
> +
>  	/*
>  	 * Prevent new openers by unlinked the bdev inode.
>  	 */
> diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h
> index 8808ee76e73c09e0ceaac41ba59e86fb0c4efc64..ace6f59b860d0813665b2f62a1c03a1f4be94059 100644
> --- a/include/linux/blk_types.h
> +++ b/include/linux/blk_types.h
> @@ -73,6 +73,9 @@ struct block_device {
>  	int			bd_writers;
>  #ifdef CONFIG_SECURITY
>  	void			*bd_security;
> +#endif
> +#ifdef CONFIG_BLK_NVMEM
> +	struct nvmem_device	*bd_nvmem;
>  #endif
>  	/*
>  	 * keep this out-of-line as it's both big and not needed in the fast
> diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
> index 890128cdea1ce66863c5baa36f3b336ec4550807..f15d2b5bf9e4fd2368b8a70416a978e22c0d4333 100644
> --- a/include/linux/blkdev.h
> +++ b/include/linux/blkdev.h
> @@ -30,6 +30,7 @@
>
>  struct module;
>  struct request_queue;
> +struct nvmem_device;
>  struct elevator_queue;
>  struct blk_trace;
>  struct request;
>
> --
> 2.34.1
>
>

I like this approach better than the previous one.

Thanks,
Bartosz

^ permalink raw reply

* Re: [PATCH v5 1/9] block: partitions: of: Skip child nodes without reg property
From: Bartosz Golaszewski @ 2026-06-15  8:47 UTC (permalink / raw)
  To: Loic Poulain
  Cc: linux-mmc, devicetree, linux-kernel, linux-arm-msm, linux-block,
	linux-wireless, ath10k, linux-bluetooth, netdev, daniel,
	Ulf Hansson, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Bjorn Andersson, Konrad Dybcio, Jens Axboe, Johannes Berg,
	Jeff Johnson, Bartosz Golaszewski, Marcel Holtmann,
	Luiz Augusto von Dentz, Balakrishna Godavarthi, Rocky Liao,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Srinivas Kandagatla, Andrew Lunn, Heiner Kallweit,
	Russell King, Saravana Kannan
In-Reply-To: <20260612-block-as-nvmem-v5-1-95e0b30fff90@oss.qualcomm.com>

On Fri, 12 Jun 2026 15:20:53 +0200, Loic Poulain
<loic.poulain@oss.qualcomm.com> said:
> Child nodes of a fixed-partitions node are not necessarily partition
> entries, for example an nvmem-layout node has no reg property. The
> current code passes a NULL reg pointer and uninitialized len to the
> length check, which can result in a kernel panic or silent failure to
> register any partitions.
>
> Fix validate_of_partition() to return a skip indicator when no reg
> property is present. Guard add_of_partition() with a reg property
> check for the same reason.
>
> Signed-off-by: Loic Poulain <loic.poulain@oss.qualcomm.com>
> ---

I think this warrants a Cc: stable and backporting as well as a Fixes tag.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* [Feature Request] ath12k: Optimize s2idle wake latency / Implement Fast Resume for WCN785x
From: Paolo Ferraris @ 2026-06-15  8:37 UTC (permalink / raw)
  To: ath12k; +Cc: linux-wireless, kvalo

Hello ath12k maintainers and developers,
I am writing to respectfully request an optimization for the  ath12k
firmware/driver regarding the resume latency from  s2idle  on modern
Intel platforms.
Hardware Details:
  • Laptop: HP Spectre 16 (Intel Meteor Lake Ultra 7 155H)
  • Wi-Fi Module: Qualcomm WCN785x Wi-Fi 7 (FastConnect 7800)
  • Kernel: Linux 7.0 (Arch Linux)
The Issue
When the laptop resumes from  s2 idle  sleep, the WCN785x takes a
consistent 10 to 15 seconds to establish a connection. Based on  dmesg
 logs, it appears the firmware undergoes a full cold-boot sequence (
fw_version  is re-logged) followed by a lengthy passive regulatory
sweep of the channels before allowing Authentication

Diagnosis & Isolation:
To ensure this was not a motherboard ACPI issue or an Intel PCIe
dropout bug (like the  0xffffffff  ASPM bug seen on some Intel cards),
we physically swapped the Wifi module on this exact same motherboard
to a MediaTek MT7922 (Wi-Fi 6E). The MT7922 resumed and authenticated
almost instantly upon waking from  s2idle . This confirms that the HP
BIOS and the Linux kernel are handling the PCIe wake states correctly,
and the latency is strictly isolated to the  ath12k  firmware
initialization and scanning routine.

Feature Request:
Would it be possible for the Qualcomm firmware team to implement "Scan
Caching" or a "Fast Resume" state for  s2idle  in a future firmware
release? Ideally, allowing the chip to cache the connected AP's
channel before sleep to bypass the massive 15-second passive sweep
upon waking.
Thank you very much for your hard work on the ath12k driver!

Best regards,
Paolo Ferraris

^ permalink raw reply

* Re: [PATCH wireless-next v1] wifi: mwifiex: fix permanently busy scans after multiple roam iterations
From: Jeff Chen @ 2026-06-15  7:45 UTC (permalink / raw)
  To: Rafael Beims
  Cc: Brian Norris, Francesco Dolcini, Rafael Beims, linux-wireless,
	linux-kernel, stable
In-Reply-To: <20260612122547.1586872-2-rafael@beims.me>

On Fri, Jun 12, 2026 at 09:25:46 AM -0300, Rafael Beims wrote:
> From: Rafael Beims <rafael.beims@toradex.com>
> 
> 
> After testing on both IW412 and W8997, I could only trigger the bug on

I don't think there's an IW412 in NXP's lineup — did you mean IW416?

> the IW412 and observed the firmwares behave differently. On the IW412
> the firmware still sends EVENT_SLEEP while the authentication /
> association process is ongoing. A W8997 under the same
> conditions seems to suppress power-save for the duration of the
> association, so PRE_SLEEP never coincided with the association response
> even after extended periods of testing using the loops
> described below (>12hours).
> 

^ permalink raw reply

* [PATCH wireless-next] wifi: nl80211: clarify NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA content
From: Johannes Berg @ 2026-06-15  7:39 UTC (permalink / raw)
  To: linux-wireless; +Cc: Johannes Berg

From: Johannes Berg <johannes.berg@intel.com>

This is currently __le16, but really the whole content of the
corresponding 802.11 element, which is even extensible and
could, in theory, be increased in size. Clarify the docs.

Signed-off-by: Johannes Berg <johannes.berg@intel.com>
---
 include/uapi/linux/nl80211.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 9998f6c0a665..74593a151e14 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -4474,8 +4474,8 @@ enum nl80211_mpath_info {
  *     capabilities IE
  * @NL80211_BAND_IFTYPE_ATTR_HE_CAP_PPE: HE PPE thresholds information as
  *     defined in HE capabilities IE
- * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities (__le16),
- *	given for all 6 GHz band channels
+ * @NL80211_BAND_IFTYPE_ATTR_HE_6GHZ_CAPA: HE 6GHz band capabilities,
+ *	given for all 6 GHz band channels (binary, element content)
  * @NL80211_BAND_IFTYPE_ATTR_VENDOR_ELEMS: vendor element capabilities that are
  *	advertised on this band/for this iftype (binary)
  * @NL80211_BAND_IFTYPE_ATTR_EHT_CAP_MAC: EHT MAC capabilities as in EHT
-- 
2.53.0


^ permalink raw reply related

* Re: [PATCH v9] PCI: Add device-specific reset for Qualcomm devices
From: Jose Ignacio Tornos Martinez @ 2026-06-15  7:30 UTC (permalink / raw)
  To: helgaas, alex
  Cc: ath11k, ath12k, bhelgaas, jjohnson, jtornosm, linux-kernel,
	linux-pci, linux-wireless, mani, mhi
In-Reply-To: <20260612151749.GA603817@bhelgaas>

Hi Bjorn and Alex,

Bjorn's questions:

> I guess "bus reset" here refers to Secondary Bus Reset being asserted
> by the bridge upstream from these devices?  Seems a bit surprising if
> that doesn't work.  Or is it just that we can't use SBR because there
> are multiple devices below that bridge?

Yes, SBR. The devices I tested are alone on their bus (single device under
bridge), so it's a device-specific issue, not a topology problem. The
quirk_no_bus_reset patch addresses this for v7.2.

> I don't know enough about VFIO, but I sort of expected that VFIO would
> reset devices between reassignment regardless of how a VM terminates.
> I guess that's not true?

VFIO does attempt reset on every reassignment. Without a working reset method,
the attempt fails and the device remains in undefined state. With this quirk,
D3hot successfully resets the device allowing reassignment.

> Since the device claims to preserve internal state across D3hot->D0
> (and it sounds like at least BARs *are* preserved), is this a
> potential leak of state between VMs?  To play devil's advocate, how do
> we convince a customer that none of their data is ever leaked to a
> subsequent tenant using this device?

This is a valid concern. Testing shows device internals are reset despite
NoSoftRst+ (command register cleared, requires driver reinitialization),
though BARs are preserved. Given these devices have no other reset method,
this provides the only viable mechanism for VFIO reuse. We cannot improve
beyond what D3hot provides - the quirk works because despite advertising
NoSoftRst+, D3hot does clear sufficient internal state for clean
reinitialization.

> If we think this is a viable method, it seems like we should use
> pci_pm_reset(), which takes care of IOMMU and device readiness issues.
>
> We would have to change pci_pm_reset() to deal with the fact that
> PCI_PM_CTRL_NO_SOFT_RESET seems wrong on these devices.  Maybe we
> could cache PCI_PM_CTRL_NO_SOFT_RESET in pci_pm_init(), then override
> it with quirks for these devices?

I explored a similar idea in v2 (PCI_DEV_FLAGS_FORCE_PM_RESET to bypass
NoSoftRst+):
https://lore.kernel.org/linux-pci/20260508145153.717641-2-jtornosm@redhat.com/
(Note: v2 used driver names ath11k/ath12k instead of device-specific names
WCN6855/WCN7850, which Jeff Johnson later commented on in v7 feedback.)

Alex provided guidance on both approaches and indicated device-specific reset
seemed more appropriate here:

"Device specific resets are made for this scenario. Look at
pci_dev_specific_reset() and pci_dev_reset_methods[]. The supporting
evidence that this performs a worthwhile reset is still a bit weak, but
heuristically it seems better than nothing, which is what we're left
with otherwise. Reset via D3hot for a device that does not expose
NoSoftRst- is not something we should enable or endorse for any common
use case."

The device-specific approach keeps this quirk isolated to proven device IDs.
But I can revisit the pm quirk approach if you both prefer it.

Alex's suggestion:

> It would be better to extract the core of pci_pm_reset() to a
> pci_do_pm_reset() function that's used both here and by the
> pci_pm_reset() function.

Good point about the code duplication. In v9 I kept it as a self-contained
quirk to avoid modifying pci_pm_reset() and touching core pci.c code, trying
to minimize the change footprint. But I agree extracting a helper function
would be cleaner.

Once we confirm the preferred approach (device-specific vs pm quirk per
Bjorn's question above), I'll send v10 with the appropriate implementation
including the helper function if we proceed with the device-specific approach.

Thanks

Best regards
Jose Ignacio


^ permalink raw reply

* Re: [PATCH wireless-next v1] wifi: mwifiex: fix permanently busy scans after multiple roam iterations
From: Jeff Chen @ 2026-06-15  7:11 UTC (permalink / raw)
  To: Rafael Beims
  Cc: Brian Norris, Francesco Dolcini, Rafael Beims, linux-wireless,
	linux-kernel, stable
In-Reply-To: <20260612122547.1586872-2-rafael@beims.me>

On Fri, Jun 12, 2026 at 09:25:46 AM -0300, Rafael Beims wrote:
> From: Rafael Beims <rafael.beims@toradex.com>
> 
> In order for the firmware to sleep, the driver has to confirm a
> previously received sleep request. The normal sequence of evets goes
> like this:
> EVENT_SLEEP -> adapter->ps_state = PS_STATE_PRE_SLEEP -> sleep-confirm
> -> SLEEP -> EVENT_AWAKE -> AWAKE.
> Before sending the sleep-confirm command, the driver must make sure
> there are no commands either running or waiting to be completed.
> 
> mwifiex_ret_802_11_associate() unconditionally sets
> ps_state = PS_STATE_AWAKE when it processes the association command
> response, outside of the normal powersave management flow. If
> EVENT_SLEEP arrives while the association command is in flight,
> ps_state is PRE_SLEEP when the association command response is parsed,
> and the forced AWAKE overwrites it. The deferred sleep-confirm is
> never sent.
> 

Hi Rafael,
Thanks for the clear analysis.
Agreed with the fix — association response handling shouldn’t override ps_state.
For reference, the NXP downstream driver also doesn’t update ps_state in the
association response path, so this is consistent.

Reviewed-by: Jeff Chen <jeff.chen_1@nxp.com>

^ permalink raw reply

* Re: [PATCH 5/5] wifi: mt76: mt7927u: enable USB RX aggregation
From: Lorenzo Bianconi @ 2026-06-15  7:10 UTC (permalink / raw)
  To: Sean Wang; +Cc: Felix Fietkau, linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260613224655.2405686-6-sean.wang@kernel.org>

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

> From: Sean Wang <sean.wang@mediatek.com>
> 
> Enable USB RX aggregation on MT7927u with vendor driver parameters for
> alignment, padding and buffer size. According to the vendor driver, the
> hardware should run RX aggregation with USB SG disabled.
> 
> Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt7925/usb.c |  6 +++++-
>  drivers/net/wireless/mediatek/mt76/usb.c        | 11 +++++++++++
>  2 files changed, 16 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> index a0bfe6f09ae4..42d13bc6ebbc 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> @@ -5,7 +5,6 @@
>  #include <linux/module.h>
>  #include <linux/sizes.h>
>  #include <linux/usb.h>
> -
>  #include "mt7925.h"
>  #include "mcu.h"
>  #include "mac.h"
> @@ -235,6 +234,11 @@ static int mt7925u_probe(struct usb_interface *usb_intf,
>  		mdev->rev = (0x7927 << 16) | (mdev->rev & 0xff);
>  	}
>  
> +	if (is_mt7927(mdev))
> +		mt76u_enable_rx_aggr(mdev, MT7927_USB_RX_AGGR_ALIGN,
> +				     MT7927_USB_RX_AGGR_PADDING,
> +				     MT7927_USB_RX_AGGR_BUF_SIZE);

This seems to me something like a NETIF_F_GRO_HW. Should the user be able to
toggle it via ethtool?

Regards,
Lorenzo

> +
>  	if (mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY)) {
>  		ret = mt792xu_wfsys_reset(dev);
>  		if (ret)
> diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
> index 10ad2b024985..f0df510904c5 100644
> --- a/drivers/net/wireless/mediatek/mt76/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/usb.c
> @@ -1300,6 +1300,17 @@ static const struct mt76_queue_ops usb_queue_ops = {
>  	.kick = mt76u_tx_kick,
>  };
>  
> +void mt76u_enable_rx_aggr(struct mt76_dev *dev, int align, int padding,
> +			  int buf_size)
> +{
> +	dev->usb.sg_en = false;
> +	dev->usb.rx_aggr.enable = true;
> +	dev->usb.rx_aggr.align = align;
> +	dev->usb.rx_aggr.padding = padding;
> +	dev->usb.rx_aggr.buf_size = buf_size;
> +}
> +EXPORT_SYMBOL_GPL(mt76u_enable_rx_aggr);
> +
>  int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
>  		 struct mt76_bus_ops *ops)
>  {
> -- 
> 2.43.0
> 

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

^ permalink raw reply

* Re: [PATCH 3/5] wifi: mt76: usb: add optional RX aggregation support
From: Lorenzo Bianconi @ 2026-06-15  7:08 UTC (permalink / raw)
  To: Sean Wang; +Cc: Felix Fietkau, linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260613224655.2405686-4-sean.wang@kernel.org>

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

On Jun 13, Sean Wang wrote:
> From: Sean Wang <sean.wang@mediatek.com>
> 
> Add common USB RX aggregation support and let drivers opt in by programming
> the UDMA RX aggregation limit and timeout.
> 
> RX aggregation allows the device to pack multiple RX packets into one USB
> transfer, reducing URB completion rate, USB interrupt/IO overhead, and host
> RX scheduling pressure. This is especially useful at high throughput, where
> per-packet USB RX handling can become a CPU bottleneck.
> 
> Keep it disabled by default so existing USB drivers keep the current RX
> behavior unless they explicitly enable aggregation.
> 
> Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt76.h     |  21 ++-
>  .../net/wireless/mediatek/mt76/mt7925/usb.c   |  12 ++
>  .../net/wireless/mediatek/mt76/mt792x_usb.c   |  23 +++-
>  drivers/net/wireless/mediatek/mt76/usb.c      | 124 +++++++++++++++++-
>  4 files changed, 169 insertions(+), 11 deletions(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
> index 81740aa7df71..125c97dc1f28 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt76.h
> @@ -680,6 +680,13 @@ struct mt76_usb {
>  	void (*ctrl_timeout)(struct mt76_dev *dev, int err);
>  	bool sg_en;
>  
> +	struct {
> +		bool enable;
> +		int align;
> +		int padding;
> +		int buf_size;
> +	} rx_aggr;
> +
>  	struct mt76u_mcu {
>  		u8 *data;
>  		/* multiple reads */
> @@ -1857,6 +1864,17 @@ mt76u_bulk_msg(struct mt76_dev *dev, void *data, int len, int *actual_len,
>  	return usb_bulk_msg(udev, pipe, data, len, actual_len, timeout);
>  }
>  
> +static inline int
> +mt76u_rx_aggr_buf_size(int max_mpdu, int aggr_limit, int aggr_pkt_limit,
> +		       int padding)
> +{
> +	int aggr_size;
> +
> +	aggr_size = min(aggr_limit, aggr_pkt_limit * (max_mpdu + padding));
> +
> +	return PAGE_ALIGN(max_mpdu + aggr_size);
> +}
> +
>  void mt76_ethtool_page_pool_stats(struct mt76_dev *dev, u64 *data, int *index);
>  void mt76_ethtool_worker(struct mt76_ethtool_worker_info *wi,
>  			 struct mt76_sta_stats *stats, bool eht);
> @@ -1882,7 +1900,8 @@ void mt76u_stop_tx(struct mt76_dev *dev);
>  void mt76u_stop_rx(struct mt76_dev *dev);
>  int mt76u_resume_rx(struct mt76_dev *dev);
>  void mt76u_queues_deinit(struct mt76_dev *dev);
> -
> +void mt76u_enable_rx_aggr(struct mt76_dev *dev, int align, int padding,
> +			  int buf_size);
>  int mt76s_init(struct mt76_dev *dev, struct sdio_func *func,
>  	       const struct mt76_bus_ops *bus_ops);
>  int mt76s_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid);
> diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> index 49ad4fe9eb1b..a0bfe6f09ae4 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt7925/usb.c
> @@ -3,12 +3,24 @@
>  
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> +#include <linux/sizes.h>
>  #include <linux/usb.h>
>  
>  #include "mt7925.h"
>  #include "mcu.h"
>  #include "mac.h"
>  
> +#define MT7927_USB_RX_AGGR_ALIGN	16
> +#define MT7927_USB_RX_AGGR_PADDING	12
> +#define MT7927_USB_RX_AGGR_LIMIT	SZ_32K
> +#define MT7927_USB_RX_AGGR_PKT_LIMIT	30
> +#define MT7927_USB_RX_MAX_MPDU		(13 * SZ_1K)
> +#define MT7927_USB_RX_AGGR_BUF_SIZE \
> +	mt76u_rx_aggr_buf_size(MT7927_USB_RX_MAX_MPDU, \
> +			       MT7927_USB_RX_AGGR_LIMIT, \
> +			       MT7927_USB_RX_AGGR_PKT_LIMIT, \
> +			       MT7927_USB_RX_AGGR_PADDING)

If I do the math correctly, it will use an order-5 buffer for each urb, right?
If so, this approach is not very unrecommended, please take a look at [0]

[0] https://lore.kernel.org/netdev/CANn89iJsNWkWzAJbOvaBNjozuLOQBcpVo1bnvfeGq5Zm6h9e=Q@mail.gmail.com/

> +
>  static const struct usb_device_id mt7925u_device_table[] = {
>  	{ USB_DEVICE_AND_INTERFACE_INFO(0x0e8d, 0x6639, 0xff, 0xff, 0xff),
>  		.driver_info = (kernel_ulong_t)MT7925_FIRMWARE_WM },
> diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
> index 6280bc4bf78d..769e828e9449 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/mt792x_usb.c
> @@ -13,6 +13,9 @@
>  
>  #define MT792X_USB_TX_TIMEOUT_LIMIT	50000
>  #define MT792X_USB_UDMA_IDLE_TIMEOUT	1000
> +#define MT792X_USB_RX_AGG_LIMIT		32
> +#define MT792X_USB_RX_AGG_TIMEOUT	100
> +#define MT792X_USB_RX_AGG_PKT_LIMIT	30
>  
>  static int mt792xu_read32(struct mt76_dev *dev, u32 addr, void *buf)
>  {
> @@ -403,9 +406,23 @@ int mt792xu_dma_init(struct mt792x_dev *dev, bool resume)
>  		 FIELD_PREP(MT_WL_TX_TMOUT_LMT,
>  			    MT792X_USB_TX_TIMEOUT_LIMIT));
>  	mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_TX_TMOUT_FUNC_EN);
> -	mt76_clear(dev, MT_UDMA_WLCFG_0,
> -		   MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT);
> -	mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT);
> +
> +	if (dev->mt76.usb.rx_aggr.enable) {
> +		mt76_set(dev, MT_UDMA_WLCFG_0, MT_WL_RX_AGG_EN);
> +		mt76_rmw(dev, MT_UDMA_WLCFG_0,
> +			 MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT,
> +			 FIELD_PREP(MT_WL_RX_AGG_TO,
> +				    MT792X_USB_RX_AGG_TIMEOUT) |
> +			 FIELD_PREP(MT_WL_RX_AGG_LMT,
> +				    MT792X_USB_RX_AGG_LIMIT));
> +		mt76_rmw(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT,
> +			 FIELD_PREP(MT_WL_RX_AGG_PKT_LMT,
> +				    MT792X_USB_RX_AGG_PKT_LIMIT));
> +	} else {
> +		mt76_clear(dev, MT_UDMA_WLCFG_0, MT_WL_RX_AGG_EN |
> +			   MT_WL_RX_AGG_TO | MT_WL_RX_AGG_LMT);
> +		mt76_clear(dev, MT_UDMA_WLCFG_1, MT_WL_RX_AGG_PKT_LMT);
> +	}
>  
>  	if (resume)
>  		return 0;
> diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
> index cab36630c978..cbdd663fbb25 100644
> --- a/drivers/net/wireless/mediatek/mt76/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/usb.c
> @@ -371,6 +371,14 @@ mt76u_refill_rx(struct mt76_dev *dev, struct mt76_queue *q,
>  		return mt76u_fill_rx_sg(dev, q, urb, nsgs);
>  
>  	urb->transfer_buffer_length = q->buf_size;
> +	if (qid == MT_RXQ_MAIN && dev->usb.rx_aggr.enable) {
> +		if (!urb->transfer_buffer)
> +			urb->transfer_buffer =
> +				mt76_get_page_pool_buf(q, &offset, q->buf_size);
> +
> +		return urb->transfer_buffer ? 0 : -ENOMEM;

maybe I am missing something, but this chunk of code seems unnecessary

> +	}
> +
>  	urb->transfer_buffer = mt76_get_page_pool_buf(q, &offset, q->buf_size);
>  
>  	return urb->transfer_buffer ? 0 : -ENOMEM;
> @@ -538,18 +546,113 @@ mt76u_build_rx_skb(struct mt76_dev *dev, void *data,
>  	return skb;
>  }
>  
> +static struct sk_buff *
> +mt76u_build_rx_skb_aggr(struct mt76_dev *dev, void *data, int data_len,
> +			int buf_len)
> +{

Can we integrate it better with mt76u_build_rx_skb()?

> +	int head_room, drv_flags = dev->drv->drv_flags;
> +	int len = min_t(int, data_len, MT_SKB_HEAD_LEN);
> +	struct sk_buff *skb;
> +
> +	if (data_len <= 0)
> +		return NULL;
> +
> +	head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN;
> +	skb = alloc_skb(len, GFP_ATOMIC);
> +	if (!skb)
> +		return NULL;
> +
> +	data += head_room;
> +	skb_put_data(skb, data, len);
> +	if (data_len > len) {
> +		struct page *page;
> +
> +		data += len;
> +		page = virt_to_head_page(data);
> +		skb_add_rx_frag(skb, skb_shinfo(skb)->nr_frags,
> +				page, data - page_address(page),
> +				data_len - len, buf_len);
> +		get_page(page);
> +	}
> +
> +	return skb;
> +}
> +
> +static int mt76u_process_rx_agg_entry(struct mt76_dev *dev, struct urb *urb)
> +{
> +	int offset = 0, head_room, drv_flags = dev->drv->drv_flags;
> +	int align = dev->usb.rx_aggr.align ?: 4;
> +	int padding = dev->usb.rx_aggr.padding ?: 4;
> +	u8 *data = urb->transfer_buffer;
> +	int min_len;
> +	int nframes = 0;
> +
> +	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) ||
> +	    test_bit(MT76_REMOVED, &dev->phy.state))
> +		return 0;
> +
> +	head_room = drv_flags & MT_DRV_RX_DMA_HDR ? 0 : MT_DMA_HDR_LEN;
> +	min_len = head_room + MT_RX_RXWI_LEN;
> +
> +	while (urb->actual_length - offset >= min_len) {
> +		struct sk_buff *skb;
> +		int len, frame_len, agg_len;
> +
> +		len = mt76u_get_rx_entry_len(dev, data + offset,
> +					     urb->actual_length - offset);
> +		if (len < 0) {
> +			dev_warn_ratelimited(dev->dev,
> +					     "invalid USB RX aggregate at offset %d\n",
> +					     offset);
> +			break;
> +		}
> +
> +		frame_len = head_room + len;
> +		if (frame_len > urb->actual_length - offset) {
> +			dev_warn_ratelimited(dev->dev,
> +					     "truncated USB RX aggregate at offset %d\n",
> +					     offset);
> +			break;
> +		}
> +
> +		agg_len = ALIGN(frame_len, align) + padding;
> +		if (dev->drv->rx_check &&
> +		    !dev->drv->rx_check(dev, data + offset + head_room, len))
> +			goto next;
> +
> +		skb = mt76u_build_rx_skb_aggr(dev, data + offset, len,
> +					      agg_len);
> +		if (skb) {
> +			dev->drv->rx_skb(dev, MT_RXQ_MAIN, skb, NULL);
> +			nframes++;
> +		}
> +
> +next:
> +		offset += agg_len;
> +	}
> +
> +	mt76_put_page_pool_buf(urb->transfer_buffer, false);
> +	urb->transfer_buffer = NULL;
> +
> +	return max(nframes, 1);
> +}
> +
>  static int
>  mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
> -		       int buf_size)
> +		       enum mt76_rxq_id qid, int buf_size)
>  {
>  	u8 *data = urb->num_sgs ? sg_virt(&urb->sg[0]) : urb->transfer_buffer;
>  	int data_len = urb->num_sgs ? urb->sg[0].length : urb->actual_length;
>  	int len, nsgs = 1, head_room, drv_flags = dev->drv->drv_flags;
>  	struct sk_buff *skb;
>  
> -	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state))
> +	if (!test_bit(MT76_STATE_INITIALIZED, &dev->phy.state) ||
> +	    test_bit(MT76_REMOVED, &dev->phy.state))

This seems a fix to me.

>  		return 0;
>  
> +	if (qid == MT_RXQ_MAIN && dev->usb.rx_aggr.enable && !urb->num_sgs)
> +		return mt76u_process_rx_agg_entry(dev, urb);
> +
>  	len = mt76u_get_rx_entry_len(dev, data, urb->actual_length);
>  	if (len < 0)
>  		return 0;
> @@ -594,6 +697,9 @@ static void mt76u_complete_rx(struct urb *urb)
>  
>  	trace_rx_urb(dev, urb);
>  
> +	if (test_bit(MT76_REMOVED, &dev->phy.state))
> +		return;

same here.

> +
>  	switch (urb->status) {
>  	case -ECONNRESET:
>  	case -ESHUTDOWN:
> @@ -658,12 +764,14 @@ mt76u_process_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
>  		if (!urb)
>  			break;
>  
> -		count = mt76u_process_rx_entry(dev, urb, q->buf_size);
> +		count = mt76u_process_rx_entry(dev, urb, qid, q->buf_size);
>  		if (count > 0) {
>  			err = mt76u_refill_rx(dev, q, urb, count);
>  			if (err < 0)
>  				break;
>  		}
> +		if (test_bit(MT76_REMOVED, &dev->phy.state))
> +			break;
>  		mt76u_submit_rx_buf(dev, qid, urb);
>  	}
>  }
> @@ -729,10 +837,6 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
>  	struct mt76_queue *q = &dev->q_rx[qid];
>  	int i, err;
>  
> -	err = mt76_create_page_pool(dev, q);
> -	if (err)
> -		return err;
> -
>  	spin_lock_init(&q->lock);
>  	q->entry = devm_kcalloc(dev->dev,
>  				MT_NUM_RX_ENTRIES, sizeof(*q->entry),
> @@ -742,6 +846,12 @@ mt76u_alloc_rx_queue(struct mt76_dev *dev, enum mt76_rxq_id qid)
>  
>  	q->ndesc = MT_NUM_RX_ENTRIES;
>  	q->buf_size = PAGE_SIZE;
> +	if (qid == MT_RXQ_MAIN && dev->usb.rx_aggr.enable)
> +		q->buf_size = dev->usb.rx_aggr.buf_size ?: PAGE_SIZE;
> +
> +	err = mt76_create_page_pool(dev, q);
> +	if (err)
> +		return err;
>  
>  	for (i = 0; i < q->ndesc; i++) {
>  		err = mt76u_rx_urb_alloc(dev, q, &q->entry[i]);
> -- 
> 2.43.0
> 

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

^ permalink raw reply

* Re: [PATCH 2/5] wifi: mt76: usb: support out-of-order RX URB completion
From: Lorenzo Bianconi @ 2026-06-15  6:35 UTC (permalink / raw)
  To: Sean Wang; +Cc: Felix Fietkau, linux-wireless, linux-mediatek, Sean Wang
In-Reply-To: <20260613224655.2405686-3-sean.wang@kernel.org>

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

> From: Sean Wang <sean.wang@mediatek.com>
> 
> Keep per-URB RX queue context and complete entries by their real queue
> position instead of assuming the completed URB is always at q->head.
> 
> USB RX URBs can complete out of order, and advancing q->head too early
> can corrupt RX queue accounting and process buffers in the wrong order.
> 
> Signed-off-by: Sean Wang <sean.wang@mediatek.com>
> ---
>  drivers/net/wireless/mediatek/mt76/mt76.h |  5 ++
>  drivers/net/wireless/mediatek/mt76/usb.c  | 77 ++++++++++++++++-------
>  2 files changed, 61 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h
> index 122e77a5f2f4..81740aa7df71 100644
> --- a/drivers/net/wireless/mediatek/mt76/mt76.h
> +++ b/drivers/net/wireless/mediatek/mt76/mt76.h
> @@ -655,6 +655,11 @@ struct mt76_mcu {
>  	wait_queue_head_t wait;
>  };
>  
> +struct mt76u_rx_entry {
> +	struct mt76_queue_entry *e;
> +	struct mt76_queue *q;
> +};
> +
>  #define MT_TX_SG_MAX_SIZE	8
>  #define MT_RX_SG_MAX_SIZE	4
>  #define MT_NUM_TX_ENTRIES	256
> diff --git a/drivers/net/wireless/mediatek/mt76/usb.c b/drivers/net/wireless/mediatek/mt76/usb.c
> index ce68e1d0c786..cab36630c978 100644
> --- a/drivers/net/wireless/mediatek/mt76/usb.c
> +++ b/drivers/net/wireless/mediatek/mt76/usb.c
> @@ -397,11 +397,25 @@ mt76u_urb_alloc(struct mt76_dev *dev, struct mt76_queue_entry *e,
>  	return 0;
>  }
>  
> +static void mt76u_urb_free(struct urb *urb)
> +{
> +	int i;
> +
> +	for (i = 0; i < urb->num_sgs; i++)
> +		mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
> +
> +	if (urb->transfer_buffer)
> +		mt76_put_page_pool_buf(urb->transfer_buffer, false);
> +
> +	usb_free_urb(urb);
> +}
> +
>  static int
>  mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
>  		   struct mt76_queue_entry *e)
>  {
>  	enum mt76_rxq_id qid = q - &dev->q_rx[MT_RXQ_MAIN];
> +	struct mt76u_rx_entry *rxe;
>  	int err, sg_size;
>  
>  	sg_size = qid == MT_RXQ_MAIN ? MT_RX_SG_MAX_SIZE : 0;
> @@ -409,20 +423,25 @@ mt76u_rx_urb_alloc(struct mt76_dev *dev, struct mt76_queue *q,
>  	if (err)
>  		return err;
>  
> -	return mt76u_refill_rx(dev, q, e->urb, sg_size);
> -}
> -
> -static void mt76u_urb_free(struct urb *urb)
> -{
> -	int i;
> +	rxe = kzalloc_obj(*rxe, GFP_KERNEL);

I guess you can move it at the beginning of mt76u_rx_urb_alloc(), right?

> +	if (!rxe) {
> +		usb_free_urb(e->urb);

IIRC, if mt76u_rx_urb_alloc() fails, mt76u_free_rx_queue() will run so we do
not need to run usb_free_urb() manually here, right?

> +		e->urb = NULL;
> +		return -ENOMEM;
> +	}
>  
> -	for (i = 0; i < urb->num_sgs; i++)
> -		mt76_put_page_pool_buf(sg_virt(&urb->sg[i]), false);
> +	rxe->e = e;
> +	rxe->q = q;
> +	e->urb->context = rxe;
>  
> -	if (urb->transfer_buffer)
> -		mt76_put_page_pool_buf(urb->transfer_buffer, false);
> +	err = mt76u_refill_rx(dev, q, e->urb, sg_size);
> +	if (err) {
> +		kfree(rxe);
> +		mt76u_urb_free(e->urb);
> +		e->urb = NULL;
> +	}
>  
> -	usb_free_urb(urb);
> +	return err;
>  }
>  
>  static void
> @@ -566,8 +585,12 @@ mt76u_process_rx_entry(struct mt76_dev *dev, struct urb *urb,
>  static void mt76u_complete_rx(struct urb *urb)
>  {
>  	struct mt76_dev *dev = dev_get_drvdata(&urb->dev->dev);
> -	struct mt76_queue *q = urb->context;
> +	struct mt76u_rx_entry *rxe = urb->context;
> +	struct mt76_queue_entry *e = rxe->e;
> +	unsigned int idx, pending, pos;
> +	struct mt76_queue *q = rxe->q;
>  	unsigned long flags;
> +	bool wake = false;
>  
>  	trace_rx_urb(dev, urb);
>  
> @@ -586,18 +609,28 @@ static void mt76u_complete_rx(struct urb *urb)
>  	}
>  
>  	spin_lock_irqsave(&q->lock, flags);
> -	if (WARN_ONCE(q->entry[q->head].urb != urb, "rx urb mismatch"))
> +	idx = e - q->entry;
> +	pending = q->ndesc - q->queued;
> +	pos = (idx + q->ndesc - q->head) % q->ndesc;
> +	if (WARN_ONCE(idx >= q->ndesc || pos >= pending, "rx urb mismatch"))

can idx be >= of q->ndesc?

>  		goto out;
>  
> -	q->head = (q->head + 1) % q->ndesc;
> -	q->queued++;
> -
> -	if (q == &dev->q_rx[MT_RXQ_MAIN])
> -		napi_schedule(&dev->napi[MT_RXQ_MAIN]);
> -	else
> -		mt76_worker_schedule(&dev->usb.rx_worker);
> +	e->done = true;
> +	while (q->entry[q->head].done) {
> +		q->entry[q->head].done = false;
> +		q->head = (q->head + 1) % q->ndesc;
> +		q->queued++;
> +		wake = true;

This seems a fix to me since theoretically we could have the same issue in
the current codebase, right?

Regards,
Lorenzo

> +	}
>  out:
>  	spin_unlock_irqrestore(&q->lock, flags);
> +
> +	if (wake) {
> +		if (q == &dev->q_rx[MT_RXQ_MAIN])
> +			napi_schedule(&dev->napi[MT_RXQ_MAIN]);
> +		else
> +			mt76_worker_schedule(&dev->usb.rx_worker);
> +	}
>  }
>  
>  static int
> @@ -607,7 +640,7 @@ mt76u_submit_rx_buf(struct mt76_dev *dev, enum mt76_rxq_id qid,
>  	int ep = qid == MT_RXQ_MAIN ? MT_EP_IN_PKT_RX : MT_EP_IN_CMD_RESP;
>  
>  	mt76u_fill_bulk_urb(dev, USB_DIR_IN, ep, urb,
> -			    mt76u_complete_rx, &dev->q_rx[qid]);
> +			    mt76u_complete_rx, urb->context);
>  	trace_submit_urb(dev, urb);
>  
>  	return usb_submit_urb(urb, GFP_ATOMIC);
> @@ -678,6 +711,7 @@ mt76u_submit_rx_buffers(struct mt76_dev *dev, enum mt76_rxq_id qid)
>  
>  	spin_lock_irqsave(&q->lock, flags);
>  	for (i = 0; i < q->ndesc; i++) {
> +		q->entry[i].done = false;
>  		err = mt76u_submit_rx_buf(dev, qid, q->entry[i].urb);
>  		if (err < 0)
>  			break;
> @@ -733,6 +767,7 @@ mt76u_free_rx_queue(struct mt76_dev *dev, struct mt76_queue *q)
>  		if (!q->entry[i].urb)
>  			continue;
>  
> +		kfree(q->entry[i].urb->context);
>  		mt76u_urb_free(q->entry[i].urb);
>  		q->entry[i].urb = NULL;
>  	}
> -- 
> 2.43.0
> 

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

^ permalink raw reply

* RE: [PATCH v3] wifi: mt76: mt792x: fix use-after-free in mt76_rx_poll_complete
From: Eason Lai (賴易聖) @ 2026-06-15  2:17 UTC (permalink / raw)
  To: Felix Fietkau, JB Tsai (蔡志彬),
	lorenzo@kernel.org
  Cc: linux-wireless@vger.kernel.org,
	linux-mediatek@lists.infradead.org,
	Deren Wu (武德仁), Sean Wang,
	Quan Zhou (周全), Ryder Lee,
	Leon Yen (顏良儒),
	Litien Chang (張立典)
In-Reply-To: <8430bab4-a064-4f58-81d8-c8801d17f89a@nbd.name>

After looking into the mac80211 STA removal flow, I didn't notice that synchronize_net() is invoked after mt76_sta_pre_rcu_remove().
You are right. I will check mt7925_change_vif_links(). Thanks for the suggestion.

Best regards,
Eason

-----Original Message-----
From: Felix Fietkau <nbd@nbd.name> 
Sent: Monday, June 1, 2026 7:13 PM
To: JB Tsai (蔡志彬) <Jb.Tsai@mediatek.com>; lorenzo@kernel.org
Cc: linux-wireless@vger.kernel.org; linux-mediatek@lists.infradead.org; Deren Wu (武德仁) <Deren.Wu@mediatek.com>; Sean Wang <Sean.Wang@mediatek.com>; Quan Zhou (周全) <Quan.Zhou@mediatek.com>; Ryder Lee <Ryder.Lee@mediatek.com>; Leon Yen (顏良儒) <Leon.Yen@mediatek.com>; Litien Chang (張立典) <Litien.Chang@mediatek.com>; Eason Lai (賴易聖) <Eason.Lai@mediatek.com>
Subject: Re: [PATCH v3] wifi: mt76: mt792x: fix use-after-free in mt76_rx_poll_complete

On 06.05.26 10:43, JB Tsai wrote:
> From: Eason Lai <Eason.Lai@mediatek.com>
> 
> A use-after-free issue occurs in mt76_rx_poll_complete due to a race 
> condition. The STA has already been removed, but the rx_status still 
> had a pointer to the wcid in the STA.
> 
> Use wcid_idx instead of storing the wcid pointer, and look up the wcid 
> via rcu_dereference() by wcid_idx.
Unless I'm misreading something, it seems to me that this patch papers over a different bug instead of fixing the root cause.
Right now the rx processing code relies on RCU to protect the wcid and sta data structures.
The rcu lock/unlock around polling also seems correct to me.

Are the freed wcid pointers maybe related to a vif sta instead of an actual station? The use of devm_kfree in mt7925_change_vif_links looks suspicious to me.

Please let me know if I'm missing something here.

- Felix

^ permalink raw reply

* RE: [PATCH] wifi: realtek: rtw8822c: replace msleep() with fsleep() for DPK delays
From: Ping-Ke Shih @ 2026-06-15  1:54 UTC (permalink / raw)
  To: Chen Jung Ku
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org,
	Chen Jung Ku
In-Reply-To: <5183c87219e2489eb99c9015cb96a7f8@realtek.com>

Ping-Ke Shih <pkshih@realtek.com> wrote:
> Chen Jung Ku <ku.loong@gapp.nthu.edu.tw> wrote:
> > Replace msleep() with fsleep(), because msleep() may oversleep
> > to as much as 20 ms when used for a 10 ms delay.
> > According to the kernel documentation, fsleep() is more suitable
> > and aligns better with modern kernel style.
> >
> > Documentation link: https://docs.kernel.org/timers/delay_sleep_functions.html
> >
> > Signed-off-by: Chen Jung Ku <ku.loong@gapp.nthu.edu.tw>
> 
> Acked-by: Ping-Ke Shih <pkshih@realtek.com>
> 

Oops. The subject prefix should be "wifi: rtw88: ..."

I'll correct subject as below during getting merged:

wifi: rtw88: 8822c: replace msleep() with fsleep() for DPK delays



^ permalink raw reply

* RE: [PATCH rtw-next 3/3] wifi: rtlwifi: disable ASPM for RTL8723BE with subsystem ID 17aa:b736
From: Ping-Ke Shih @ 2026-06-15  1:41 UTC (permalink / raw)
  To: William Hansen-Baird
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260614135508.70307-4-william.hansen.baird@gmail.com>

William Hansen-Baird <william.hansen.baird@gmail.com> wrote:
>  drivers/net/wireless/realtek/rtlwifi/pci.c | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c
> b/drivers/net/wireless/realtek/rtlwifi/pci.c
> index 03b743809258..bbcd1922acb2 100644
> --- a/drivers/net/wireless/realtek/rtlwifi/pci.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
> @@ -33,6 +33,7 @@ static const u8 ac_to_hwq[] = {
> 
>  static const struct pci_device_id rtl8723be_aspm_quirks[] = {

Please change to rtl_aspm_quirks[] by patch 2/3. 

>         { PCI_DEVICE_SUB(PCI_ANY_ID, PCI_ANY_ID, 0x11ad, 0x1723) },
> +       { PCI_DEVICE_SUB(PCI_ANY_ID, PCI_ANY_ID, 0x17aa, 0xb736) },

As opinion in patch 2/3, fill VID/PID "PCI_VENDOR_ID_REALTEK, 0xB723". 

>         { 0 }
>  };
> 
> --
> 2.54.0


^ permalink raw reply

* RE: [PATCH rtw-next 2/3] wifi: rtlwifi: convert pci ID if-statement to table
From: Ping-Ke Shih @ 2026-06-15  1:37 UTC (permalink / raw)
  To: William Hansen-Baird
  Cc: linux-wireless@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <20260614135508.70307-3-william.hansen.baird@gmail.com>

William Hansen-Baird <william.hansen.baird@gmail.com> wrote:

[...]

> --- a/drivers/net/wireless/realtek/rtlwifi/pci.c
> +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c
> @@ -31,6 +31,11 @@ static const u8 ac_to_hwq[] = {
>         BK_QUEUE
>  };
> 
> +static const struct pci_device_id rtl8723be_aspm_quirks[] = {
> +       { PCI_DEVICE_SUB(PCI_ANY_ID, PCI_ANY_ID, 0x11ad, 0x1723) },

Why not filling VID/PID explicitly? As there is only one listed in
rtl8723be_pci_ids[].

	{RTL_PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0xB723, ...)},

> +       { 0 }
> +};
> +
>  static u8 _rtl_mac_to_hwqueue(struct ieee80211_hw *hw, struct sk_buff *skb)
>  {
>         struct rtl_hal *rtlhal = rtl_hal(rtl_priv(hw));
> @@ -327,14 +332,12 @@ static void rtl_pci_init_aspm(struct ieee80211_hw *hw)
> 
>         _rtl_pci_update_default_setting(hw);
> 
> -       /* RTL8723BE found on some ASUSTek laptops, such as F441U and
> -        * X555UQ with subsystem ID 11ad:1723 are known to output large
> +       /* RTL8723BE with certain subsytem IDs are known to output large
>          * amounts of PCIe AER errors during and after boot up, causing
>          * heavy lags, poor network throughput, and occasional lock-ups.
>          */
>         if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE &&

With above opinion, here just check pci_match_id(), no need to check RTL8723BE.

> -           (rtlpci->pdev->subsystem_vendor == 0x11ad &&
> -            rtlpci->pdev->subsystem_device == 0x1723)) {
> +               pci_match_id(rtl8723be_aspm_quirks, rtlpci->pdev)) {
>                 rtl_pci_disable_aspm(hw);
>                 ppsc->support_aspm = false;
>         }
> --
> 2.54.0


^ permalink raw reply


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