[PATCH v3 0/2] wifi: rtw88: sdio: Fix interrupt storm on 3081 chips and split RX handling
From: Alastair D'Silva
Date: Wed Sep 30 2026 - 20:03:25 EST
This series resolves an unhandled RX request interrupt storm on 3081-based
SDIO chips (RTL8821CS, RTL8822CS) and cleans up the RX interrupt handling
path.
On 3081 SDIO chips, REG_SDIO_HISR_RX_REQUEST is not cleared automatically
by hardware when the RX FIFO is empty. Masking this bit out in software
before writing back to HISR prevented it from ever being acknowledged in
hardware, trapping the CPU core in an infinite interrupt storm loop.
In v2, HIMR masking and early writeback were introduced. As Luka Gejak
pointed out, this broke the drain loop on 8051 chips (RTL8723BS, RTL8723CS,
RTL8723DS) because 8051 hardware automatically clears the bit when empty
and re-reads HISR across iterations. Clearing it early causes 8051 to abort
draining after a single packet. In addition, RTL8723BS in rtw-next requires
RTW_SDIO_HISR_CLEAR_MASK to prevent undefined bits from causing resume
storms.
As the number of architecture-specific special cases has grown (16-bit vs
32-bit register widths, differing HISR writeback timing, synthetic loop
flags, and RTL8723BS resume masking), attempting to accommodate both
architectures within a single monolithic handler has become fragile and
prone to cross-architecture regressions.
To cleanly address both architectures without compromises:
- Patch 1 makes rtw_sdio_handle_interrupt() a dispatcher with separate
8051 and 3081 handlers. 8051 retains its original unmasked writeback
and RTW_SDIO_HISR_CLEAR_MASK, while 3081 adopts the HIMR masking and
W1C acknowledgment pattern. Spurious IRQ check (hisr == 0) and early exit
are moved into the dispatcher.
- Patch 2 splits rtw_sdio_rx_isr() into separate 8051 and 3081 variants,
eliminating per-packet branches in the RX hot path and removing the
artificial hisr assignment on 3081.
Hardware Verification & Empirical Testing (Fly-C5 / RTL8821CS / Linux 7.2):
===========================================================================
Testing was performed on an Allwinner H618 (Mellow Fly-C5) with an onboard
RTL8821CS SDIO Wi-Fi module running Linux 7.2.8 over wlan0.
Instrumentation was added to measure ISR durations, HIMR toggle overhead,
and packet arrivals occurring while interrupts were masked.
1. Baseline Performance (0 us delay, wlan0):
- TCP Saturation (iperf3 -P4): 35.1 Mbps, 15,988 RX IRQs, 0 RCU stalls.
- UDP Saturation (iperf3 -u -b 40M): 36.7 Mbps, 32,271 datagrams
received, 0 lost (0.0% packet loss), 1,342 masked arrivals (7.5%).
- HIMR toggle overhead (disable + enable): 34.2 us (10.3% of ISR time).
2. Injected Critical Section Delays (Race-Window & Masking Verification):
To verify that packet arrivals during interrupt servicing are reliably
latched and re-asserted upon unmasking, delays were injected between W1C
acknowledgment and unmasking:
Delay (us) | Traffic | Total IRQs | Masked Arrv | % IRQs | Loss
-----------+---------+------------+-------------+--------+------
0 us | UDP 40M | 17,786 | 1,342 | 7.5% | 0.0%
500 us | UDP 40M | 9,036 | 1,364 | 15.1% | 0.0%
2,000 us | UDP 40M | 3,892 | 458 | 11.8% | 0.0%
10,000 us | UDP 40M | 1,317 | 217 | 16.5% | 0.0%
10,000 us | TCP sat | 1,048 | 171 | 16.5% | 24 retr
Explanation of Masked Arrival Dynamics:
A counterintuitive observation is that the raw count of masked arrivals
decreases at longer delays (1,342 -> 217 at 10 ms). This occurs because:
a) The metric counts *interrupt invocations* where at least one packet
arrived while masked, not individual packets.
b) With a 10 ms delay per ISR, the system can only execute ~1,300
total interrupts during a 10-second test (down from ~17,800).
The proportion of interrupts with masked arrivals more than
doubled (7.5% -> 16.5%).
c) RTL8821CS has a 16 KB RX FIFO. At 40 Mbps, the FIFO fills in ~3.2 ms.
Once full, 802.11 block ACKs pause and over-the-air transmission
stops until the host drains the FIFO. Each interrupt then drains
the accumulated data in a single burst (RX drain time grew from
234 us to 912 us).
d) Zero packet loss (0.0%) was maintained across all delay
configurations.
v3:
- Rebased on rtw-next.
- Preserved 8051 interrupt handling behavior and RTW_SDIO_HISR_CLEAR_MASK
by splitting rtw_sdio_handle_interrupt() into separate 8051 and 3081
dispatch routines (thanks Luka Gejak for the review and analysis).
- Moved hisr reading and early exit on spurious IRQ into the dispatcher.
- Split rtw_sdio_rx_isr() into 8051 and 3081 implementations (patch 2).
- Added hardware benchmark metrics and delay-injected test results under
the cut line.
- Added Assisted-by tag per Documentation/process/coding-assistants.rst.
v2:
- Reworked interrupt acknowledgment to use HIMR masking
(rtw_sdio_disable_interrupt / rtw_sdio_enable_interrupt) to avoid races
between reading RX0_REQ_LEN and clearing HISR.
v1:
- https://lore.kernel.org/linux-wireless/20260921085502.123456-1-alastair@xxxxxxxxxxx/
Alastair D'Silva (2):
wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants
drivers/net/wireless/realtek/rtw88/sdio.c | 95 +++++++++++++++--------
1 file changed, 62 insertions(+), 33 deletions(-)
--
2.53.0