* Re: [PATCH do not merge 0/4] wifi: mwifiex: add iw61x support
From: Marek Vasut @ 2026-07-20 22:46 UTC (permalink / raw)
To: Sascha Hauer, Brian Norris, Francesco Dolcini, Ulf Hansson
Cc: linux-wireless, linux-kernel, linux-mmc
In-Reply-To: <20250326-mwifiex-iw61x-v1-0-ff875ed35efc@pengutronix.de>
Hello Sascha,
On 3/26/25 1:18 PM, Sascha Hauer wrote:
> This series adds iw61x support to the mwifiex driver. It works for me,
> but is not yet ready to be merged. Some people showed interest in it, so
> I am sending it here.
>
> All testing and review feedback appreciated.
>
> During startup I get these messages:
>
> [ 12.078010] mwifiex_sdio mmc1:0001:1: Unknown GET_HW_SPEC TLV type: 0xff
> [ 12.078018] mwifiex_sdio mmc1:0001:1: Unknown GET_HW_SPEC TLV type: 0xff
> [ 12.078024] mwifiex_sdio mmc1:0001:1: Unknown GET_HW_SPEC TLV type: 0x23e
> [ 12.078029] mwifiex_sdio mmc1:0001:1: Unknown GET_HW_SPEC TLV type: 0x25c
>
> Not sure what these are about, I can't see these handled in the
> downstream mwifiex driver as well. Could also be there is some parsing
> error. Nevertheless the driver seems to work.
>
> The series applies to v6.14, but should apply to my mwifiex cleanup
> series as well.
I was able to test these patches with IW610G based muRata 2LL SDIO
device. I used firmware from [1] and I had to add SDIO ID for that
IW610G device (trivial patch is below). Besides that, I am adding my:
Tested-by: Marek Vasut <marek.vasut@mailbox.org> # muRata 2LL
Do you plan to finish upstreaming these patches ?
"
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c
b/drivers/net/wireless/marvell/mwifiex/sdio.c
index 84e3946cc4e4e..54fc82a6e3458 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -1049,6 +1049,8 @@ static const struct sdio_device_id mwifiex_ids[] = {
.driver_data = (unsigned long)&mwifiex_sdio_sd8987},
{SDIO_DEVICE(SDIO_VENDOR_ID_MARVELL,
SDIO_DEVICE_ID_MARVELL_8997_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_sd8997},
+ {SDIO_DEVICE(SDIO_VENDOR_ID_NXP, 0x0215),
+ .driver_data = (unsigned long)&mwifiex_sdio_iw612},
{SDIO_DEVICE(SDIO_VENDOR_ID_NXP, SDIO_DEVICE_ID_NXP_IW612_WLAN),
.driver_data = (unsigned long)&mwifiex_sdio_iw612},
{},
"
[1]
https://github.com/nxp-imx/imx-firmware/tree/lf-6.18.20_2.0.0/FwImage_IW610_SD
Thank you for your help !
^ permalink raw reply related
* Re: [PATCH] wifi: brcmfmac: cyw: clean up PMKID and cookie code
From: Bogdan Nicolae @ 2026-07-20 21:30 UTC (permalink / raw)
To: Arend van Spriel
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel
In-Reply-To: <20260720194900.2356442-2-arend.vanspriel@broadcom.com>
Arend,
Sounds good. Do you prefer two patches in a single email or two separate emails?
Best,
Bogdan
On Mon, Jul 20, 2026 at 2:49 PM Arend van Spriel
<arend.vanspriel@broadcom.com> wrote:
>
> On Tue, 14 Jul 2026, bogdan.nicolae@gmail.com wrote:
>
> > [PATCH] wifi: brcmfmac: cyw: clean up PMKID and cookie code
>
> The code itself is correct, but there are process issues that need to be
> fixed before I can accept this:
>
> 1. This is a resubmission of the patch sent on 2026-07-09 [1]. A
> resubmission must carry a version tag: the subject line should read
> "[PATCH v2] wifi: brcmfmac: ..." not "[PATCH] wifi: brcmfmac: ...".
>
> 2. A v2 submission must include a "Changes in v2:" section below the ---
> line documenting what changed from v1.
>
> 3. The From: address (bogdan.nicolae@gmail.com) does not match the
> Signed-off-by address (bogdan.nicolae@acm.org). Please use a
> consistent address or add a second Signed-off-by for the sending
> address.
>
> 4. The patch addresses two independent issues: fixing the packet_id in
> brcmf_cyw_mgmt_tx() (SAE auth frame TX) and fixing the zero-init and
> PMKID copy in brcmf_cyw_external_auth() (auth completion). Please
> split these into two separate patches.
>
> Please send a v2 addressing these points.
>
> [1] https://lore.kernel.org/linux-wireless/20260709122315.11400-1-bogdan.nicolae@gmail.com/
>
> Regards,
> Arend
^ permalink raw reply
* Re: short description of GeoNetworking
From: Andrew Lunn @ 2026-07-20 20:16 UTC (permalink / raw)
To: Simon Dietz
Cc: andrew+netdev, davem, dietz23838, edumazet, johannes, kuniyu,
linux-wireless, netdev
In-Reply-To: <20260720183613.2020886-1-simon.dietz@plantwatch.de>
On Mon, Jul 20, 2026 at 08:36:13PM +0200, Simon Dietz wrote:
> Hi Andrew,
>
> > Is there an architecture documentation somewhere?
> No really available one, at least not public. The ETSI ITS standard is
> public available and there are research papers, but that would be a lot
> to read.
Sorry, i was meaning Linux architecture. Something is feeding in GPS
location information. I assume there is some daemon talking to gpsd on
one side, and the kernel and the other? What other user space pieces
are there?
> > One of my comments was about routing tables.
> I agree that cosine calculations may not belong to the kernel space.
> Before we continue talking about routing tables, let me give a short
>
> Description of the GeoNetworking (gn) protocol
>
> GeoNetworking is used in a vehicle2x context where vehicles exchange
> position information (and other data in higher protocol layers like
> BTP) for use cases like trafic jam notifications, or railroad crossing
BTP?
> communication with cars or trains.
>
> gn transmitts packets in various possible 'modes', including:
> * broadcast (all recievers in range, like ip)
Hold on. IP broadcast, not L2 broadcast. So if you have a mash, this
broadcast is L2 multi hop, but stays within the same IP subnet.
> * single hop broadcast (all recievers in direct range, like ip, if the
> reviever is in the same network and no default gateway is used)
Given the previous definition, this makes no sense. IP broadcast never
leaves the IP subnet. You need to use IP multicast, and an IP
multicast gateway for such packets to go into other subnets. And then
you need PIM or some other multicast routing protocol.
> * unicast (one reciever out of range, packet is sent to the closest
> intermediary; only here a routing decision is involved)
Closest intermediary. So the idea is the maximise the number of L2
hops? That can make sense, assuming the underlying WiFi network is
using different coding rates. A short hop can use a high coding rate,
making the use of air time shorter. If the furthest intermediary was
used, you need to use a lower coding rate, which takes up more air
time.
And only unicast needs routing? So broadcast is dumb flood everywhere,
and the receiver needs to remove duplicates, and not reflood
duplicates. And broadcast uses the lowest coding rate, so giving the
biggest coverage, but takes up the most air time.
> gn packets contain a gps position and a geographic target scope, which
> can be one of predefined shapes (rectangle, circle, ellipsis) and
> dimensions of that shape (radius if circle, length and width if
> rectangle). Hosts with a position outside the shape may recieve, e.g.
> a broadcast, but drop the packet (because it's out of the target scope)
So the sender does not care about the shape, it is the receiver which
does the filtering. And there is no concept of a receiver which is
outside the shape being able to fill dead spots by transmitting back
towards the shape?
> There are location service (ls) requests, which are used to query
> nearby hosts for the location of a host out of the sender's own range,
> which are answered with ls reply packets.
So this is a flood search? Is the outward path recorded in each LS
request packet as it hops away from the sender? So when it reaches
the target, the reply can be hop-by-hop unicast back to the requester?
> I would suggest to handle them in kernel space and only notify the user
> space, if something happens (previously unknown beacon recieved, ls
> reply recieved, ...) instead of passing all the beacon and ls packets
> to user space.
We generally split policy from mechanical actions. Doing a routing
table lookup is mechanical, and goes in the kernel. The policy of what
to put in the routing table is generally in userspace. If you think
about IP routing, we have a couple of different OSPF versions, BGP,
IS-IS, RIP, EIGRP, etc. Each implement a different policy.
It also seems like there is some scope for experimentation here with
routing. A node at the edge of the shape receiving from a node in the
middle of the shape could consider where its neighbours are within the
same, and set the coding rate for the broadcast based on the unicast
coding rates to pick the highest rate which should work for all
neighbours, so saving air time? And rather than trying to maximise
hops, you could try to find an intermediary somewhere in the middle,
so you balance hops and coding rate/air time?
That sort of experimentation is a lot harder to do in the kernel, it
is more natural to do in userspace.
> > There also seems to be a need for location information. How does that
> > get into the kernel? Is there a daemon for that? Patches to gpsd?
> In the first version /proc has been used, after that ioctl; today
> netlink generic seems to be the most reasonable option. We used
> standard u-blox gps recievers and wrote a little userspace tool to
> inject the location information into the kernel space (via ioctl).
So it would be good to include a link to your git repo. We generally
want open user spaces tools. And i would expect a generic solution,
e.g. using gpsd, so any of the GPSes supported by gpds can be used.
However, given the simplicity of the API, this is not a GPU after all,
this is less important.
> That brings me to the question, how the ideal interface between user
> and kernel space should look like for this module/functionality.
>
> For short term, it should be possible to strip the routing stuff incl.
> the cos table from the module and return -EOPNOTSUPP and/or -EINVAL
> when using advanced stuff like routing and only support recieving gn
> packets and send broadcast and beacon packets. Routing could then be
> added in a v2 patch series.
How useful is the stack without unicast?
Andrew
^ permalink raw reply
* Re: [PATCH v2] wifi: brcmfmac: set F2 blocksize to 256 for BCM43752
From: Arend van Spriel @ 2026-07-20 19:49 UTC (permalink / raw)
To: LiangCheng Wang
Cc: Arend van Spriel, linux-wireless, brcm80211,
brcm80211-dev-list.pdl, linux-kernel, stable, Kalle Valo,
Angus Ainslie, Gokul Sivakumar, Wig Cheng, Peter Robinson
In-Reply-To: <20260715-b43752-f2-blksz-v2-1-f9be49856050@gmail.com>
On Wed, 15 Jul 2026, LiangCheng Wang wrote:
> [PATCH v2] wifi: brcmfmac: set F2 blocksize to 256 for BCM43752
>
> The BCM43752 is not reliable with the default 512-byte SDIO function 2
> block size ...
>
> 1 file changed, 1 insertion(+)
The stable backport explanation is clear and the "needs adjustments" tag
format is correct. The one-line fix is trivially correct.
Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
> The BCM43751 shares the 43752 firmware handling and F2 watermark case
> and may need the same fix, but I have no 43751 hardware to verify.
I have a number of devices but no BCM43751. If anyone has please try
this change and let us know.
Regards,
Arend
^ permalink raw reply
* Re: [PATCH v2 2/2] wifi: brcmfmac: drain bus_reset work on device removal
From: Arend van Spriel @ 2026-07-20 19:48 UTC (permalink / raw)
To: Fan Wu
Cc: Arend van Spriel, linux-wireless, brcm80211,
brcm80211-dev-list.pdl, linux-kernel, stable, kvalo,
eddiephillips, franky.lin, hante.meuleman, wright.feng,
SHA-cyfmac-dev-list
In-Reply-To: <20260718024353.3147201-3-fanwu01@zju.edu.cn>
On Sat, 18 Jul 2026, Fan Wu wrote:
> [PATCH v2 2/2] wifi: brcmfmac: drain bus_reset work on device removal
>
> brcmf_fw_crashed() and the debugfs "reset" entry both schedule
> drvr->bus_reset, whose callback recovers drvr through container_of()
> and dereferences it. The removal path frees drvr (brcmf_free ->
> wiphy_free) without draining the work ...
>
> 7 files changed, 77 insertions(+), 4 deletions(-)
Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Regards,
Arend
^ permalink raw reply
* Re: [PATCH v2 1/2] wifi: brcmfmac: make release_scratchbuffers idempotent
From: Arend van Spriel @ 2026-07-20 19:48 UTC (permalink / raw)
To: Fan Wu
Cc: Arend van Spriel, linux-wireless, brcm80211,
brcm80211-dev-list.pdl, linux-kernel, stable, kvalo,
eddiephillips, franky.lin, hante.meuleman, wright.feng,
SHA-cyfmac-dev-list
In-Reply-To: <20260718024353.3147201-2-fanwu01@zju.edu.cn>
On Sat, 18 Jul 2026, Fan Wu wrote:
> [PATCH v2 1/2] wifi: brcmfmac: make release_scratchbuffers idempotent
>
> brcmf_pcie_release_scratchbuffers() frees the shared.scratch and
> shared.ringupd DMA buffers with dma_free_coherent() but does not clear
> the pointers afterwards ...
>
> 1 file changed, 6 insertions(+), 2 deletions(-)
Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
Regards,
Arend
^ permalink raw reply
* Re: [PATCH v5] wifi: brcmfmac: add DPP support
From: Arend van Spriel @ 2026-07-20 19:48 UTC (permalink / raw)
To: linux-wireless
Cc: Arend van Spriel, brcm80211, brcm80211-dev-list.pdl, linux-kernel,
Jason Huang, Kurt Lee
In-Reply-To: <20260715084718.667522-1-Jason.Huang2@infineon.com>
On Wed, 15 Jul 2026, Jason Huang wrote:
> [PATCH v5] wifi: brcmfmac: add DPP support
>
> Add DPP AKM handling and RSN parsing support ...
>
> Changes since v4:
> - Drop the pre-existing P2P device vif fixes; they are now a standalone
> wireless patch.
The DPP code looks good. The P2P fixes have been removed as requested and
the changelog is present.
Acked-by: Arend van Spriel <arend.vanspriel@broadcom.com>
This ack is conditional on the standalone P2P device vif guard patch being
submitted. When you post it, please reply here with the lore.kernel.org
link so I can track it.
Regards,
Arend
^ permalink raw reply
* Re: [PATCH] wifi: brcmfmac: cyw: clean up PMKID and cookie code
From: Arend van Spriel @ 2026-07-20 19:48 UTC (permalink / raw)
To: bogdan.nicolae
Cc: Arend van Spriel, linux-wireless, brcm80211,
brcm80211-dev-list.pdl, linux-kernel, Bogdan Nicolae
In-Reply-To: <20260714150423.53960-1-bogdan.nicolae@gmail.com>
On Tue, 14 Jul 2026, bogdan.nicolae@gmail.com wrote:
> [PATCH] wifi: brcmfmac: cyw: clean up PMKID and cookie code
The code itself is correct, but there are process issues that need to be
fixed before I can accept this:
1. This is a resubmission of the patch sent on 2026-07-09 [1]. A
resubmission must carry a version tag: the subject line should read
"[PATCH v2] wifi: brcmfmac: ..." not "[PATCH] wifi: brcmfmac: ...".
2. A v2 submission must include a "Changes in v2:" section below the ---
line documenting what changed from v1.
3. The From: address (bogdan.nicolae@gmail.com) does not match the
Signed-off-by address (bogdan.nicolae@acm.org). Please use a
consistent address or add a second Signed-off-by for the sending
address.
4. The patch addresses two independent issues: fixing the packet_id in
brcmf_cyw_mgmt_tx() (SAE auth frame TX) and fixing the zero-init and
PMKID copy in brcmf_cyw_external_auth() (auth completion). Please
split these into two separate patches.
Please send a v2 addressing these points.
[1] https://lore.kernel.org/linux-wireless/20260709122315.11400-1-bogdan.nicolae@gmail.com/
Regards,
Arend
^ permalink raw reply
* Re: [PATCH v4] wifi: brcmfmac: add DPP support
From: Arend van Spriel @ 2026-07-20 18:48 UTC (permalink / raw)
To: HungTsung Huang
Cc: linux-wireless, brcm80211, brcm80211-dev-list.pdl, linux-kernel
In-Reply-To: <20260715092229.GA667683@ISCN5CG5251XQT.infineon.com>
On 15/07/2026 11:22, HungTsung Huang wrote:
> I agree this is a pre-existing P2P bug rather than part of DPP support, so I
> moved it into the standalone P2P fix patch.
Hi Jason,
I must have missed the "standalone P2P fix patch". Can you provide a
reference or submit the patch against the wireless tree if not already
done so.
Thanks,
Arend
^ permalink raw reply
* short description of GeoNetworking
From: Simon Dietz @ 2026-07-20 18:36 UTC (permalink / raw)
To: andrew
Cc: andrew+netdev, davem, dietz23838, edumazet, johannes, kuniyu,
linux-wireless, netdev, simon.dietz
In-Reply-To: <89b3aa8c-f2ec-4b27-b5b0-5891d870e001@lunn.ch>
Hi Andrew,
> Is there an architecture documentation somewhere?
No really available one, at least not public. The ETSI ITS standard is
public available and there are research papers, but that would be a lot
to read.
> One of my comments was about routing tables.
I agree that cosine calculations may not belong to the kernel space.
Before we continue talking about routing tables, let me give a short
Description of the GeoNetworking (gn) protocol
GeoNetworking is used in a vehicle2x context where vehicles exchange
position information (and other data in higher protocol layers like
BTP) for use cases like trafic jam notifications, or railroad crossing
communication with cars or trains.
gn transmitts packets in various possible 'modes', including:
* broadcast (all recievers in range, like ip)
* single hop broadcast (all recievers in direct range, like ip, if the
reviever is in the same network and no default gateway is used)
* unicast (one reciever out of range, packet is sent to the closest
intermediary; only here a routing decision is involved)
gn packets contain a gps position and a geographic target scope, which
can be one of predefined shapes (rectangle, circle, ellipsis) and
dimensions of that shape (radius if circle, length and width if
rectangle). Hosts with a position outside the shape may recieve, e.g.
a broadcast, but drop the packet (because it's out of the target scope)
There are beacon packets, which are continously sent by each host,
which contain the gps position of the host in order for the other hosts
to be able to perform distance calculations necessary for the routing
and (if no beacon packet has been recieved for a certain while) for
pruning the routing tables from other hosts which are no longer there.
There are location service (ls) requests, which are used to query
nearby hosts for the location of a host out of the sender's own range,
which are answered with ls reply packets.
There is an address rotation mechanism for privacy reasons, so it may
occur, that a vehicle disappears at a point and reappears as different
vehicle (without advertisement of the address change, so a correlation
of old/new address is not feasable).
So regarding routing there is the question where (user or kernel space)
the beacons and location service should be handled.
I would suggest to handle them in kernel space and only notify the user
space, if something happens (previously unknown beacon recieved, ls
reply recieved, ...) instead of passing all the beacon and ls packets
to user space.
> There also seems to be a need for location information. How does that
> get into the kernel? Is there a daemon for that? Patches to gpsd?
In the first version /proc has been used, after that ioctl; today
netlink generic seems to be the most reasonable option. We used
standard u-blox gps recievers and wrote a little userspace tool to
inject the location information into the kernel space (via ioctl).
That brings me to the question, how the ideal interface between user
and kernel space should look like for this module/functionality.
For short term, it should be possible to strip the routing stuff incl.
the cos table from the module and return -EOPNOTSUPP and/or -EINVAL
when using advanced stuff like routing and only support recieving gn
packets and send broadcast and beacon packets. Routing could then be
added in a v2 patch series.
What do you think?
Simon
^ permalink raw reply
* Re: [PATCH v2] wifi: mt76: mt7996: remove beacon_int_min_gcd from ADHOC interface combinations
From: Alex Gavin @ 2026-07-20 16:25 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, nbd, lorenzo
Cc: linux-wireless, ryder.lee, shayne.chen
In-Reply-To: <407f7ac5-0e97-4c84-8272-c0f3cec3890c@candelatech.com>
On 7/20/26 09:17, Alex Gavin wrote:
> On 7/19/26 23:04, Jose Ignacio Tornos Martinez wrote:
>> Hi Felix, Lorenzo,
>>
>> Just a gentle ping on this patch when you have a chance to take a look.
>>
>> It fixes a device registration failure (-EINVAL) for mt7996/mt7992
>> devices
>> introduced by commit 5ef0e8e2653b. Without this fix the device is
>> completely
>> unusable, as cfg80211 validates all advertised interface combinations at
>> registration time and rejects the entire driver.
>>
>> Please let me know if there is anything I should improve or change.
>>
>> Thank you for your time
>>
>> Best regards,
>> Jose Ignacio
>>
>
> This patch fixes driver initialization in my testing. I had previously
> used this patch [1] from OpenWRT's tree as a workaround. I have not done
> any testing with IBSS/ADHOC interfaces, though.
>
>
> Best,
>
> Alex
>
> [1] https://github.com/openwrt/openwrt/blob/main/package/kernel/
> mac80211/patches/subsys/220-allow-ibss-mixed.patch
>
Tested-by: Alex Gavin <alex.gavin@candelatech.com>
^ permalink raw reply
* Re: [PATCH v2] wifi: mt76: mt7996: remove beacon_int_min_gcd from ADHOC interface combinations
From: Alex Gavin @ 2026-07-20 16:17 UTC (permalink / raw)
To: Jose Ignacio Tornos Martinez, nbd, lorenzo
Cc: linux-wireless, ryder.lee, shayne.chen
In-Reply-To: <20260720060432.7943-1-jtornosm@redhat.com>
On 7/19/26 23:04, Jose Ignacio Tornos Martinez wrote:
> Hi Felix, Lorenzo,
>
> Just a gentle ping on this patch when you have a chance to take a look.
>
> It fixes a device registration failure (-EINVAL) for mt7996/mt7992 devices
> introduced by commit 5ef0e8e2653b. Without this fix the device is completely
> unusable, as cfg80211 validates all advertised interface combinations at
> registration time and rejects the entire driver.
>
> Please let me know if there is anything I should improve or change.
>
> Thank you for your time
>
> Best regards,
> Jose Ignacio
>
This patch fixes driver initialization in my testing. I had previously
used this patch [1] from OpenWRT's tree as a workaround. I have not done
any testing with IBSS/ADHOC interfaces, though.
Best,
Alex
[1]
https://github.com/openwrt/openwrt/blob/main/package/kernel/mac80211/patches/subsys/220-allow-ibss-mixed.patch
^ permalink raw reply
* Re: [PATCH v3] wifi: ath12k: fix survey indexing across bands
From: Jeff Johnson @ 2026-07-20 14:22 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson
Cc: linux-wireless, ath12k, linux-kernel, kernel,
Rameshkumar Sundaram, Nicolas Escande, Rameshkumar Sundaram
In-Reply-To: <8e4a3842-4fc2-4a05-9142-6fd8f23751ed@oss.qualcomm.com>
On 7/20/2026 7:01 AM, Jeff Johnson wrote:
> On 7/3/2026 8:56 AM, Matthew Leach wrote:
>> When running 'iw dev wlan0 survey dump' the values for the channel busy
>> time have the same sequence across bands. This is caused by indexing
>> into the ath12k survey array using a band-local index rather than the
>> global index passed by mac80211. This results in surveys for 5 GHz and 6
>> GHz channels returning values from 2.4 GHz slots, making the survey
>> unusable on those bands. Further, there are redundant survey slots for
>> multi-radio/single-phy instances.
>>
>> Fix by moving the survey data into ath12k_hw so multiple radios under a
>> single wiphy share one table, and index into it using the global
>> mac80211 index. A new spinlock in ath12k_hw serialises access to the
>> survey array, which is now shared across all radios under a single hw.
>>
>> Band busy-times Before this fix:
>>
>> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>>
>> After this fix, times are independent:
>>
>> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
>> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
>> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>>
>> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>>
>> Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
>> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
>> ---
>
> my review agent flagged:
> "file": "drivers/net/wireless/ath/ath12k/core.h",
> "summary": "data_lock comment still lists 'survey info' as one of the fields
> it protects, but survey data is now protected by ah->survey_lock",
> "failure_scenario": "A developer adding survey-related code follows the
> comment and uses data_lock to protect ah->survey[], introducing an unprotected
> access (since the actual readers/writers use survey_lock, not data_lock)."
>
> I'll update the comment in my pending branch
https://git.kernel.org/pub/scm/linux/kernel/git/ath/ath.git/commit/?h=pending&id=9ff6ef3b7b33d9c4afa898581a99ca86dc8b5004
^ permalink raw reply
* Re: [PATCH v3] wifi: ath12k: fix survey indexing across bands
From: Jeff Johnson @ 2026-07-20 14:01 UTC (permalink / raw)
To: Matthew Leach, Jeff Johnson
Cc: linux-wireless, ath12k, linux-kernel, kernel,
Rameshkumar Sundaram, Nicolas Escande, Rameshkumar Sundaram
In-Reply-To: <20260703-ath12-survey-band-fix-v3-1-2fb050c2505a@collabora.com>
On 7/3/2026 8:56 AM, Matthew Leach wrote:
> When running 'iw dev wlan0 survey dump' the values for the channel busy
> time have the same sequence across bands. This is caused by indexing
> into the ath12k survey array using a band-local index rather than the
> global index passed by mac80211. This results in surveys for 5 GHz and 6
> GHz channels returning values from 2.4 GHz slots, making the survey
> unusable on those bands. Further, there are redundant survey slots for
> multi-radio/single-phy instances.
>
> Fix by moving the survey data into ath12k_hw so multiple radios under a
> single wiphy share one table, and index into it using the global
> mac80211 index. A new spinlock in ath12k_hw serialises access to the
> survey array, which is now shared across all radios under a single hw.
>
> Band busy-times Before this fix:
>
> 2.4 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 5 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
> 6 GHz: 9, 2, 2, 2, 4, 2, 10, 16, 4, 12, 5
>
> After this fix, times are independent:
>
> 2.4 GHz: 23, 5, 5, 12, 2, 12, 26, 5, 3, 1, 27
> 5 GHz: 30, 40, 29, 27, 118, 118, 112, 120, 11, 11, 11
> 6 GHz: 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1
>
> Tested-on: wcn7850 hw2.0 PCI WLAN.IOE_HMT.1.1-00018-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1
>
> Fixes: 4f242b1d6996 ("wifi: ath12k: support get_survey mac op for single wiphy")
> Signed-off-by: Matthew Leach <matthew.leach@collabora.com>
> ---
my review agent flagged:
"file": "drivers/net/wireless/ath/ath12k/core.h",
"summary": "data_lock comment still lists 'survey info' as one of the fields
it protects, but survey data is now protected by ah->survey_lock",
"failure_scenario": "A developer adding survey-related code follows the
comment and uses data_lock to protect ah->survey[], introducing an unprotected
access (since the actual readers/writers use survey_lock, not data_lock)."
I'll update the comment in my pending branch
/jeff
^ permalink raw reply
* [PATCH v2] wifi: ath12k: restore country code during resume
From: Stian Knudsen @ 2026-07-20 12:09 UTC (permalink / raw)
To: Jeff Johnson, ath12k; +Cc: linux-wireless, Baochen Qiang, Stian Knudsen
In-Reply-To: <df37460e-f8ba-4e11-b412-4aa8b17cd32e@oss.qualcomm.com>
The country code configured before suspend is lost after resume:
the device is powered down in suspend_late and powered back up in
resume_early, so firmware reboots with its default regulatory
settings and the previously set country code is no longer applied.
On WCN7850 the firmware comes back in the world regulatory domain
(country 00) and takes ~7 seconds to rediscover the country code
from AP beacons, part of a ~16 second total delay before wifi
reconnects after resume.
Restore it by resending WMI_SET_CURRENT_COUNTRY_CMDID during resume
if a country code was set before suspend, i.e. when ar->alpha2 is
valid. This follows the same approach as ath11k commit 7f0343b7b871
("wifi: ath11k: restore country code during resume").
Note that only single_pdev_only devices support suspend/resume (see
ath12k_core_continue_suspend_resume()), so handling the first and
only pdev is sufficient.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
Fixes: 8d5f4da8d70b ("wifi: ath12k: support suspend/resume")
Suggested-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
Link: https://lore.kernel.org/r/-_iDJ_M5RrqACWB0qmtleg@pokerfj.es
Signed-off-by: Stian Knudsen <stian@pokerfj.es>
---
v2:
- Add Fixes tag, per Baochen's review comment.
- Link to v1: https://lore.kernel.org/r/20260717130254.36732-1-stian@pokerfj.es
---
drivers/net/wireless/ath/ath12k/core.c | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -197,6 +197,7 @@ EXPORT_SYMBOL(ath12k_core_resume_early);
int ath12k_core_resume(struct ath12k_base *ab)
{
+ struct ath12k *ar;
long time_left;
int ret;
@@ -211,6 +212,26 @@ int ath12k_core_resume(struct ath12k_base *ab)
return -ETIMEDOUT;
}
+ /* So far only single_pdev_only devices can reach here,
+ * so it is valid to handle the first, and the only, pdev.
+ */
+ ar = ab->pdevs[0].ar;
+ if (ab->hw_params->current_cc_support &&
+ ar->alpha2[0] != 0 && ar->alpha2[1] != 0) {
+ struct wmi_set_current_country_arg arg = {};
+
+ memcpy(&arg.alpha2, ar->alpha2, 2);
+
+ reinit_completion(&ar->regd_update_completed);
+
+ ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg);
+ if (ret) {
+ ath12k_warn(ab, "failed to set country code during resume: %d\n",
+ ret);
+ return ret;
+ }
+ }
+
return 0;
}
EXPORT_SYMBOL(ath12k_core_resume);
--
2.50.0
^ permalink raw reply
* [PATCH v2] wifi: mwifiex: validate event lengths before copying bodies
From: Pengpeng Hou @ 2026-07-20 11:51 UTC (permalink / raw)
To: Brian Norris
Cc: Francesco Dolcini, linux-wireless, linux-kernel, Pengpeng Hou
mwifiex event packets contain a four-byte event cause followed by the
event body. The USB and SDIO paths copy from data after that header using
the full packet length, so the source range extends four bytes beyond the
skb. The SDIO path also reads the event cause before validating the
packet and publishes oversized events without a copied body.
Reject SDIO events that are shorter than the header or larger than the
event buffer limit, using the same skb-release path for both failures.
Retain the equivalent USB bounds and copy only the bytes after the event
header in both paths.
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
Changes since v1: https://lore.kernel.org/all/20260704011317.50900-1-pengpeng@iscas.ac.cn/
- reject both short and oversized SDIO events through one cleanup path
- copy only the body length in the SDIO and USB paths
- rebase onto v7.2-rc4
drivers/net/wireless/marvell/mwifiex/sdio.c | 15 +++++++++++----
drivers/net/wireless/marvell/mwifiex/usb.c | 3 ++-
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c
index f039d6f19183..4101bf1edca1 100644
--- a/drivers/net/wireless/marvell/mwifiex/sdio.c
+++ b/drivers/net/wireless/marvell/mwifiex/sdio.c
@@ -1712,12 +1712,19 @@ static int mwifiex_decode_rx_packet(struct mwifiex_adapter *adapter,
case MWIFIEX_TYPE_EVENT:
mwifiex_dbg(adapter, EVENT,
"info: --- Rx: Event ---\n");
+ if (skb->len < MWIFIEX_EVENT_HEADER_LEN ||
+ skb->len > MAX_EVENT_SIZE) {
+ mwifiex_dbg(adapter, ERROR,
+ "EVENT: invalid skb->len %u\n", skb->len);
+ dev_kfree_skb_any(skb);
+ return -1;
+ }
+
adapter->event_cause = get_unaligned_le32(skb->data);
- if ((skb->len > 0) && (skb->len < MAX_EVENT_SIZE))
- memcpy(adapter->event_body,
- skb->data + MWIFIEX_EVENT_HEADER_LEN,
- skb->len);
+ memcpy(adapter->event_body,
+ skb->data + MWIFIEX_EVENT_HEADER_LEN,
+ skb->len - MWIFIEX_EVENT_HEADER_LEN);
/* event cause has been saved to adapter->event_cause */
adapter->event_received = true;
diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c
index f4b94a1054f6..a6ed82e7228b 100644
--- a/drivers/net/wireless/marvell/mwifiex/usb.c
+++ b/drivers/net/wireless/marvell/mwifiex/usb.c
@@ -110,7 +110,8 @@ static int mwifiex_usb_recv(struct mwifiex_adapter *adapter,
}
memcpy(adapter->event_body, skb->data +
- MWIFIEX_EVENT_HEADER_LEN, skb->len);
+ MWIFIEX_EVENT_HEADER_LEN,
+ skb->len - MWIFIEX_EVENT_HEADER_LEN);
adapter->event_received = true;
adapter->event_skb = skb;
^ permalink raw reply related
* Re: [PATCH ath-current] wifi: ath12k: fix frequency range for single-pdev devices
From: Shenghan Gao @ 2026-07-20 9:25 UTC (permalink / raw)
To: Baochen Qiang
Cc: Jeff Johnson, Vasanthakumar Thiagarajan, linux-wireless, ath12k,
linux-kernel
In-Reply-To: <5ca16013-e8a0-4403-a2b3-b3b43ea2bb2d@oss.qualcomm.com>
The update sequence is as follows.
ath12k_regd_update() first resets ar->freq_range to zero. On the
tested WCN7850 under the CN regulatory domain, the 2 GHz branch
calculates a valid range, so the first call to
ath12k_mac_update_freq_range() sets ar->freq_range to 2402-2482 MHz.
The existing 5 GHz branch is skipped because ar->supports_6ghz is true.
When the new regulatory domain is built, reg_freq_6ghz.end_freq is
reset to zero. Since the CN regulatory event contains no 6 GHz rules,
it remains zero. The 6 GHz branch therefore calculates freq_high as
zero, and ath12k_mac_update_freq_range() returns without extending the
existing range.
Consequently, ar->freq_range remains 2402-2482 MHz, and the subsequent
channel-list update filters out all 5 GHz channels.
With this patch, the 5 GHz branch also runs for single-pdev devices and
merges the valid 5 GHz range, extending ar->freq_range to
2402-5835 MHz.
Baochen Qiang <baochen.qiang@oss.qualcomm.com> 于2026年7月20日周一 16:34写道:
>
>
>
> On 7/15/2026 2:52 PM, Shenghan Gao wrote:
> > Commit 0d777aa2ca77 ("wifi: ath12k: fix mac pdev frequency range update")
> > made ath12k_regd_update() handle each supported band independently.
> > However, it uses WMI band capability values as indices into
> > pdev->cap.band[]. Those values are bit flags, while cap.band[] is indexed
> > by enum nl80211_band. As a result, the 2.4 GHz lookup reads the
> > 5 GHz entry, while the 5 GHz lookup reads the 60 GHz entry.
> >
> > Also, the 5 GHz range is skipped whenever the radio supports 6 GHz. This
> > is valid when 5 and 6 GHz belong to separate pdevs, but not for single-pdev
> > devices such as WCN7850, where the same pdev covers both bands. After a
> > regulatory update, 5 GHz is therefore omitted from ar->freq_range and later
> > filtered out of the channel list sent to firmware.
> >
> > On the tested WCN7850, the 11d regulatory update left the frequency range
> > at 2402-2482 MHz and sent 13 channels to firmware. A subsequent 5 GHz scan
>
> can you share more details on how the frequency range is updated to cover only 2 GHz band ?
>
> > failed with WMI_SCAN_REASON_INTERNAL_FAILURE. With both ranges combined,
> > the range is 2402-5835 MHz and 26 channels are sent to firmware.
> >
> > Index cap.band[] with NL80211_BAND_* and update 5 GHz for single-pdev
> > devices even when 6 GHz is supported.
> >
> > Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
> >
> > Fixes: 0d777aa2ca77 ("wifi: ath12k: fix mac pdev frequency range update")
> > Cc: stable@vger.kernel.org
> > Assisted-by: Codex:GPT-5.6 Sol
> > Signed-off-by: Shenghan Gao <gsh20040816@gmail.com>
> > ---
> > Testing notes:
> >
> > - Runtime testing was performed on WCN7850 under the CN regulatory domain.
> > - 2.4 and 5 GHz scanning and 5 GHz association were verified.
> > - 6 GHz operation was not tested because it is unavailable under the CN
> > regulatory domain.
> > - QCC2072 was not tested because the hardware was not available.
> >
> > drivers/net/wireless/ath/ath12k/reg.c | 7 ++++---
> > 1 file changed, 4 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
> > index 89abf2e87ad1..c3bb1df2b1e2 100644
> > --- a/drivers/net/wireless/ath/ath12k/reg.c
> > +++ b/drivers/net/wireless/ath/ath12k/reg.c
> > @@ -300,7 +300,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
> >
> > if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
> > if (ab->hw_params->single_pdev_only) {
> > - phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_2GHZ_CAP].phy_id;
> > + phy_id = ar->pdev->cap.band[NL80211_BAND_2GHZ].phy_id;
> > reg_cap = &ab->hal_reg_cap[phy_id];
> > }
> >
> > @@ -310,9 +310,10 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
> > ath12k_mac_update_freq_range(ar, freq_low, freq_high);
> > }
> >
> > - if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) {
> > + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
> > + (!ar->supports_6ghz || ab->hw_params->single_pdev_only)) {
> > if (ab->hw_params->single_pdev_only) {
> > - phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id;
> > + phy_id = ar->pdev->cap.band[NL80211_BAND_5GHZ].phy_id;
> > reg_cap = &ab->hal_reg_cap[phy_id];
> > }
> >
> >
> > base-commit: a1a21995c2e1cc2ca6b2226cfe4f5f018370182a
>
^ permalink raw reply
* [PATCH mt76 v2] wifi: mt76: fix handling channel context with different bands in mt76_switch_vif_chanctx()
From: Shayne Chen @ 2026-07-20 9:01 UTC (permalink / raw)
To: Felix Fietkau
Cc: linux-wireless, Lorenzo Bianconi, Ryder Lee, Evelyn Tsai,
Money Wang, linux-mediatek, Shayne Chen, Rex Lu
When performing channel switches on different radios within a short
timeframe, channel contexts with different bands can be carried for
each struct ieee80211_vif_chanctx_switch.
Rework mt76_switch_vif_chanctx() to properly handle this scenario.
Fixes: 82334623af0c ("wifi: mt76: add chanctx functions for multi-channel phy support")
Co-developed-by: Rex Lu <rex.lu@mediatek.com>
Signed-off-by: Rex Lu <rex.lu@mediatek.com>
Signed-off-by: Shayne Chen <shayne.chen@mediatek.com>
---
v2:
- prevent from doing duplicated works if need_update[band] is marked
---
drivers/net/wireless/mediatek/mt76/channel.c | 92 +++++++++++---------
1 file changed, 49 insertions(+), 43 deletions(-)
diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c
index 6edcb3b8f279..28ad7bcaffd4 100644
--- a/drivers/net/wireless/mediatek/mt76/channel.c
+++ b/drivers/net/wireless/mediatek/mt76/channel.c
@@ -186,68 +186,74 @@ int mt76_switch_vif_chanctx(struct ieee80211_hw *hw,
int n_vifs,
enum ieee80211_chanctx_switch_mode mode)
{
- struct mt76_chanctx *old_ctx = (struct mt76_chanctx *)vifs->old_ctx->drv_priv;
- struct mt76_chanctx *new_ctx = (struct mt76_chanctx *)vifs->new_ctx->drv_priv;
- struct ieee80211_chanctx_conf *conf = vifs->new_ctx;
- struct mt76_phy *old_phy = old_ctx->phy;
- struct mt76_phy *phy = hw->priv;
+ struct ieee80211_vif_chanctx_switch *v;
+ struct mt76_chanctx *old_ctx, *new_ctx;
+ struct mt76_phy *old_phy, *phy = hw->priv;
struct mt76_dev *dev = phy->dev;
struct mt76_vif_link *mlink;
- bool update_chan;
+ bool need_update[__MT_MAX_BAND] = {};
int i, ret = 0;
- if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
- phy = new_ctx->phy = dev->band_phys[conf->def.chan->band];
- else
- phy = new_ctx->phy;
- if (!phy)
- return -EINVAL;
+ for (i = 0; i < n_vifs; i++) {
+ v = &vifs[i];
+ new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv;
+ if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS)
+ phy = new_ctx->phy = dev->band_phys[v->new_ctx->def.chan->band];
+ else
+ phy = new_ctx->phy;
- update_chan = phy->chanctx != new_ctx;
- if (update_chan) {
- if (dev->scan.phy == phy)
- mt76_abort_scan(dev);
+ if (!phy)
+ return -EINVAL;
- cancel_delayed_work_sync(&phy->mac_work);
+ if (need_update[phy->band_idx])
+ continue;
+
+ if (phy->chanctx != new_ctx) {
+ if (dev->scan.phy == phy)
+ mt76_abort_scan(dev);
+
+ cancel_delayed_work_sync(&phy->mac_work);
+ need_update[phy->band_idx] = true;
+ }
}
mutex_lock(&dev->mutex);
- if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS &&
- phy != old_phy && old_phy->chanctx == old_ctx)
- old_phy->chanctx = NULL;
+ for (i = 0; i < n_vifs; i++) {
+ v = &vifs[i];
+ old_ctx = (struct mt76_chanctx *)v->old_ctx->drv_priv;
+ old_phy = old_ctx->phy;
+
+ new_ctx = (struct mt76_chanctx *)v->new_ctx->drv_priv;
+ phy = new_ctx->phy;
- if (update_chan)
- ret = mt76_phy_update_channel(phy, vifs->new_ctx);
+ if (mode == CHANCTX_SWMODE_SWAP_CONTEXTS && old_phy->chanctx &&
+ old_phy->chanctx == old_ctx && phy != old_phy)
+ old_phy->chanctx = NULL;
- if (ret)
- goto out;
+ if (need_update[phy->band_idx]) {
+ ret = mt76_phy_update_channel(phy, v->new_ctx);
+ if (ret)
+ goto out;
- if (old_phy == phy)
- goto skip_link_replace;
+ need_update[phy->band_idx] = false;
+ }
- for (i = 0; i < n_vifs; i++) {
- mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
+ mlink = mt76_vif_conf_link(dev, v->vif, v->link_conf);
if (!mlink)
continue;
- dev->drv->vif_link_remove(old_phy, vifs[i].vif,
- vifs[i].link_conf, mlink);
-
- ret = dev->drv->vif_link_add(phy, vifs[i].vif,
- vifs[i].link_conf, mlink);
- if (ret)
- goto out;
-
- }
+ if (old_phy != phy) {
+ dev->drv->vif_link_remove(old_phy, v->vif, v->link_conf,
+ mlink);
-skip_link_replace:
- for (i = 0; i < n_vifs; i++) {
- mlink = mt76_vif_conf_link(dev, vifs[i].vif, vifs[i].link_conf);
- if (!mlink)
- continue;
+ ret = dev->drv->vif_link_add(phy, v->vif, v->link_conf,
+ mlink);
+ if (ret)
+ goto out;
+ }
- mlink->ctx = vifs->new_ctx;
+ mlink->ctx = v->new_ctx;
if (mlink->beacon_mon_interval)
WRITE_ONCE(mlink->beacon_mon_last, jiffies);
}
--
2.51.0
^ permalink raw reply related
* Re: [PATCH ath-current] wifi: ath12k: fix frequency range for single-pdev devices
From: Baochen Qiang @ 2026-07-20 8:34 UTC (permalink / raw)
To: Shenghan Gao, Jeff Johnson
Cc: Aditya Kumar Singh, Vasanthakumar Thiagarajan, linux-wireless,
ath12k, linux-kernel
In-Reply-To: <20260715065218.41232-1-gsh20040816@gmail.com>
On 7/15/2026 2:52 PM, Shenghan Gao wrote:
> Commit 0d777aa2ca77 ("wifi: ath12k: fix mac pdev frequency range update")
> made ath12k_regd_update() handle each supported band independently.
> However, it uses WMI band capability values as indices into
> pdev->cap.band[]. Those values are bit flags, while cap.band[] is indexed
> by enum nl80211_band. As a result, the 2.4 GHz lookup reads the
> 5 GHz entry, while the 5 GHz lookup reads the 60 GHz entry.
>
> Also, the 5 GHz range is skipped whenever the radio supports 6 GHz. This
> is valid when 5 and 6 GHz belong to separate pdevs, but not for single-pdev
> devices such as WCN7850, where the same pdev covers both bands. After a
> regulatory update, 5 GHz is therefore omitted from ar->freq_range and later
> filtered out of the channel list sent to firmware.
>
> On the tested WCN7850, the 11d regulatory update left the frequency range
> at 2402-2482 MHz and sent 13 channels to firmware. A subsequent 5 GHz scan
can you share more details on how the frequency range is updated to cover only 2 GHz band ?
> failed with WMI_SCAN_REASON_INTERNAL_FAILURE. With both ranges combined,
> the range is 2402-5835 MHz and 26 channels are sent to firmware.
>
> Index cap.band[] with NL80211_BAND_* and update 5 GHz for single-pdev
> devices even when 6 GHz is supported.
>
> Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c7-00108-QCAHMTSWPL_V1.0_V2.0_SILICONZ_UPSTREAM-3
>
> Fixes: 0d777aa2ca77 ("wifi: ath12k: fix mac pdev frequency range update")
> Cc: stable@vger.kernel.org
> Assisted-by: Codex:GPT-5.6 Sol
> Signed-off-by: Shenghan Gao <gsh20040816@gmail.com>
> ---
> Testing notes:
>
> - Runtime testing was performed on WCN7850 under the CN regulatory domain.
> - 2.4 and 5 GHz scanning and 5 GHz association were verified.
> - 6 GHz operation was not tested because it is unavailable under the CN
> regulatory domain.
> - QCC2072 was not tested because the hardware was not available.
>
> drivers/net/wireless/ath/ath12k/reg.c | 7 ++++---
> 1 file changed, 4 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c
> index 89abf2e87ad1..c3bb1df2b1e2 100644
> --- a/drivers/net/wireless/ath/ath12k/reg.c
> +++ b/drivers/net/wireless/ath/ath12k/reg.c
> @@ -300,7 +300,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
>
> if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) {
> if (ab->hw_params->single_pdev_only) {
> - phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_2GHZ_CAP].phy_id;
> + phy_id = ar->pdev->cap.band[NL80211_BAND_2GHZ].phy_id;
> reg_cap = &ab->hal_reg_cap[phy_id];
> }
>
> @@ -310,9 +310,10 @@ int ath12k_regd_update(struct ath12k *ar, bool init)
> ath12k_mac_update_freq_range(ar, freq_low, freq_high);
> }
>
> - if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) {
> + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP &&
> + (!ar->supports_6ghz || ab->hw_params->single_pdev_only)) {
> if (ab->hw_params->single_pdev_only) {
> - phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id;
> + phy_id = ar->pdev->cap.band[NL80211_BAND_5GHZ].phy_id;
> reg_cap = &ab->hal_reg_cap[phy_id];
> }
>
>
> base-commit: a1a21995c2e1cc2ca6b2226cfe4f5f018370182a
^ permalink raw reply
* [PATCH v3 3/3] wifi: ath12k: implement custom wake_tx_queue with flow control
From: Jose Ignacio Tornos Martinez @ 2026-07-20 7:08 UTC (permalink / raw)
To: jjohnson
Cc: ath11k, ath12k, linux-wireless, linux-kernel,
Jose Ignacio Tornos Martinez
In-Reply-To: <20260720070852.206495-1-jtornosm@redhat.com>
Under heavy traffic, ath12k can hang and experiences -ENOMEM errors
("failed to transmit frame -12") when the hardware TCL ring fills up.
This issue is more commonly observed in VMs with PCIe passthrough but
also occurs on bare metal systems.
Implement a custom wake_tx_queue operation that:
1. Checks hardware ring space before dequeuing packets from mac80211
2. Uses per-txq locking via txq->drv_priv to serialize peek and
dequeue operations for the same txq, preventing use-after-free
races between ieee80211_tx_peek() and ieee80211_tx_dequeue() when
multiple CPUs process the same txq concurrently, while keeping
different txqs fully parallel
3. Syncs with hardware state to get accurate free slot count
4. Uses ieee80211_tx_peek() to determine the exact target ring via
get_ring_selector(), matching dp_tx on all platforms
5. Returns early during firmware crash in the same way as other
tx paths
This approach follows the pattern used in the iwlwifi driver, adapted
for ath12k's hardware ring architecture.
This prevents hangs, eliminates -ENOMEM errors, and improves throughput
by optimizing resource usage and preventing unnecessary packet drops.
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v3: Address the review comments from Tamizh Raja:
- Replace per-ring wake_tx_lock with per-txq spinlock via
txq->drv_priv to fix race condition between ieee80211_tx_peek() and
ieee80211_tx_dequeue() when multiple CPUs process the same txq
- Fix MLO link selection in wake_tx_queue to use
rcu_dereference(ahvif->link[link_id]) instead of deflink, matching
the link selection logic in op_tx
v2: https://lore.kernel.org/all/20260715125017.277242-4-jtornosm@redhat.com/
drivers/net/wireless/ath/ath12k/hal.c | 1 +
drivers/net/wireless/ath/ath12k/mac.c | 17 +++++
drivers/net/wireless/ath/ath12k/mac.h | 5 ++
drivers/net/wireless/ath/ath12k/wifi7/hw.c | 87 +++++++++++++++++++++-
4 files changed, 109 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c
index a164563fff28..c1c656e4550b 100644
--- a/drivers/net/wireless/ath/ath12k/hal.c
+++ b/drivers/net/wireless/ath/ath12k/hal.c
@@ -390,6 +390,7 @@ int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng,
else
return ((srng->ring_size - hp + tp) / srng->entry_size) - 1;
}
+EXPORT_SYMBOL_GPL(ath12k_hal_srng_src_num_free);
void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab,
struct hal_srng *srng)
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 51c4df32e716..03618a8d3e65 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -7705,10 +7705,12 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
struct ath12k_link_vif *arvif;
struct ath12k_link_sta *arsta;
unsigned long valid_links;
+ struct ath12k_txq *atxq;
u16 selected_links = 0;
u8 link_id = 0, i;
struct ath12k *ar;
int ret = -EINVAL;
+ int tid;
struct ath12k_dp_peer_create_params dp_params = {};
lockdep_assert_wiphy(hw->wiphy);
@@ -7728,6 +7730,14 @@ int ath12k_mac_op_sta_state(struct ieee80211_hw *hw,
memset(ahsta, 0, sizeof(*ahsta));
ahsta->free_logical_link_idx_map = U16_MAX;
+ for (tid = 0; tid < ARRAY_SIZE(sta->txq); tid++) {
+ if (!sta->txq[tid])
+ continue;
+
+ atxq = (void *)sta->txq[tid]->drv_priv;
+ spin_lock_init(&atxq->lock);
+ }
+
arsta = &ahsta->deflink;
/* ML sta */
@@ -10790,6 +10800,7 @@ int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif);
struct ath12k_reg_info *reg_info;
struct ath12k_link_vif *arvif;
+ struct ath12k_txq *atxq;
struct ath12k_base *ab;
struct ath12k *ar;
int i;
@@ -10802,6 +10813,11 @@ int ath12k_mac_op_add_interface(struct ieee80211_hw *hw,
ahvif->vif = vif;
arvif = &ahvif->deflink;
+ if (vif->txq) {
+ atxq = (void *)vif->txq->drv_priv;
+ spin_lock_init(&atxq->lock);
+ }
+
ath12k_mac_init_arvif(ahvif, arvif, -1);
/* Allocate Default Queue now and reassign during actual vdev create */
@@ -14899,6 +14915,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah)
hw->vif_data_size = sizeof(struct ath12k_vif);
hw->sta_data_size = sizeof(struct ath12k_sta);
+ hw->txq_data_size = sizeof(struct ath12k_txq);
hw->extra_tx_headroom = ab->hw_params->iova_mask;
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h
index aba98afd4365..2855d8fdb99d 100644
--- a/drivers/net/wireless/ath/ath12k/mac.h
+++ b/drivers/net/wireless/ath/ath12k/mac.h
@@ -17,6 +17,11 @@ struct ath12k_hw;
struct ath12k_hw_group;
struct ath12k_pdev_map;
+struct ath12k_txq {
+ /* protects ieee80211_tx_peek/dequeue serialization per txq */
+ spinlock_t lock;
+};
+
struct ath12k_generic_iter {
struct ath12k *ar;
int ret;
diff --git a/drivers/net/wireless/ath/ath12k/wifi7/hw.c b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
index d9fdd2fc8298..86c3e9a8b87c 100644
--- a/drivers/net/wireless/ath/ath12k/wifi7/hw.c
+++ b/drivers/net/wireless/ath/ath12k/wifi7/hw.c
@@ -1100,9 +1100,94 @@ static void ath12k_wifi7_mac_op_tx(struct ieee80211_hw *hw,
}
}
+static void ath12k_wifi7_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(txq->vif);
+ struct ath12k_txq *atxq = (void *)txq->drv_priv;
+ struct ath12k_hw *ah = ath12k_hw_to_ah(hw);
+ struct ieee80211_tx_control control = {
+ .sta = txq->sta,
+ };
+ struct ieee80211_vif *vif = txq->vif;
+ const struct ath12k_hw_ops *ops;
+ const struct sk_buff *peek_skb;
+ struct ath12k_link_vif *arvif;
+ struct dp_tx_ring *tx_ring;
+ struct hal_srng *tcl_ring;
+ struct ath12k_sta *ahsta;
+ struct ath12k_dp *dp;
+ struct sk_buff *skb;
+ struct ath12k *ar;
+ u32 ring_selector;
+ int num_free;
+ u8 ring_id;
+ u8 link_id;
+
+ while (1) {
+ if (unlikely(test_bit(ATH12K_FLAG_CRASH_FLUSH,
+ &ah->radio[0].ab->dev_flags)))
+ break;
+
+ spin_lock_bh(&atxq->lock);
+
+ peek_skb = ieee80211_tx_peek(hw, txq);
+ if (!peek_skb) {
+ spin_unlock_bh(&atxq->lock);
+ break;
+ }
+
+ if (ieee80211_vif_is_mld(vif) && txq->sta) {
+ ahsta = ath12k_sta_to_ahsta(txq->sta);
+ link_id = ahsta->assoc_link_id;
+ } else {
+ link_id = ahvif->deflink.link_id;
+ }
+
+ rcu_read_lock();
+
+ arvif = rcu_dereference(ahvif->link[link_id]);
+ if (!arvif || !arvif->ar) {
+ rcu_read_unlock();
+ spin_unlock_bh(&atxq->lock);
+ break;
+ }
+
+ ar = arvif->ar;
+ dp = ar->ab->dp;
+
+ ops = dp->hw_params->hw_ops;
+ ring_selector = ops->get_ring_selector((struct sk_buff *)peek_skb);
+ ring_id = ring_selector % dp->hw_params->max_tx_ring;
+
+ tx_ring = &dp->tx_ring[ring_id];
+ tcl_ring = &dp->hal->srng_list[tx_ring->tcl_data_ring.ring_id];
+
+ spin_lock(&tcl_ring->lock);
+ num_free = ath12k_hal_srng_src_num_free(ar->ab, tcl_ring, true);
+ spin_unlock(&tcl_ring->lock);
+
+ if (num_free == 0) {
+ rcu_read_unlock();
+ spin_unlock_bh(&atxq->lock);
+ break;
+ }
+
+ skb = ieee80211_tx_dequeue(hw, txq);
+
+ rcu_read_unlock();
+ spin_unlock_bh(&atxq->lock);
+
+ if (!skb)
+ break;
+
+ ath12k_wifi7_mac_op_tx(hw, &control, skb);
+ }
+}
+
static const struct ieee80211_ops ath12k_ops_wifi7 = {
.tx = ath12k_wifi7_mac_op_tx,
- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = ath12k_wifi7_mac_op_wake_tx_queue,
.start = ath12k_mac_op_start,
.stop = ath12k_mac_op_stop,
.reconfig_complete = ath12k_mac_op_reconfig_complete,
--
2.54.0
^ permalink raw reply related
* [PATCH v3 1/3] wifi: mac80211: add ieee80211_tx_peek API
From: Jose Ignacio Tornos Martinez @ 2026-07-20 7:08 UTC (permalink / raw)
To: jjohnson
Cc: ath11k, ath12k, linux-wireless, linux-kernel,
Jose Ignacio Tornos Martinez
In-Reply-To: <20260720070852.206495-1-jtornosm@redhat.com>
Add ieee80211_tx_peek() to allow drivers to inspect the next frame
in a TXQ without removing it.
Drivers implementing custom wake_tx_queue operations may need to
determine which hardware TX ring to use before dequeuing a packet,
using properties like the skb hash or queue mapping that are only
available from the skb itself.
The function checks pending fragments first, then iterates through
all fair-queue flows (new_flows and old_flows) to find the first
queued frame, matching the iteration behavior of fq_tin_dequeue.
It also pre-caches the skb hash via skb_get_hash() so that
hash-based ring selectors see a stable value through the subsequent
dequeue and TX path.
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v3: no modification
v2: https://lore.kernel.org/all/20260715125017.277242-2-jtornosm@redhat.com/
include/net/mac80211.h | 20 ++++++++++++++++++++
net/mac80211/tx.c | 39 +++++++++++++++++++++++++++++++++++++++
2 files changed, 59 insertions(+)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 4f95da023746..9289b8dca972 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -7742,6 +7742,26 @@ void ieee80211_unreserve_tid(struct ieee80211_sta *sta, u8 tid);
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq);
+/**
+ * ieee80211_tx_peek - peek at the next packet in a software tx queue
+ *
+ * @hw: pointer as obtained from ieee80211_alloc_hw()
+ * @txq: pointer obtained from station or virtual interface, or from
+ * ieee80211_next_txq()
+ *
+ * Return: the next skb without dequeuing it, or %NULL if the queue is empty.
+ * The returned pointer is const — the caller must not modify or free the skb.
+ * The skb remains queued and will be returned by a subsequent
+ * ieee80211_tx_dequeue() call.
+ *
+ * This is useful for drivers that need to inspect the next frame (e.g. to
+ * determine the target TX ring) before deciding whether to dequeue.
+ *
+ * Must be called in the same context as ieee80211_tx_dequeue().
+ */
+const struct sk_buff *ieee80211_tx_peek(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq);
+
/**
* ieee80211_tx_dequeue_ni - dequeue a packet from a software tx queue
* (in process context)
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index c13b209fad47..91bfd8ef9428 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -3857,6 +3857,45 @@ static bool ieee80211_xmit_fast(struct ieee80211_sub_if_data *sdata,
return true;
}
+const struct sk_buff *ieee80211_tx_peek(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct txq_info *txqi = container_of(txq, struct txq_info, txq);
+ struct ieee80211_local *local = hw_to_local(hw);
+ struct fq_tin *tin = &txqi->tin;
+ struct fq *fq = &local->fq;
+ struct sk_buff *skb = NULL;
+ struct fq_flow *flow;
+
+ WARN_ON_ONCE(softirq_count() == 0);
+
+ spin_lock_bh(&fq->lock);
+
+ skb = skb_peek(&txqi->frags);
+ if (skb)
+ goto out;
+
+ list_for_each_entry(flow, &tin->new_flows, flowchain) {
+ skb = skb_peek(&flow->queue);
+ if (skb)
+ goto out;
+ }
+
+ list_for_each_entry(flow, &tin->old_flows, flowchain) {
+ skb = skb_peek(&flow->queue);
+ if (skb)
+ goto out;
+ }
+
+out:
+ if (skb)
+ skb_get_hash(skb);
+
+ spin_unlock_bh(&fq->lock);
+ return skb;
+}
+EXPORT_SYMBOL_GPL(ieee80211_tx_peek);
+
struct sk_buff *ieee80211_tx_dequeue(struct ieee80211_hw *hw,
struct ieee80211_txq *txq)
{
--
2.54.0
^ permalink raw reply related
* [PATCH v3 2/3] wifi: ath11k: implement custom wake_tx_queue with flow control
From: Jose Ignacio Tornos Martinez @ 2026-07-20 7:08 UTC (permalink / raw)
To: jjohnson
Cc: ath11k, ath12k, linux-wireless, linux-kernel,
Jose Ignacio Tornos Martinez
In-Reply-To: <20260720070852.206495-1-jtornosm@redhat.com>
Under heavy traffic, ath11k experiences frequent -ENOMEM errors
("failed to transmit frame -12") when the hardware TCL ring fills up.
This issue is more commonly observed in VMs with PCIe passthrough but
also occurs on bare metal systems. It is particularly problematic on
devices with a single shared TCL ring where all traffic classes
compete for the same 512 descriptor slots.
Implement a custom wake_tx_queue operation that:
1. Checks hardware ring space before dequeuing packets from mac80211
2. Uses per-txq locking via txq->drv_priv to serialize peek and
dequeue operations for the same txq, preventing use-after-free
races between ieee80211_tx_peek() and ieee80211_tx_dequeue() when
multiple CPUs process the same txq concurrently, while keeping
different txqs fully parallel
3. Syncs with hardware state to get accurate free slot count
4. Uses ieee80211_tx_peek() to determine the exact target ring via
get_ring_selector(), matching dp_tx on all platforms
5. Returns early during firmware crash in the same way as other
tx paths
This approach follows the pattern used in the iwlwifi driver, adapted
for ath11k's hardware ring architecture.
This eliminates -ENOMEM errors and improves throughput by optimizing
resource usage and preventing unnecessary packet drops.
Signed-off-by: Jose Ignacio Tornos Martinez <jtornosm@redhat.com>
---
v3: Address the review comments from Tamizh Raja:
- Replace per-ring wake_tx_lock with per-txq spinlock via
txq->drv_priv to fix race condition between ieee80211_tx_peek() and
ieee80211_tx_dequeue() when multiple CPUs process the same txq
v2: https://lore.kernel.org/all/20260715125017.277242-3-jtornosm@redhat.com/
drivers/net/wireless/ath/ath11k/mac.c | 80 ++++++++++++++++++++++++++-
drivers/net/wireless/ath/ath11k/mac.h | 5 ++
2 files changed, 84 insertions(+), 1 deletion(-)
diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c
index 2d55cdc4d165..1eb5edca4ee6 100644
--- a/drivers/net/wireless/ath/ath11k/mac.c
+++ b/drivers/net/wireless/ath/ath11k/mac.c
@@ -7079,6 +7079,7 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
struct vdev_create_params vdev_param = {};
struct peer_create_params peer_param;
u32 param_id, param_value;
+ struct ath11k_txq *atxq;
u16 nss;
int i;
int ret, fbret;
@@ -7112,6 +7113,11 @@ static int ath11k_mac_op_add_interface(struct ieee80211_hw *hw,
INIT_DELAYED_WORK(&arvif->connection_loss_work,
ath11k_mac_vif_sta_connection_loss_work);
+ if (vif->txq) {
+ atxq = (void *)vif->txq->drv_priv;
+ spin_lock_init(&atxq->lock);
+ }
+
for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) {
arvif->bitrate_mask.control[i].legacy = 0xffffffff;
arvif->bitrate_mask.control[i].gi = NL80211_TXRATE_FORCE_SGI;
@@ -9938,7 +9944,9 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
enum ieee80211_ap_reg_power power_type;
struct cur_regulatory_info *reg_info;
struct ath11k_peer *peer;
+ struct ath11k_txq *atxq;
int ret = 0;
+ int tid;
/* cancel must be done outside the mutex to avoid deadlock */
if ((old_state == IEEE80211_STA_NONE &&
@@ -9957,6 +9965,14 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
INIT_WORK(&arsta->update_wk, ath11k_sta_rc_update_wk);
INIT_WORK(&arsta->set_4addr_wk, ath11k_sta_set_4addr_wk);
+ for (tid = 0; tid < ARRAY_SIZE(sta->txq); tid++) {
+ if (!sta->txq[tid])
+ continue;
+
+ atxq = (void *)sta->txq[tid]->drv_priv;
+ spin_lock_init(&atxq->lock);
+ }
+
ret = ath11k_mac_station_add(ar, vif, sta);
if (ret)
ath11k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n",
@@ -10065,9 +10081,70 @@ static int ath11k_mac_op_sta_state(struct ieee80211_hw *hw,
return ret;
}
+static void ath11k_mac_op_wake_tx_queue(struct ieee80211_hw *hw,
+ struct ieee80211_txq *txq)
+{
+ struct ath11k_txq *atxq = (void *)txq->drv_priv;
+ struct ieee80211_tx_control control = {
+ .sta = txq->sta,
+ };
+ const struct ath11k_hw_ops *ops;
+ const struct sk_buff *peek_skb;
+ struct ath11k *ar = hw->priv;
+ struct dp_tx_ring *tx_ring;
+ struct hal_srng *tcl_ring;
+ struct sk_buff *skb;
+ u32 ring_selector;
+ int num_free;
+ u8 ring_id;
+
+ if (!ar)
+ return;
+
+ while (1) {
+ if (unlikely(test_bit(ATH11K_FLAG_CRASH_FLUSH,
+ &ar->ab->dev_flags)))
+ break;
+
+ spin_lock_bh(&atxq->lock);
+
+ peek_skb = ieee80211_tx_peek(hw, txq);
+ if (!peek_skb) {
+ spin_unlock_bh(&atxq->lock);
+ break;
+ }
+
+ ops = ar->ab->hw_params.hw_ops;
+ ring_selector = ops->get_ring_selector((struct sk_buff *)peek_skb);
+ ring_id = ring_selector %
+ ar->ab->hw_params.hal_params->num_tx_rings;
+
+ tx_ring = &ar->ab->dp.tx_ring[ring_id];
+ tcl_ring = &ar->ab->hal.srng_list[tx_ring->tcl_data_ring.ring_id];
+
+ spin_lock(&tcl_ring->lock);
+ num_free = ath11k_hal_srng_src_num_free(ar->ab, tcl_ring, true);
+ spin_unlock(&tcl_ring->lock);
+
+ if (num_free == 0) {
+ spin_unlock_bh(&atxq->lock);
+ break;
+ }
+
+ skb = ieee80211_tx_dequeue(hw, txq);
+
+ spin_unlock_bh(&atxq->lock);
+
+ if (!skb)
+ break;
+
+ ath11k_mac_op_tx(hw, &control, skb);
+ }
+}
+
static const struct ieee80211_ops ath11k_ops = {
.tx = ath11k_mac_op_tx,
- .wake_tx_queue = ieee80211_handle_wake_tx_queue,
+ .wake_tx_queue = ath11k_mac_op_wake_tx_queue,
.start = ath11k_mac_op_start,
.stop = ath11k_mac_op_stop,
.reconfig_complete = ath11k_mac_op_reconfig_complete,
@@ -10590,6 +10667,7 @@ static int __ath11k_mac_register(struct ath11k *ar)
ar->hw->vif_data_size = sizeof(struct ath11k_vif);
ar->hw->sta_data_size = sizeof(struct ath11k_sta);
+ ar->hw->txq_data_size = sizeof(struct ath11k_txq);
wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
wiphy_ext_feature_set(ar->hw->wiphy, NL80211_EXT_FEATURE_STA_TX_PWR);
diff --git a/drivers/net/wireless/ath/ath11k/mac.h b/drivers/net/wireless/ath/ath11k/mac.h
index 59f83c7175fd..b0116ced9c67 100644
--- a/drivers/net/wireless/ath/ath11k/mac.h
+++ b/drivers/net/wireless/ath/ath11k/mac.h
@@ -14,6 +14,11 @@
struct ath11k;
struct ath11k_base;
+struct ath11k_txq {
+ /* protects ieee80211_tx_peek/dequeue serialization per txq */
+ spinlock_t lock;
+};
+
struct ath11k_generic_iter {
struct ath11k *ar;
int ret;
--
2.54.0
^ permalink raw reply related
* [PATCH v3 0/3] wifi: ath11k/ath12k: implement TX flow control
From: Jose Ignacio Tornos Martinez @ 2026-07-20 7:08 UTC (permalink / raw)
To: jjohnson
Cc: ath11k, ath12k, linux-wireless, linux-kernel,
Jose Ignacio Tornos Martinez
This series implements custom wake_tx_queue operations for ath11k and
ath12k drivers to prevent hardware ring overflow issues under heavy
traffic.
Without proper flow control, both drivers experience -ENOMEM errors
("failed to transmit frame -12") when the hardware TCL ring fills up.
Additionally, ath12k can hang under sustained high throughput. These
issues are more commonly observed in VMs with PCIe passthrough but
also occur on bare metal systems.
The implementation follows the pattern used in the iwlwifi driver,
checking hardware ring space before dequeuing packets from mac80211.
Per-txq locking via txq->drv_priv serializes peek and dequeue
operations for the same txq, preventing use-after-free races while
keeping different txqs fully parallel.
Testing shows stable operation with eliminated -ENOMEM errors, no hangs,
and improved throughput under heavy traffic conditions.
Jose Ignacio Tornos Martinez (3):
mac80211: add ieee80211_tx_peek API
wifi: ath11k: implement custom wake_tx_queue with flow control
wifi: ath12k: implement custom wake_tx_queue with flow control
v3: Address the review comments from Tamizh Raja:
- ath11k/ath12k: replace per-ring wake_tx_lock with per-txq spinlock via
txq->drv_priv to fix race condition between ieee80211_tx_peek() and
ieee80211_tx_dequeue() when multiple CPUs process the same txq
- ath12k: fix MLO link selection in wake_tx_queue to use
rcu_dereference(ahvif->link[link_id]) instead of deflink, matching
the link selection logic in op_tx
v2: https://lore.kernel.org/all/20260715125017.277242-1-jtornosm@redhat.com/
--
2.49.0
^ permalink raw reply
* Re: [PATCH] wifi: mwifiex: fix NULL dereference when the AP has HT-cap but no HT-oper
From: Francesco Dolcini @ 2026-07-20 7:05 UTC (permalink / raw)
To: Doruk Tan Ozturk
Cc: briannorris, francesco, linux-wireless, linux-kernel, stable
In-Reply-To: <20260716103042.88469-1-doruk@0sec.ai>
On Thu, Jul 16, 2026 at 12:30:42PM +0200, Doruk Tan Ozturk wrote:
> mwifiex_tdls_add_ht_oper() gates its follow-the-AP-bandwidth path on
> bss_desc->bcn_ht_cap being present, but then dereferences a different
> pointer, bss_desc->bcn_ht_oper:
>
> if (ISSUPP_CHANWIDTH40(priv->adapter->hw_dot_11n_dev_cap) &&
> bss_desc->bcn_ht_cap &&
> ISALLOWED_CHANWIDTH40(bss_desc->bcn_ht_oper->ht_param))
>
> bcn_ht_cap and bcn_ht_oper are populated independently while parsing the
> associated AP's beacon in mwifiex_update_bss_desc_with_ie(): an AP that
> advertises an HT Capabilities element but no HT Operation element leaves
> bcn_ht_cap non-NULL and bcn_ht_oper NULL. Setting up a TDLS link to a
> peer while associated to such an AP then dereferences the NULL
> bcn_ht_oper and crashes the kernel. Every other bcn_ht_oper user in the
> driver NULL-checks it first.
>
> Guard on the pointer that is actually dereferenced.
>
> Found by 0sec automated security-research tooling (https://0sec.ai).
>
> Fixes: 396939f94084 ("mwifiex: add HT operation IE in TDLS setup confirm")
> Cc: stable@vger.kernel.org
> Assisted-by: 0sec:multi-model
> Signed-off-by: Doruk Tan Ozturk <doruk@0sec.ai>
Reviewed-by: Francesco Dolcini <francesco.dolcini@toradex.com>
^ permalink raw reply
* [PATCH ath-current v2 8/8] wifi: ath12k: resolve PENDING ML peer ID from MLO_PEER_MAP HTT event
From: Baochen Qiang @ 2026-07-20 6:43 UTC (permalink / raw)
To: Jeff Johnson; +Cc: linux-wireless, ath12k, Baochen Qiang
In-Reply-To: <20260720-ath12k-fw-allocated-ml-peer-id-v2-0-630632758a80@oss.qualcomm.com>
Add ath12k_dp_peer_fixup_peer_id() and call it from the
HTT_T2H_MSG_TYPE_MLO_RX_PEER_MAP handler. For devices where the
firmware allocates the MLD peer ID, this is the point at which
all data structures that were left with ATH12K_MLO_PEER_ID_PENDING
or ATH12K_MLO_PEER_ID_INVALID get their real ID:
- dp_peer->peer_id is updated and the dp_peer is published into
dp_hw->dp_peers[];
- every existing dp_link_peer in dp_peer->link_peers[] gets its
ml_id set to the same value;
- ahsta->ml_peer_id is updated to the same value so peer_assoc,
sta_state and cleanup paths see a consistent ID.
Devices with host_alloc_ml_id == true also receive the same HTT
event, but the firmware-reported ID always matches the
host-allocated one and everything has already been populated by
ath12k_dp_peer_create(); Skips the helper entirely on those devices.
Tested-on: WCN7850 hw2.0 PCI WLAN.HMT.1.1.c5-00302-QCAHMTSWPL_V1.0_V2.0_SILICONZ-1.115823.3
Closes: https://bugzilla.kernel.org/show_bug.cgi?id=221039
Signed-off-by: Baochen Qiang <baochen.qiang@oss.qualcomm.com>
---
drivers/net/wireless/ath/ath12k/core.c | 2 ++
drivers/net/wireless/ath/ath12k/core.h | 1 +
drivers/net/wireless/ath/ath12k/dp_htt.c | 19 +++++++++++
drivers/net/wireless/ath/ath12k/dp_peer.c | 52 +++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath12k/dp_peer.h | 2 ++
drivers/net/wireless/ath/ath12k/mac.c | 24 ++++++++++++++
6 files changed, 100 insertions(+)
diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c
index 0e7c732f8222..cf9c5b068fa6 100644
--- a/drivers/net/wireless/ath/ath12k/core.c
+++ b/drivers/net/wireless/ath/ath12k/core.c
@@ -1547,6 +1547,8 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab)
}
wiphy_unlock(ah->hw->wiphy);
+
+ complete(&ah->peer_ml_id_done);
}
wake_up(&ab->wmi_ab.tx_credits_wq);
diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h
index 8769b41f5db5..30726e580833 100644
--- a/drivers/net/wireless/ath/ath12k/core.h
+++ b/drivers/net/wireless/ath/ath12k/core.h
@@ -795,6 +795,7 @@ struct ath12k_hw {
bool regd_updated;
bool use_6ghz_regd;
bool host_alloc_ml_id;
+ struct completion peer_ml_id_done;
u8 num_radio;
diff --git a/drivers/net/wireless/ath/ath12k/dp_htt.c b/drivers/net/wireless/ath/ath12k/dp_htt.c
index 150b190f9c7f..68968f96b4f1 100644
--- a/drivers/net/wireless/ath/ath12k/dp_htt.c
+++ b/drivers/net/wireless/ath/ath12k/dp_htt.c
@@ -6,6 +6,7 @@
#include "core.h"
#include "peer.h"
+#include "dp_peer.h"
#include "htc.h"
#include "dp_htt.h"
#include "debugfs_htt_stats.h"
@@ -582,6 +583,7 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
struct htt_t2h_mlo_peer_map_event *ev = &resp->mlo_peer_map_ev;
u16 raw_peer_id, peer_id, addr_h16;
u8 peer_addr[ETH_ALEN];
+ int ret;
if (skb->len < sizeof(*ev)) {
ath12k_warn(ab, "unexpected htt mlo peer map event len %u\n",
@@ -600,6 +602,23 @@ static void ath12k_dp_htt_mlo_peer_map_handler(struct ath12k_base *ab,
ath12k_dbg(ab, ATH12K_DBG_DP_HTT, "htt mlo peer map peer %pM id %u\n",
peer_addr, peer_id);
+
+ /*
+ * Fix up the dp_peer entry created with ATH12K_MLO_PEER_ID_PENDING
+ * earlier; on chips with host_alloc_ml_id == false this is the only
+ * point at which the host learns the firmware-assigned ID. Chips
+ * that allocate the ID on the host also receive this event but the
+ * firmware-reported ID matches the host-allocated one, so there is
+ * nothing to fix up.
+ */
+ if (!ab->hw_params->host_alloc_ml_id) {
+ ret = ath12k_dp_peer_fixup_peer_id(ab, peer_addr,
+ peer_id);
+ if (ret)
+ ath12k_warn(ab,
+ "failed to fix up peer id %u for dp peer %pM: %d\n",
+ peer_id, peer_addr, ret);
+ }
}
void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab,
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.c b/drivers/net/wireless/ath/ath12k/dp_peer.c
index 2a2eae194007..09142dcb74f9 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.c
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.c
@@ -695,3 +695,55 @@ void ath12k_dp_link_peer_reset_rx_stats(struct ath12k_dp *dp, const u8 *addr)
if (rx_stats)
memset(rx_stats, 0, sizeof(*rx_stats));
}
+
+int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab,
+ const u8 *peer_addr, u16 peer_id)
+{
+ struct ath12k_dp_link_peer *link_peer;
+ struct ath12k_dp_peer *dp_peer = NULL;
+ struct ath12k_hw_group *ag = ab->ag;
+ struct ath12k_dp_hw *dp_hw = NULL;
+ struct ath12k_hw *ah;
+ int i;
+
+ if (peer_id >= (ATH12K_PEER_ML_ID_VALID | ATH12K_MAX_MLO_PEERS))
+ return -EINVAL;
+
+ for (i = 0; i < ag->num_hw; i++) {
+ ah = ag->ah[i];
+ if (!ah)
+ continue;
+
+ spin_lock_bh(&ah->dp_hw.peer_lock);
+ dp_peer = ath12k_dp_peer_find_by_addr(&ah->dp_hw,
+ (u8 *)peer_addr);
+ if (dp_peer) {
+ dp_hw = &ah->dp_hw;
+ break;
+ }
+ spin_unlock_bh(&ah->dp_hw.peer_lock);
+ }
+
+ if (!dp_peer)
+ return -ENOENT;
+
+ /* dp_hw->peer_lock is held */
+
+ dp_peer->peer_id = peer_id;
+ rcu_assign_pointer(dp_hw->dp_peers[peer_id], dp_peer);
+
+ for (i = 0; i < ATH12K_NUM_MAX_LINKS; i++) {
+ link_peer = rcu_dereference_protected(dp_peer->link_peers[i],
+ lockdep_is_held(&dp_hw->peer_lock));
+ if (link_peer)
+ link_peer->ml_id = peer_id;
+ }
+
+ ath12k_sta_to_ahsta(dp_peer->sta)->ml_peer_id = peer_id;
+
+ spin_unlock_bh(&dp_hw->peer_lock);
+
+ complete(&ah->peer_ml_id_done);
+
+ return 0;
+}
diff --git a/drivers/net/wireless/ath/ath12k/dp_peer.h b/drivers/net/wireless/ath/ath12k/dp_peer.h
index f5067e66f1e1..9842671b5475 100644
--- a/drivers/net/wireless/ath/ath12k/dp_peer.h
+++ b/drivers/net/wireless/ath/ath12k/dp_peer.h
@@ -180,4 +180,6 @@ struct ath12k_dp_peer *ath12k_dp_peer_find_by_peerid(struct ath12k_pdev_dp *dp_p
struct ath12k_dp_link_peer *
ath12k_dp_link_peer_find_by_peerid(struct ath12k_pdev_dp *dp_pdev, u16 peer_id);
void ath12k_dp_link_peer_free(struct ath12k_dp_link_peer *peer);
+int ath12k_dp_peer_fixup_peer_id(struct ath12k_base *ab, const u8 *peer_addr,
+ u16 peer_id);
#endif
diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c
index 533109f9d5bd..e4c240f168e3 100644
--- a/drivers/net/wireless/ath/ath12k/mac.c
+++ b/drivers/net/wireless/ath/ath12k/mac.c
@@ -3855,9 +3855,11 @@ static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar,
static int ath12k_mac_peer_assoc(struct ath12k *ar,
struct ath12k_wmi_peer_assoc_arg *peer_arg)
{
+ struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
int ret;
reinit_completion(&ar->peer_assoc_done);
+ reinit_completion(&ah->peer_ml_id_done);
ret = ath12k_wmi_send_peer_assoc_cmd(ar, peer_arg);
if (ret) {
@@ -3872,6 +3874,27 @@ static int ath12k_mac_peer_assoc(struct ath12k *ar,
return -ETIMEDOUT;
}
+ /*
+ * For devices where the firmware allocates the MLD peer ID, the host
+ * learns the real ID only from the MLO_RX_PEER_MAP HTT event, which is
+ * handled in a softirq (BH workqueue) context that cannot take the
+ * wiphy lock. Block here, while still holding the wiphy lock, until
+ * that event has fixed up the ID. This serialises the fixup against
+ * all other wiphy-locked ml_peer_id accesses.
+ *
+ * The firmware sends the event only once, in response to the assoc-link
+ * peer assoc, so block only for that link.
+ */
+ if (!ah->host_alloc_ml_id &&
+ peer_arg->is_assoc &&
+ peer_arg->ml.enabled &&
+ peer_arg->ml.assoc_link &&
+ !wait_for_completion_timeout(&ah->peer_ml_id_done, 1 * HZ)) {
+ ath12k_warn(ar->ab, "failed to get MLO peer map event for %pM vdev %i\n",
+ peer_arg->peer_mac, peer_arg->vdev_id);
+ return -ETIMEDOUT;
+ }
+
return 0;
}
@@ -15332,6 +15355,7 @@ static struct ath12k_hw *ath12k_mac_hw_allocate(struct ath12k_hw_group *ag,
ah->num_radio = num_pdev_map;
mutex_init(&ah->hw_mutex);
+ init_completion(&ah->peer_ml_id_done);
spin_lock_init(&ah->dp_hw.peer_lock);
INIT_LIST_HEAD(&ah->dp_hw.dp_peers_list);
--
2.25.1
^ 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