-: ------------ > 1: 6561bb289494 wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv -: ------------ > 2: fac668caa77f wifi: rtw88: sdio: Track running state and cancel TX worker on stop 1: cb1f35a489b9 ! 3: 3b64d3e7ea31 wifi: rtw88: sdio: Fix unhandled RX request interrupt storm @@ Metadata ## Commit message ## wifi: rtw88: sdio: Fix unhandled RX request interrupt storm  - 8051 and 3081 SDIO chipsets handle the REG_SDIO_HISR_RX_REQUEST status bit - differently: + On 3081-based SDIO chips (e.g. RTL8821CS), hardware does not automatically + clear REG_SDIO_HISR_RX_REQUEST when the RX buffer is empty. Masking this + bit out before writing back to HISR prevents it from being acknowledged, + causing an infinite interrupt storm loop that locks up the system.  - - 8051-based chips (e.g. RTL8723BS, RTL8723CS, RTL8723DS): - The hardware automatically clears REG_SDIO_HISR_RX_REQUEST once the RX - buffer is empty. Software must not clear this bit, because the drain - loop in rtw_sdio_rx_isr() re-reads REG_SDIO_HISR across iterations to - decide whether more requests are pending. Clearing it in software - terminates the loop after a single request, stranding the remainder of - the FIFO. Additionally, RTL8723BS requires RTW_SDIO_HISR_CLEAR_MASK to - avoid undefined bits causing resume storms. + 8051-based chips instead rely on hardware to clear this bit automatically + once the buffer is empty, and software must not clear it.  - - 3081-based chips (e.g. RTL8821CS, RTL8822CS): - The hardware does not automatically clear REG_SDIO_HISR_RX_REQUEST when - the RX buffer 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 that starved - RCU and locked up the system. Furthermore, on 3081 chips the physical - RX FIFO capacity is at most 24 KB (16 KB on RTL8821CS, 24 KB on - RTL8822CS), which is well within the 64 KB loop budget. - - 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. - - Resolve this by making rtw_sdio_handle_interrupt() a dispatcher with - separate paths for 8051 and 3081: - - 1. 8051 chips preserve the existing unmasked writeback behavior, leaving - REG_SDIO_HISR_RX_REQUEST for hardware to drop and respecting - RTW_SDIO_HISR_CLEAR_MASK on RTL8723BS. - 2. 3081 chips adopt the interrupt masking pattern: disable HIMR, - acknowledge pending status bits in HISR via W1C, service the pending - events, and re-enable HIMR. Any packet arriving during servicing - latches REG_SDIO_HISR_RX_REQUEST in hardware and re-asserts the IRQ line - once unmasked. - - Splitting into separate handlers isolates these quirks cleanly and - simplifies future maintenance. + Split rtw_sdio_handle_interrupt() into separate 8051 and 3081 handlers. + For 3081, disable interrupts via HIMR, acknowledge pending status bits + via W1C writeback, service pending events, and re-enable interrupts. + For 8051 chips, preserve the existing behavior.  Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets") Cc: stable@vger.kernel.org @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_rx_isr(struct rt  - rtwdev = hw->priv;  - rtwsdio = (struct rtw_sdio *)rtwdev->priv;  - +- if (!rtwsdio->running) +- return; +-  - rtwsdio->irq_thread = current;  -  - hisr = rtw_read32(rtwdev, REG_SDIO_HISR); @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt  +  +static void rtw_sdio_handle_interrupt_3081(struct rtw_dev *rtwdev, u32 hisr)  +{ ++ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; ++  + rtw_sdio_disable_interrupt(rtwdev);  + rtw_write32(rtwdev, REG_SDIO_HISR, hisr);  + @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt  + rtw_sdio_rx_isr(rtwdev);  +  + /* Unmasking HIMR re-asserts the IRQ line if new packets arrived */ -+ rtw_sdio_enable_interrupt(rtwdev); ++ if (rtwsdio->running) ++ rtw_sdio_enable_interrupt(rtwdev);  +}  +  +static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func) @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt  + rtwdev = hw->priv;  + rtwsdio = (struct rtw_sdio *)rtwdev->priv;  + ++ if (!rtwsdio->running) ++ return; ++  + rtwsdio->irq_thread = current;  +  + hisr = rtw_read32(rtwdev, REG_SDIO_HISR); 2: a5eca7a6bcf5 ! 4: 001205a1c42b wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants @@ Metadata ## Commit message ## wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants  - Following the split of rtw_sdio_handle_interrupt(), the receive FIFO drain - loop in rtw_sdio_rx_isr() still contained a growing number of special - cases between 8051 and 3081 chips, evaluated twice per packet in the RX - hot path: + Split rtw_sdio_rx_isr() into separate 8051 and 3081 variants to be + clear. The two differences between the architectures are: + - Register size: REG_SDIO_RX0_REQ_LEN is 16-bit on 8051, 32-bit on 3081. + - HISR behavior: 8051 re-reads REG_SDIO_HISR on each iteration, while + 3081 drains based on rx_len without re-reading HISR.  - 1. Register width: 8051 uses a 16-bit read of REG_SDIO_RX0_REQ_LEN, - while 3081 uses a 32-bit read. - 2. Loop termination: 8051 must re-read REG_SDIO_HISR on each iteration - because the RX buffer may contain data while HW or FW is still filling - it. Conversely, 3081 has improved HW/FW that can use rx_len - unconditionally, previously requiring a synthetic assignment of - hisr = REG_SDIO_HISR_RX_REQUEST to trick the loop condition into - continuing. - - To avoid accumulating further special cases and eliminate per-packet - branching in the RX hot path, split rtw_sdio_rx_isr() into separate - rtw_sdio_rx_isr_8051() and rtw_sdio_rx_isr_3081() functions. - - This removes the artificial hisr assignment on 3081 and keeps the RX - processing logic cleanly separated by architecture. + This is a refactoring without any logic changes.  Assisted-by: LLM Signed-off-by: Alastair D'Silva   ## drivers/net/wireless/realtek/rtw88/sdio.c ## -@@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) - } +@@ drivers/net/wireless/realtek/rtw88/sdio.c: static int rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len) + return 0; }   -static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)  +static void rtw_sdio_rx_isr_8051(struct rtw_dev *rtwdev) { u32 rx_len, hisr, total_rx_bytes = 0; + int ret;  do {  - if (rtw_chip_wcpu_8051(rtwdev)) @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_rx_isr(struct rt  +static void rtw_sdio_rx_isr_3081(struct rtw_dev *rtwdev)  +{  + u32 rx_len, total_rx_bytes = 0; ++ int ret;  +  + do {  + rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);  + if (!rx_len)  + break;  + -+ rtw_sdio_rxfifo_recv(rtwdev, rx_len); ++ ret = rtw_sdio_rxfifo_recv(rtwdev, rx_len); ++ if (ret) ++ break;  +  + total_rx_bytes += rx_len;  + } while (total_rx_bytes < SZ_64K); @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt  + rtw_sdio_rx_isr_3081(rtwdev);  /* Unmasking HIMR re-asserts the IRQ line if new packets arrived */ - rtw_sdio_enable_interrupt(rtwdev); + if (rtwsdio->running)