* [PATCH rtw-next v6 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv
2026-10-14 7:44 [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
@ 2026-10-14 7:44 ` Alastair D'Silva
2026-10-14 7:44 ` [PATCH rtw-next v6 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
` (3 subsequent siblings)
4 siblings, 0 replies; 8+ messages in thread
From: Alastair D'Silva @ 2026-10-14 7:44 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
When dev_alloc_skb() or rtw_sdio_read_port() fails, rtw_sdio_rxfifo_recv()
returns without reading data. Because it returned void, the caller still
counted rx_len against the loop budget while leaving data stranded in the
FIFO.
Return an error code from rtw_sdio_rxfifo_recv() on failure and break out
of the receive loop in the caller immediately.
Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Reviewed-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 15 ++++++++++-----
1 file changed, 10 insertions(+), 5 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index dc2fd0f8f9ff..e39284b71837 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1349,7 +1349,7 @@ static void rtw_sdio_rx_skb(struct rtw_dev *rtwdev, struct sk_buff *skb,
ieee80211_rx_irqsafe(rtwdev->hw, skb);
}
-static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
+static int rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
const struct rtw_chip_info *chip = rtwdev->chip;
@@ -1366,12 +1366,12 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
skb = dev_alloc_skb(bufsz);
if (!skb)
- return;
+ return -ENOMEM;
ret = rtw_sdio_read_port(rtwdev, skb->data, bufsz);
if (ret) {
dev_kfree_skb_any(skb);
- return;
+ return ret;
}
while (true) {
@@ -1380,7 +1380,7 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
&pkt_stat, &rx_status);
if (ret) {
dev_kfree_skb_any(skb);
- return;
+ return ret;
}
pkt_offset = pkt_desc_sz + pkt_stat.drv_info_sz +
@@ -1416,11 +1416,14 @@ static void rtw_sdio_rxfifo_recv(struct rtw_dev *rtwdev, u32 rx_len)
skb_reserve(skb, curr_pkt_len);
rx_len -= curr_pkt_len;
}
+
+ return 0;
}
static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
{
u32 rx_len, hisr, total_rx_bytes = 0;
+ int ret;
do {
if (rtw_chip_wcpu_8051(rtwdev))
@@ -1431,7 +1434,9 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
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;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH rtw-next v6 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop
2026-10-14 7:44 [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
2026-10-14 7:44 ` [PATCH rtw-next v6 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv Alastair D'Silva
@ 2026-10-14 7:44 ` Alastair D'Silva
2026-10-14 9:12 ` Luka Gejak
2026-10-14 7:44 ` [PATCH rtw-next v6 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
` (2 subsequent siblings)
4 siblings, 1 reply; 8+ messages in thread
From: Alastair D'Silva @ 2026-10-14 7:44 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
When rtw_sdio_stop() disables interrupts, in-flight interrupt handlers or
delayed TX work may still run against a powered-down device.
Track the operational state in rtwsdio->running with an irq_lock mutex
(similar to PCI), check it at the entry of rtw_sdio_handle_interrupt()
and rtw_sdio_tx_handler(), serialize state transitions in rtw_sdio_start()
and rtw_sdio_stop() under sdio_claim_host() and irq_lock, and cancel the
TX worker synchronously in rtw_sdio_stop().
Fixes: 28eaf7971a20 ("wifi: rtw88: sdio: add TX back-pressure and retry on page starvation")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 33 +++++++++++++++++++++--
drivers/net/wireless/realtek/rtw88/sdio.h | 3 +++
2 files changed, 34 insertions(+), 2 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index e39284b71837..2e26e7a2a0fd 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -941,6 +941,8 @@ static void rtw_sdio_init(struct rtw_dev *rtwdev)
{
struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ mutex_init(&rtwsdio->irq_lock);
+
if (rtw_is_8723bs(rtwdev))
rtwsdio->irq_mask = REG_SDIO_HIMR_RX_REQUEST;
else
@@ -1054,6 +1056,7 @@ static int rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev)
static int rtw_sdio_start(struct rtw_dev *rtwdev)
{
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
u32 clear;
if (rtw_is_8723bs(rtwdev)) {
@@ -1069,20 +1072,36 @@ static int rtw_sdio_start(struct rtw_dev *rtwdev)
rtw_sdio_enable_rx_aggregation(rtwdev);
+ sdio_claim_host(rtwsdio->sdio_func);
+
if (rtw_is_8723bs(rtwdev)) {
clear = rtw_read32(rtwdev, REG_SDIO_HISR) & RTW_SDIO_HISR_CLEAR_MASK;
if (clear)
rtw_write32(rtwdev, REG_SDIO_HISR, clear);
}
- rtw_sdio_enable_interrupt(rtwdev);
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ rtwsdio->running = true;
+ rtw_sdio_enable_interrupt(rtwdev);
+ }
+
+ sdio_release_host(rtwsdio->sdio_func);
return 0;
}
static void rtw_sdio_stop(struct rtw_dev *rtwdev)
{
- rtw_sdio_disable_interrupt(rtwdev);
+ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+
+ sdio_claim_host(rtwsdio->sdio_func);
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ rtwsdio->running = false;
+ rtw_sdio_disable_interrupt(rtwdev);
+ }
+ sdio_release_host(rtwsdio->sdio_func);
+
+ cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
}
static void rtw_sdio_deep_ps_enter(struct rtw_dev *rtwdev)
@@ -1470,6 +1489,11 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtwdev = hw->priv;
rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (!rtwsdio->running)
+ return;
+ }
+
rtwsdio->irq_thread = current;
hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
@@ -1703,6 +1727,11 @@ static void rtw_sdio_tx_handler(struct work_struct *work)
rtwdev = work_data->rtwdev;
rtwsdio = (struct rtw_sdio *)rtwdev->priv;
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (!rtwsdio->running)
+ return;
+ }
+
if (!rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
rtw_sdio_deep_ps_leave(rtwdev);
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.h b/drivers/net/wireless/realtek/rtw88/sdio.h
index 6e7e6009744b..64266b54f595 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.h
+++ b/drivers/net/wireless/realtek/rtw88/sdio.h
@@ -168,6 +168,9 @@ struct rtw_sdio {
bool sdio3_bus_mode;
void *irq_thread;
+ bool running;
+ /* protect running state and enabling interrupts */
+ struct mutex irq_lock;
struct workqueue_struct *txwq;
struct rtw_sdio_work_data *tx_handler_data;
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH rtw-next v6 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop
2026-10-14 7:44 ` [PATCH rtw-next v6 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
@ 2026-10-14 9:12 ` Luka Gejak
0 siblings, 0 replies; 8+ messages in thread
From: Luka Gejak @ 2026-10-14 9:12 UTC (permalink / raw)
To: Alastair D'Silva, linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Martin Blumenstingl, Jernej Skrabec, Ulf Hansson, linux-kernel,
Alastair D'Silva, stable
Hi Alastair,
October 14, 2026 at 9:44 AM, "Alastair D'Silva" <alastair@d-silva.org> wrote:
> static void rtw_sdio_stop(struct rtw_dev *rtwdev)
> { > - rtw_sdio_disable_interrupt(rtwdev);
> + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; > +
> + sdio_claim_host(rtwsdio->sdio_func);
> + scoped_guard(mutex, &rtwsdio->irq_lock) {
> + rtwsdio->running = false;
> + rtw_sdio_disable_interrupt(rtwdev);
> + }
> + sdio_release_host(rtwsdio->sdio_func);
> +
> + cancel_delayed_work_sync(&rtwsdio->tx_handler_data->work);
> }
One thing about that cancel. The delayed work is the back pressure retry:
rtw_sdio_8723bs_reschedule_tx() re-arms it with queue_delayed_work() when
a page shortage parks a frame, and rtw_sdio_tx_kick_off() re-arms it with
mod_delayed_work() for every new frame. On 8723bs the frames that fill the
software FIFO past the watermark also stop the matching mac80211 queue
(ieee80211_stop_queue() in rtw_sdio_8723bs_stop_tx_queue()), and
rtw_sdio_start() undoes neither that nor tx_queue_stopped[]. The flag is
cleared in rtw_sdio_init_tx() and in rtw_sdio_8723bs_wake_tx_queue(), and
the second of those only runs after a write has gone out.
So a stop that lands while an access category is held back leaves the
queue stopped and its frames parked in rtwsdio->tx_queue[] with no work
queued to come back for them. It recovers as soon as anything transmits
on any queue, because that kicks the worker again and the handler walks
all queues, so I could not make it a permanent wedge. It does mean an
idle link can sit on those frames for a long time. Would it be worth
re-arming the worker at the end of rtw_sdio_start(), or clearing
tx_queue_stopped[] and waking the queues there?
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH rtw-next v6 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
2026-10-14 7:44 [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
2026-10-14 7:44 ` [PATCH rtw-next v6 1/4] wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv Alastair D'Silva
2026-10-14 7:44 ` [PATCH rtw-next v6 2/4] wifi: rtw88: sdio: Track running state and cancel TX worker on stop Alastair D'Silva
@ 2026-10-14 7:44 ` Alastair D'Silva
2026-10-14 9:13 ` Luka Gejak
2026-10-14 7:44 ` [PATCH rtw-next v6 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants Alastair D'Silva
2026-10-14 9:12 ` [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Luka Gejak
4 siblings, 1 reply; 8+ messages in thread
From: Alastair D'Silva @ 2026-10-14 7:44 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva, stable
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 instead rely on hardware to clear this bit automatically
once the buffer is empty, and software must not clear it.
Split rtw_sdio_handle_interrupt() into separate 8051 and 3081 handlers.
For 3081, disable interrupts via HIMR, acknowledge all pending status
bits via W1C writeback (preserving the existing full status word
writeback used on non-RTL8723BS chips, now including
REG_SDIO_HISR_RX_REQUEST), service pending events, and re-enable
interrupts under irq_lock if still running.
On 3081 hardware, REG_SDIO_HISR_RX_REQUEST is level-sensitive to FIFO
occupancy: if unread packets remain in the FIFO when interrupts are
re-enabled (or if new packets arrived), the chip re-asserts
REG_SDIO_HISR_RX_REQUEST and re-triggers the IRQ line. For 8051 chips,
preserve the existing behavior.
Fixes: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets")
Cc: stable@vger.kernel.org
Assisted-by: LLM
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 74 ++++++++++++++++++-----
1 file changed, 58 insertions(+), 16 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index 2e26e7a2a0fd..e459541b7678 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1479,6 +1479,59 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
}
+static void rtw_sdio_handle_interrupt_8051(struct rtw_dev *rtwdev, u32 hisr)
+{
+ if (hisr & REG_SDIO_HISR_TXERR)
+ rtw_sdio_tx_err_isr(rtwdev);
+ if (hisr & REG_SDIO_HISR_RX_REQUEST) {
+ hisr &= ~REG_SDIO_HISR_RX_REQUEST;
+ rtw_sdio_rx_isr(rtwdev);
+ }
+
+ /*
+ * RTL8723BS keeps raising the interrupt after resume if undefined
+ * status bits are written back, so acknowledge only the bits this
+ * driver defines. Other chips keep the existing behaviour.
+ */
+ if (rtw_is_8723bs(rtwdev))
+ hisr &= RTW_SDIO_HISR_CLEAR_MASK;
+
+ rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+}
+
+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);
+
+ /*
+ * Acknowledge all pending status bits via W1C writeback. Unlike 8051,
+ * 3081 requires REG_SDIO_HISR_RX_REQUEST to be explicitly cleared by
+ * software.
+ */
+ rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
+
+ if (hisr & REG_SDIO_HISR_TXERR)
+ rtw_sdio_tx_err_isr(rtwdev);
+ if (hisr & REG_SDIO_HISR_RX_REQUEST)
+ rtw_sdio_rx_isr(rtwdev);
+
+ /*
+ * Unmasking HIMR re-asserts the IRQ line if new packets arrived or if
+ * unread data remains in the RX FIFO (e.g. if the 64KB budget was
+ * reached or an RX error occurred). On 3081 hardware,
+ * REG_SDIO_HISR_RX_REQUEST is level-sensitive to FIFO occupancy:
+ * although cleared by the W1C writeback above, the chip automatically
+ * re-asserts REG_SDIO_HISR_RX_REQUEST as long as the RX buffer is
+ * non-empty, re-triggering the interrupt when unmasked.
+ */
+ scoped_guard(mutex, &rtwsdio->irq_lock) {
+ if (rtwsdio->running)
+ rtw_sdio_enable_interrupt(rtwdev);
+ }
+}
+
static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
{
struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func);
@@ -1497,24 +1550,13 @@ static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)
rtwsdio->irq_thread = current;
hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
-
- if (hisr & REG_SDIO_HISR_TXERR)
- rtw_sdio_tx_err_isr(rtwdev);
- if (hisr & REG_SDIO_HISR_RX_REQUEST) {
- hisr &= ~REG_SDIO_HISR_RX_REQUEST;
- rtw_sdio_rx_isr(rtwdev);
+ if (hisr) {
+ if (rtw_chip_wcpu_8051(rtwdev))
+ rtw_sdio_handle_interrupt_8051(rtwdev, hisr);
+ else
+ rtw_sdio_handle_interrupt_3081(rtwdev, hisr);
}
- /*
- * RTL8723BS keeps raising the interrupt after resume if undefined
- * status bits are written back, so acknowledge only the bits this
- * driver defines. Other chips keep the existing behaviour.
- */
- if (rtw_is_8723bs(rtwdev))
- hisr &= RTW_SDIO_HISR_CLEAR_MASK;
-
- rtw_write32(rtwdev, REG_SDIO_HISR, hisr);
-
rtwsdio->irq_thread = NULL;
}
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH rtw-next v6 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
2026-10-14 7:44 ` [PATCH rtw-next v6 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
@ 2026-10-14 9:13 ` Luka Gejak
0 siblings, 0 replies; 8+ messages in thread
From: Luka Gejak @ 2026-10-14 9:13 UTC (permalink / raw)
To: Alastair D'Silva, linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Martin Blumenstingl, Jernej Skrabec, Ulf Hansson, linux-kernel,
Alastair D'Silva, stable
Hi Alastair,
October 14, 2026 at 9:44 AM, "Alastair D'Silva" <alastair@d-silva.org> wrote:
> 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.
In my opinion, I would reword this first sentance. Your own probe shows the
bit does clear, post_drain_hisr comes back as 0x00800030 once the FIFO is
empty, so what the hardware needs is the software acknowledge plus an
empty FIFO. The old code masked the acknowledge out of the writeback so
it never happened, which is the failure the second sentence describes.
As written the first one reads as if the bit could never be cleared and
the drain did not matter. The fix depends on the opposite reading.
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH rtw-next v6 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants
2026-10-14 7:44 [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
` (2 preceding siblings ...)
2026-10-14 7:44 ` [PATCH rtw-next v6 3/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
@ 2026-10-14 7:44 ` Alastair D'Silva
2026-10-14 9:12 ` [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Luka Gejak
4 siblings, 0 replies; 8+ messages in thread
From: Alastair D'Silva @ 2026-10-14 7:44 UTC (permalink / raw)
To: linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Luka Gejak, Martin Blumenstingl, Jernej Skrabec, Ulf Hansson,
linux-kernel, Alastair D'Silva
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.
This is a refactoring without any logic changes.
Assisted-by: LLM
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Reviewed-by: Luka Gejak <luka.gejak@linux.dev>
Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
---
drivers/net/wireless/realtek/rtw88/sdio.c | 58 +++++++++++++----------
1 file changed, 33 insertions(+), 25 deletions(-)
diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c
index e459541b7678..3569aae0f2f0 100644
--- a/drivers/net/wireless/realtek/rtw88/sdio.c
+++ b/drivers/net/wireless/realtek/rtw88/sdio.c
@@ -1439,17 +1439,13 @@ 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))
- rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
- else
- rx_len = rtw_read32(rtwdev, REG_SDIO_RX0_REQ_LEN);
-
+ rx_len = rtw_read16(rtwdev, REG_SDIO_RX0_REQ_LEN);
if (!rx_len)
break;
@@ -1459,33 +1455,45 @@ static void rtw_sdio_rx_isr(struct rtw_dev *rtwdev)
total_rx_bytes += rx_len;
- if (rtw_chip_wcpu_8051(rtwdev)) {
- /* Stop if no more RX requests are pending, even if
- * rx_len could be greater than zero in the next
- * iteration. This is needed because the RX buffer may
- * already contain data while either HW or FW are not
- * done filling that buffer yet. Still reading the
- * buffer can result in packets where
- * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
- * end of the buffer.
- */
- hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
- } else {
- /* RTW_WCPU_3081 chips have improved hardware or
- * firmware and can use rx_len unconditionally.
- */
- hisr = REG_SDIO_HISR_RX_REQUEST;
- }
+ /*
+ * Stop if no more RX requests are pending, even if
+ * rx_len could be greater than zero in the next
+ * iteration. This is needed because the RX buffer may
+ * already contain data while either HW or FW are not
+ * done filling that buffer yet. Still reading the
+ * buffer can result in packets where
+ * rtw_rx_pkt_stat.pkt_len is zero or points beyond the
+ * end of the buffer.
+ */
+ hisr = rtw_read32(rtwdev, REG_SDIO_HISR);
} while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST);
}
+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;
+
+ ret = rtw_sdio_rxfifo_recv(rtwdev, rx_len);
+ if (ret)
+ break;
+
+ total_rx_bytes += rx_len;
+ } while (total_rx_bytes < SZ_64K);
+}
+
static void rtw_sdio_handle_interrupt_8051(struct rtw_dev *rtwdev, u32 hisr)
{
if (hisr & REG_SDIO_HISR_TXERR)
rtw_sdio_tx_err_isr(rtwdev);
if (hisr & REG_SDIO_HISR_RX_REQUEST) {
hisr &= ~REG_SDIO_HISR_RX_REQUEST;
- rtw_sdio_rx_isr(rtwdev);
+ rtw_sdio_rx_isr_8051(rtwdev);
}
/*
@@ -1515,7 +1523,7 @@ static void rtw_sdio_handle_interrupt_3081(struct rtw_dev *rtwdev, u32 hisr)
if (hisr & REG_SDIO_HISR_TXERR)
rtw_sdio_tx_err_isr(rtwdev);
if (hisr & REG_SDIO_HISR_RX_REQUEST)
- rtw_sdio_rx_isr(rtwdev);
+ rtw_sdio_rx_isr_3081(rtwdev);
/*
* Unmasking HIMR re-asserts the IRQ line if new packets arrived or if
--
2.53.0
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm
2026-10-14 7:44 [PATCH rtw-next v6 0/4] wifi: rtw88: sdio: Fix unhandled RX request interrupt storm Alastair D'Silva
` (3 preceding siblings ...)
2026-10-14 7:44 ` [PATCH rtw-next v6 4/4] wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants Alastair D'Silva
@ 2026-10-14 9:12 ` Luka Gejak
4 siblings, 0 replies; 8+ messages in thread
From: Luka Gejak @ 2026-10-14 9:12 UTC (permalink / raw)
To: Alastair D'Silva, linux-wireless, Ping-Ke Shih, Kalle Valo
Cc: Martin Blumenstingl, Jernej Skrabec, Ulf Hansson, linux-kernel,
Alastair D'Silva
Hi Alastair,
October 14, 2026 at 9:44 AM, "Alastair D'Silva" <alastair@d-silva.org> wrote:
> On 3081-based SDIO chips (observed on RTL8821CS; RTL8822CS is inferred to
> be affected as it shares the 3081 architecture), hardware does not
> automatically clear the REG_SDIO_HISR_RX_REQUEST status bit when the RX
> FIFO becomes 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 that locked up the system upon
> receiving network traffic.
There are three 3081 parts with an SDIO front end, not two. 8822BS is
RTW_WCPU_3081 as well (rtw8822b.c:2527) and rtw8822bs.c is built as
rtw88_8822bs.o, so naming 8821CS and 8822CS only leaves it out. Patch 3
changes the interrupt path for all three at once and only the 8821CS was
probed, which is the one thing I would add to a series that Cc's stable.
If there is no run on 8822BS and 8822CS to hand, saying the same for both
would do, the way the cover already does for 8822CS.
Best regards,
Luka Gejak
^ permalink raw reply [flat|nested] 8+ messages in thread