1: 6561bb289494 ! 1: 958c667dcf4d wifi: rtw88: sdio: Handle allocation and read errors in rtw_sdio_rxfifo_recv @@ Commit message 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  Signed-off-by: Alastair D'Silva   ## drivers/net/wireless/realtek/rtw88/sdio.c ## 2: fac668caa77f ! 2: 94d131f86bca wifi: rtw88: sdio: Track running state and cancel TX worker on stop @@ Commit message 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 (similar to PCI), check it - at the entry of rtw_sdio_handle_interrupt() and rtw_sdio_tx_handler(), and - cancel the TX worker synchronously in rtw_sdio_stop(). + 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: 65371a3f14e7 ("wifi: rtw88: sdio: Add HCI implementation for SDIO based chipsets") + 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   ## drivers/net/wireless/realtek/rtw88/sdio.c ## +@@ drivers/net/wireless/realtek/rtw88/sdio.c: 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 +@@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_init(struct rtw_dev *rtwdev) + REG_SDIO_HIMR_CPWM1; + } +  ++static void rtw_sdio_deinit(struct rtw_dev *rtwdev) ++{ ++ struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; ++ ++ mutex_destroy(&rtwsdio->irq_lock); ++} ++ + static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev) + { + u8 size, timeout;  @@ drivers/net/wireless/realtek/rtw88/sdio.c: static int rtw_sdio_8723bs_check_rqpn(struct rtw_dev *rtwdev)  static int rtw_sdio_start(struct rtw_dev *rtwdev) @@ drivers/net/wireless/realtek/rtw88/sdio.c: static int rtw_sdio_8723bs_check_rqpn  if (rtw_is_8723bs(rtwdev)) {  @@ drivers/net/wireless/realtek/rtw88/sdio.c: 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); }  -+ rtwsdio->running = true; - rtw_sdio_enable_interrupt(rtwdev); +- 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; -@@ drivers/net/wireless/realtek/rtw88/sdio.c: static int rtw_sdio_start(struct rtw_dev *rtwdev) + }  static void rtw_sdio_stop(struct rtw_dev *rtwdev) { +- rtw_sdio_disable_interrupt(rtwdev);  + struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv;  + -+ rtwsdio->running = false; - rtw_sdio_disable_interrupt(rtwdev); ++ 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); }  @@ 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; ++ scoped_guard(mutex, &rtwsdio->irq_lock) { ++ if (!rtwsdio->running) ++ return; ++ }  + rtwsdio->irq_thread = current;  @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_tx_handler(struc rtwdev = work_data->rtwdev; rtwsdio = (struct rtw_sdio *)rtwdev->priv;  -+ if (!rtwsdio->running) -+ return; ++ 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);  +@@ drivers/net/wireless/realtek/rtw88/sdio.c: int rtw_sdio_probe(struct sdio_func *sdio_func, + ret = rtw_sdio_init_tx(rtwdev); + if (ret) { + rtw_err(rtwdev, "failed to init SDIO TX queue\n"); +- goto err_sdio_declaim; ++ goto err_deinit; + } +  + ret = rtw_chip_info_setup(rtwdev); +@@ drivers/net/wireless/realtek/rtw88/sdio.c: int rtw_sdio_probe(struct sdio_func *sdio_func, + rtw_sdio_free_irq(rtwdev, sdio_func); + err_destroy_txwq: + rtw_sdio_deinit_tx(rtwdev); +-err_sdio_declaim: ++err_deinit: ++ rtw_sdio_deinit(rtwdev); + rtw_sdio_declaim(rtwdev, sdio_func); + err_deinit_core: + rtw_core_deinit(rtwdev); +@@ drivers/net/wireless/realtek/rtw88/sdio.c: void rtw_sdio_remove(struct sdio_func *sdio_func) + rtw_sdio_free_irq(rtwdev, sdio_func); + rtw_sdio_declaim(rtwdev, sdio_func); + rtw_sdio_deinit_tx(rtwdev); ++ rtw_sdio_deinit(rtwdev); + rtw_core_deinit(rtwdev); + ieee80211_free_hw(hw); + }  ## drivers/net/wireless/realtek/rtw88/sdio.h ##  @@ drivers/net/wireless/realtek/rtw88/sdio.h: struct rtw_sdio { @@ drivers/net/wireless/realtek/rtw88/sdio.h: struct rtw_sdio {  void *irq_thread;  + bool running; ++ /* protect running state and interrupt mask */ ++ struct mutex irq_lock;  struct workqueue_struct *txwq; struct rtw_sdio_work_data *tx_handler_data; 3: 3b64d3e7ea31 ! 3: 5543b4c96c93 wifi: rtw88: sdio: Fix unhandled RX request interrupt storm @@ Commit message  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. + via W1C writeback, service pending events, and re-enable interrupts under + irq_lock if still running. 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 } while (total_rx_bytes < SZ_64K && hisr & REG_SDIO_HISR_RX_REQUEST); }  --static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func)  +static void rtw_sdio_handle_interrupt_8051(struct rtw_dev *rtwdev, u32 hisr) - { -- struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func); -- struct rtw_sdio *rtwsdio; -- struct rtw_dev *rtwdev; -- u32 hisr; -- -- rtwdev = hw->priv; -- rtwsdio = (struct rtw_sdio *)rtwdev->priv; -- -- if (!rtwsdio->running) -- return; -- -- 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) { -@@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func) - hisr &= RTW_SDIO_HISR_CLEAR_MASK; -  - 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) { ++ 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) @@ 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 */ -+ if (rtwsdio->running) -+ rtw_sdio_enable_interrupt(rtwdev); ++ 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); -+ struct rtw_sdio *rtwsdio; -+ struct rtw_dev *rtwdev; -+ u32 hisr; -+ -+ rtwdev = hw->priv; -+ rtwsdio = (struct rtw_sdio *)rtwdev->priv; -+ -+ if (!rtwsdio->running) -+ return; -+ -+ rtwsdio->irq_thread = current; -+ -+ hisr = rtw_read32(rtwdev, REG_SDIO_HISR); + static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func) + { + struct ieee80211_hw *hw = sdio_get_drvdata(sdio_func); +@@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_handle_interrupt(struct sdio_func *sdio_func) + rtwsdio->irq_thread = current; +  + hisr = rtw_read32(rtwdev, REG_SDIO_HISR);  + if (!hisr)  + goto out; -+ +  +- 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);  + if (rtw_chip_wcpu_8051(rtwdev))  + rtw_sdio_handle_interrupt_8051(rtwdev, hisr);  + else 4: 001205a1c42b ! 4: d774798f92bb wifi: rtw88: sdio: Split rtw_sdio_rx_isr into 8051 and 3081 variants @@ Commit message This is a refactoring without any logic changes.  Assisted-by: LLM + Acked-by: Ping-Ke Shih  Signed-off-by: Alastair D'Silva   ## drivers/net/wireless/realtek/rtw88/sdio.c ## @@ drivers/net/wireless/realtek/rtw88/sdio.c: static void rtw_sdio_rx_isr(struct rt  - */  - hisr = REG_SDIO_HISR_RX_REQUEST;  - } -+ /* Stop if no more RX requests are pending, even if ++ /* ++ * 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 @@ 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 */ - if (rtwsdio->running) + scoped_guard(mutex, &rtwsdio->irq_lock) {