drivers/net/wireless/realtek/rtw88/usb.c | 3 --- 1 file changed, 3 deletions(-)
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
In AP mode every broadcast and multicast data frame is routed to
TX_DESC_QSEL_HIGH, the after-DTIM queue. The firmware drains that
queue at beacon pace, roughly a dozen frames per second, while a
single associated client's mDNS/SSDP chatter alone exceeds that.
The excess accumulates inside the chip until the shared TX page
pool is exhausted; measured on RTL8822BU, 14 of 1803 pages were
left. From that point every host-sourced frame queues behind the
backlog: authentication responses reach the air seconds after the
client has given up, so no station can associate anymore, and the
beacon reserved-page download fails the BCN_VALID poll ("error
beacon valid") because it needs pages from the same exhausted pool.
The AP keeps beaconing throughout, so from the outside this looks
like a silent receive stall, and only a reboot recovers.
On USB the HIGH, MGMT, BEACON and H2C queues additionally share one
bulk-out endpoint, so the jam also head-of-line blocks firmware
commands.
Route broadcast/multicast data through the regular AC queues
instead. They then leave at line rate and the page pool never
fills. The trade-off is that stations in power save may miss
multicast that the after-DTIM queue would have buffered for them;
at the chatter rates that trigger the jam those frames were being
dropped anyway.
On a bench AP (RTL8822BU, USB2, 20 MHz, WPA2, hostapd, a Windows
client driven through disconnect/reconnect cycles): reconnects fail
0/5 before this change and pass 5/5 with it, with the page pool
staying healthy and no beacon errors logged.
Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
---
drivers/net/wireless/realtek/rtw88/usb.c | 3 ---
1 file changed, 3 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index 64e1c3420..f528fe0f2 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -565,9 +565,6 @@ static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb)
if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)))
qsel = TX_DESC_QSEL_MGMT;
- else if (is_broadcast_ether_addr(hdr->addr1) ||
- is_multicast_ether_addr(hdr->addr1))
- qsel = TX_DESC_QSEL_HIGH;
else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK)
qsel = skb->priority;
else
--
2.54.0
On 13/08/2026 16:18, Mehmet Fide wrote:
> From: Mehmet Fide <mehmet.fide@screeningeagle.com>
>
> In AP mode every broadcast and multicast data frame is routed to
> TX_DESC_QSEL_HIGH, the after-DTIM queue. The firmware drains that
> queue at beacon pace, roughly a dozen frames per second, while a
> single associated client's mDNS/SSDP chatter alone exceeds that.
> The excess accumulates inside the chip until the shared TX page
> pool is exhausted; measured on RTL8822BU, 14 of 1803 pages were
> left. From that point every host-sourced frame queues behind the
> backlog: authentication responses reach the air seconds after the
> client has given up, so no station can associate anymore, and the
> beacon reserved-page download fails the BCN_VALID poll ("error
> beacon valid") because it needs pages from the same exhausted pool.
> The AP keeps beaconing throughout, so from the outside this looks
> like a silent receive stall, and only a reboot recovers.
>
> On USB the HIGH, MGMT, BEACON and H2C queues additionally share one
> bulk-out endpoint, so the jam also head-of-line blocks firmware
> commands.
>
> Route broadcast/multicast data through the regular AC queues
> instead. They then leave at line rate and the page pool never
> fills. The trade-off is that stations in power save may miss
> multicast that the after-DTIM queue would have buffered for them;
> at the chatter rates that trigger the jam those frames were being
> dropped anyway.
>
> On a bench AP (RTL8822BU, USB2, 20 MHz, WPA2, hostapd, a Windows
> client driven through disconnect/reconnect cycles): reconnects fail
> 0/5 before this change and pass 5/5 with it, with the page pool
> staying healthy and no beacon errors logged.
>
> Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
I wonder if you can reproduce this problem with kernel 6.5? It looks
like commit 076f786a0ae1 ("wifi: rtw88: Fix AP mode incorrect DTIM
behavior") from 6.5 was supposed to fix the exact same problem.
This is also the commit which introduced the code you are now removing.
> ---
> drivers/net/wireless/realtek/rtw88/usb.c | 3 ---
> 1 file changed, 3 deletions(-)
>
> diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
> index 64e1c3420..f528fe0f2 100644
> --- a/drivers/net/wireless/realtek/rtw88/usb.c
> +++ b/drivers/net/wireless/realtek/rtw88/usb.c
> @@ -565,9 +565,6 @@ static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb)
>
> if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)))
> qsel = TX_DESC_QSEL_MGMT;
> - else if (is_broadcast_ether_addr(hdr->addr1) ||
> - is_multicast_ether_addr(hdr->addr1))
> - qsel = TX_DESC_QSEL_HIGH;
> else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK)
> qsel = skb->priority;
> else
On 13/08/2026 18:07, Bitterblue Smith wrote:
> On 13/08/2026 16:18, Mehmet Fide wrote:
>> From: Mehmet Fide <mehmet.fide@screeningeagle.com>
>>
>> In AP mode every broadcast and multicast data frame is routed to
>> TX_DESC_QSEL_HIGH, the after-DTIM queue. The firmware drains that
>> queue at beacon pace, roughly a dozen frames per second, while a
>> single associated client's mDNS/SSDP chatter alone exceeds that.
>> The excess accumulates inside the chip until the shared TX page
>> pool is exhausted; measured on RTL8822BU, 14 of 1803 pages were
>> left. From that point every host-sourced frame queues behind the
>> backlog: authentication responses reach the air seconds after the
>> client has given up, so no station can associate anymore, and the
>> beacon reserved-page download fails the BCN_VALID poll ("error
>> beacon valid") because it needs pages from the same exhausted pool.
>> The AP keeps beaconing throughout, so from the outside this looks
>> like a silent receive stall, and only a reboot recovers.
>>
>> On USB the HIGH, MGMT, BEACON and H2C queues additionally share one
>> bulk-out endpoint, so the jam also head-of-line blocks firmware
>> commands.
>>
>> Route broadcast/multicast data through the regular AC queues
>> instead. They then leave at line rate and the page pool never
>> fills. The trade-off is that stations in power save may miss
>> multicast that the after-DTIM queue would have buffered for them;
>> at the chatter rates that trigger the jam those frames were being
>> dropped anyway.
>>
>> On a bench AP (RTL8822BU, USB2, 20 MHz, WPA2, hostapd, a Windows
>> client driven through disconnect/reconnect cycles): reconnects fail
>> 0/5 before this change and pass 5/5 with it, with the page pool
>> staying healthy and no beacon errors logged.
>>
>> Signed-off-by: Mehmet Fide <mehmet.fide@screeningeagle.com>
>
> I wonder if you can reproduce this problem with kernel 6.5? It looks
> like commit 076f786a0ae1 ("wifi: rtw88: Fix AP mode incorrect DTIM
> behavior") from 6.5 was supposed to fix the exact same problem.
> This is also the commit which introduced the code you are now removing.
>
By the way, do you know an easy way to generate mDNS/SSDP chatter,
to reproduce this bug?
>> ---
>> drivers/net/wireless/realtek/rtw88/usb.c | 3 ---
>> 1 file changed, 3 deletions(-)
>>
>> diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
>> index 64e1c3420..f528fe0f2 100644
>> --- a/drivers/net/wireless/realtek/rtw88/usb.c
>> +++ b/drivers/net/wireless/realtek/rtw88/usb.c
>> @@ -565,9 +565,6 @@ static u8 rtw_usb_tx_queue_mapping_to_qsel(struct sk_buff *skb)
>>
>> if (unlikely(ieee80211_is_mgmt(fc) || ieee80211_is_ctl(fc)))
>> qsel = TX_DESC_QSEL_MGMT;
>> - else if (is_broadcast_ether_addr(hdr->addr1) ||
>> - is_multicast_ether_addr(hdr->addr1))
>> - qsel = TX_DESC_QSEL_HIGH;
>> else if (skb_get_queue_mapping(skb) <= IEEE80211_AC_BK)
>> qsel = skb->priority;
>> else
>
Hi Bitterblue, You don't need real mDNS/SSDP - the driver keys the routing purely on the destination address of the frame, so any multicast/broadcast the AP transmits takes the same path. Real Windows/Apple clients get you there on their own eventually (that is how we first hit it), but here is a deterministic recipe. Setup: RTL8822BU in AP mode (hostapd), DTIM 1, at least one station associated so the BSS is live. Generate the load from the AP host, egressing the wlan interface (use the bridge/interface that carries the BSS, and your subnet's broadcast address): # ~100 broadcast frames/s out of the AP ping -I wlan0 -b -i 0.01 192.168.42.255 Multicast works the same, e.g. "ping -I wlan0 -i 0.01 224.0.0.251", or a UDP loop to the SSDP group 239.255.255.250:1900. The firmware drains the HIGH queue at beacon pace - in my measurements about a dozen frames/s at DTIM 1 - so ~100/s overruns it at once. The shared TX page pool then bleeds down; I polled the free-page count (register 0x240) and watched it fall from ~1800 to low double digits within a minute. Once it bottoms out, new stations can no longer associate (their auth/assoc responses queue behind the backlog) and "error beacon valid" starts logging, because the reserved-page download needs pages from the same exhausted pool. The AP keeps beaconing, so from the air it looks like a silent receive stall; only a reboot recovers. Two knobs confirm it is the queue and nothing else: raise DTIM (e.g. 3) and the HIGH queue drains even slower, so the pool empties faster; drop the ping rate below ~12/s and it never fills. (This is on the v1 thread; the current revision is the v3 I sent as "route bmc frames via the high queue only for DTIM delivery", which keeps the after-DTIM path only for genuinely buffered multicast. The reproduction above applies to both.) Best regards, Mehmet
Hi Bitterblue, You don't need real mDNS/SSDP - the driver keys the routing purely on the destination address of the frame, so any multicast/broadcast the AP transmits takes the same path. Real Windows/Apple clients get you there on their own eventually (that is how we first hit it), but here is a deterministic recipe. Setup: RTL8822BU in AP mode (hostapd), DTIM 1, at least one station associated so the BSS is live. Generate the load from the AP host, egressing the wlan interface (use the bridge/interface that carries the BSS, and your subnet's broadcast address): # ~100 broadcast frames/s out of the AP ping -I wlan0 -b -i 0.01 192.168.42.255 Multicast works the same, e.g. "ping -I wlan0 -i 0.01 224.0.0.251", or a UDP loop to the SSDP group 239.255.255.250:1900. The firmware drains the HIGH queue at beacon pace - in my measurements about a dozen frames/s at DTIM 1 - so ~100/s overruns it at once. The shared TX page pool then bleeds down; I polled the free-page count (register 0x240) and watched it fall from ~1800 to low double digits within a minute. Once it bottoms out, new stations can no longer associate (their auth/assoc responses queue behind the backlog) and "error beacon valid" starts logging, because the reserved-page download needs pages from the same exhausted pool. The AP keeps beaconing, so from the air it looks like a silent receive stall; only a reboot recovers. Two knobs confirm it is the queue and nothing else: raise DTIM (e.g. 3) and the HIGH queue drains even slower, so the pool empties faster; drop the ping rate below ~12/s and it never fills. (This is on the v1 thread; the current revision is the v3 I sent as "route bmc frames via the high queue only for DTIM delivery", which keeps the after-DTIM path only for genuinely buffered multicast. The reproduction above applies to both.) Best regards, Mehmet
From: Mehmet Fide <mehmet.fide@screeningeagle.com>
Hello Bitterblue,
> I wonder if you can reproduce this problem with kernel 6.5? It looks
> like commit 076f786a0ae1 ("wifi: rtw88: Fix AP mode incorrect DTIM
> behavior") from 6.5 was supposed to fix the exact same problem.
> This is also the commit which introduced the code you are now removing.
Thanks, I was not aware of that commit, and you are right that my patch
reverts exactly the usb.c hunk of it; the MORE_DATA and HGQMD parts
stay. I will say that in the commit message in a v2.
I did not run 6.5 and cannot easily on this hardware, the board support
we run starts at 6.12. I do not think it would add information though:
the code from 076f786a0ae1 is unchanged between 6.5 and the 6.12.103 I
tested, and it was demonstrably engaged while the AP was wedged. In a
register snapshot taken in that state REG_TCR reads 0x00303030, so
BIT_TCR_UPDATE_HGQMD was set. Still, the high queue drained at about 14
frames a second, roughly 3 frames per DTIM at beacon interval 100 and
dtim_period 2, measured over minutes from the URB submit/complete
counters, while several hundred frames sat queued. So at least on
RTL8822BU the burst fetch does not happen even with that fix active.
RTL8821CU behaves the same, measured today on the same bench with only
the dongle swapped: stock fails the reconnect test 0/3 with the "error
beacon valid" messages appearing, and passes 5/5 with none once the bmc
routing is reverted. RTL8822BU is 0/5 stock and 10/10 with the revert.
I also think the two problems are different. 076f786a0ae1 addresses the
hardware fetching one buffered packet per DTIM instead of the whole
burst. What we hit is sustained inflow above any DTIM-paced outflow: on
USB the high queue shares the single bulk-out endpoint and the page
pool with the beacon and H2C queues, so once the pool is exhausted
(measured: 14 of 1803 pages left) the beacon reserved-page download
fails ("error beacon valid"), the TIM stops updating, and the firmware
never releases the burst, which closes the loop and no station can
associate again.
If you would rather keep the after-DTIM delivery for stations in power
save, I am happy to test an alternative, for example routing bmc to the
high queue only while a station is actually in PS, or a depth cap on
the high queue. On this hardware the plain AC-queue routing is what I
could verify.
Thanks,
Mehmet
On 13/08/2026 18:52, Mehmet Fide wrote:
> From: Mehmet Fide <mehmet.fide@screeningeagle.com>
>
> Hello Bitterblue,
>
>> I wonder if you can reproduce this problem with kernel 6.5? It looks
>> like commit 076f786a0ae1 ("wifi: rtw88: Fix AP mode incorrect DTIM
>> behavior") from 6.5 was supposed to fix the exact same problem.
>> This is also the commit which introduced the code you are now removing.
>
> Thanks, I was not aware of that commit, and you are right that my patch
> reverts exactly the usb.c hunk of it; the MORE_DATA and HGQMD parts
> stay. I will say that in the commit message in a v2.
>
> I did not run 6.5 and cannot easily on this hardware, the board support
> we run starts at 6.12. I do not think it would add information though:
> the code from 076f786a0ae1 is unchanged between 6.5 and the 6.12.103 I
> tested, and it was demonstrably engaged while the AP was wedged. In a
> register snapshot taken in that state REG_TCR reads 0x00303030, so
> BIT_TCR_UPDATE_HGQMD was set. Still, the high queue drained at about 14
> frames a second, roughly 3 frames per DTIM at beacon interval 100 and
> dtim_period 2, measured over minutes from the URB submit/complete
> counters, while several hundred frames sat queued. So at least on
> RTL8822BU the burst fetch does not happen even with that fix active.
> RTL8821CU behaves the same, measured today on the same bench with only
> the dongle swapped: stock fails the reconnect test 0/3 with the "error
> beacon valid" messages appearing, and passes 5/5 with none once the bmc
> routing is reverted. RTL8822BU is 0/5 stock and 10/10 with the revert.
>
> I also think the two problems are different. 076f786a0ae1 addresses the
> hardware fetching one buffered packet per DTIM instead of the whole
> burst. What we hit is sustained inflow above any DTIM-paced outflow: on
> USB the high queue shares the single bulk-out endpoint and the page
> pool with the beacon and H2C queues, so once the pool is exhausted
> (measured: 14 of 1803 pages left) the beacon reserved-page download
> fails ("error beacon valid"), the TIM stops updating, and the firmware
> never releases the burst, which closes the loop and no station can
> associate again.
>
> If you would rather keep the after-DTIM delivery for stations in power
> save, I am happy to test an alternative, for example routing bmc to the
> high queue only while a station is actually in PS, or a depth cap on
> the high queue. On this hardware the plain AC-queue routing is what I
> could verify.
>
> Thanks,
> Mehmet
Could you check what the official driver is doing in the same situation?
https://github.com/morrownr/88x2bu-20210702
From: Mehmet Fide <mehmet.fide@screeningeagle.com> Hello Bitterblue, > Could you check what the official driver is doing in the same situation? > > https://github.com/morrownr/88x2bu-20210702 Good timing, we had already done exactly that comparison before your mail arrived. That driver was our working reference on the same bench throughout the investigation (5/5 reconnect cycles where in-tree fails 0/5), and we went through its AP transmit path to understand why it is immune. It handles this quite differently, in five ways: 1. bmc frames are buffered only while a station is actually dozing (sta_dz_bitmap check in core/rtw_xmit.c, rtw_xmit_ap_enqueue). With no sleeper present they leave immediately through the normal AC path, which is what my patch restores for rtw88. 2. The buffering happens in software (the bmc sta's sleep_q), not in the hardware high queue. The hardware HIQ receives at most one DTIM burst at a time (chk_bmc_sleepq_hdl), so it can never accumulate into the page pool the way rtw88's standing high queue does. 3. Even then, a filter decides what may ride the HIQ at all. The default (rtw_hiq_filter=1, "allow special") lets only ARP, EAPOL and DHCP through; mDNS/SSDP chatter never enters the high queue. The rest of the buffered frames go out through the normal queues when the burst is released. 4. The driver owns the TIM bit itself: it sets it when it starts buffering, pushes the updated beacon, and clears it only after both the software queue and the hardware HIQ are empty, polled through HW_VAR_CHK_HI_QUEUE_EMPTY. It does not depend on a beacon download whose pages come from the same pool the backlog is exhausting. 5. MORE_DATA is set on every burst frame except the last one, which carries 0 and terminates the burst. rtw88 sets it on every high queue frame including the last. That may be part of why I measured the burst fetch not happening here even with BIT_TCR_UPDATE_HGQMD set: the hardware never sees the end marker the vendor code provides. So the field-proven driver never routes bulk multicast through the high queue either, sleeper or not; the high queue is a small, filtered, explicitly terminated DTIM burst buffer for it, not a transport path. Given that, I still think routing bmc through the AC queues is the right minimal fix for rtw88 USB. If you want to keep buffered delivery for dozing stations I am happy to work on the fuller shape, meaning PS tracking plus a software staging queue released at DTIM, but that is a much bigger change and the current behavior is a hard AP breakage. Thanks, Mehmet
On 13/08/2026 21:50, Mehmet Fide wrote: > From: Mehmet Fide <mehmet.fide@screeningeagle.com> > > Hello Bitterblue, > >> Could you check what the official driver is doing in the same situation? >> >> https://github.com/morrownr/88x2bu-20210702 > > Good timing, we had already done exactly that comparison before your > mail arrived. That driver was our working reference on the same bench > throughout the investigation (5/5 reconnect cycles where in-tree fails > 0/5), and we went through its AP transmit path to understand why it is > immune. It handles this quite differently, in five ways: > > 1. bmc frames are buffered only while a station is actually dozing > (sta_dz_bitmap check in core/rtw_xmit.c, rtw_xmit_ap_enqueue). With > no sleeper present they leave immediately through the normal AC > path, which is what my patch restores for rtw88. > > 2. The buffering happens in software (the bmc sta's sleep_q), not in > the hardware high queue. The hardware HIQ receives at most one DTIM > burst at a time (chk_bmc_sleepq_hdl), so it can never accumulate > into the page pool the way rtw88's standing high queue does. > > 3. Even then, a filter decides what may ride the HIQ at all. The > default (rtw_hiq_filter=1, "allow special") lets only ARP, EAPOL and > DHCP through; mDNS/SSDP chatter never enters the high queue. The > rest of the buffered frames go out through the normal queues when > the burst is released. > > 4. The driver owns the TIM bit itself: it sets it when it starts > buffering, pushes the updated beacon, and clears it only after both > the software queue and the hardware HIQ are empty, polled through > HW_VAR_CHK_HI_QUEUE_EMPTY. It does not depend on a beacon download > whose pages come from the same pool the backlog is exhausting. > > 5. MORE_DATA is set on every burst frame except the last one, which > carries 0 and terminates the burst. rtw88 sets it on every high > queue frame including the last. That may be part of why I measured > the burst fetch not happening here even with BIT_TCR_UPDATE_HGQMD > set: the hardware never sees the end marker the vendor code > provides. > > So the field-proven driver never routes bulk multicast through the high > queue either, sleeper or not; the high queue is a small, filtered, > explicitly terminated DTIM burst buffer for it, not a transport path. > > Given that, I still think routing bmc through the AC queues is the > right minimal fix for rtw88 USB. If you want to keep buffered delivery > for dozing stations I am happy to work on the fuller shape, meaning PS > tracking plus a software staging queue released at DTIM, but that is a > much bigger change and the current behavior is a hard AP breakage. > > Thanks, > Mehmet I don't know enough to have an opinion on this. Shouldn't mac80211 handle the buffering already?
From: Mehmet Fide <mehmet.fide@screeningeagle.com> Hello Bitterblue, > Shouldn't mac80211 handle the buffering already? It handles the decision already, and that turns out to be the key. For bmc frames in AP mode, ieee80211_tx_h_multicast_ps_buf() sets IEEE80211_TX_CTL_SEND_AFTER_DTIM only while at least one station is actually dozing, and then either buffers the frame itself, when the driver declares IEEE80211_HW_HOST_BROADCAST_PS_BUFFERING and pulls them with ieee80211_get_buffered_bc() after each DTIM beacon, or passes it to the driver with the flag set and expects the device to do the after-DTIM release. rtw88 is in the second group, but it never looks at the flag: it routes every bmc frame through the high queue whether anybody sleeps or not, which is what lets the backlog build up. The full host buffering model does not really fit rtw88 USB, the beacons come from the firmware and there is no per-beacon event to pull buffered frames on. Honoring the flag fits, and it is a one line change: flagged frames keep the high queue with the MORE_DATA/HGQMD handling from 076f786a0ae1, everything else goes out through the AC queues at line rate. The vendor driver gates on the same condition, a station actually being in PS, just with its own bookkeeping. I have sent a v2 doing exactly that. It passes the same reconnect test that fails on stock (RTL8822BU 10/10 with 4/4 pings on every cycle, first association included). The dozing-station path keeps the existing high queue code unchanged; I could not exercise a client in powersave on this bench yet and will follow up with that measurement next week. Thanks, Mehmet
© 2016 - 2026 Red Hat, Inc.