From 9cf132d9b50185865479f25d09783d9edc2eaa77 Mon Sep 17 00:00:00 2001 From: Lucas Kawatoko <97761679+lucaskawatoko@users.noreply.github.com> Date: Thu, 24 Sep 2026 09:07:55 -0300 Subject: [PATCH] fix: avoid spurious WARN in rwnx_close during USB teardown Unplugging the adapter while the interface is connecting or disconnecting prints a spurious kernel warning: rwnx_close connecting or disconnecting, not finish WARNING: CPU: ... rwnx_close+0x96/0x3f0 [aic8800_fdrv] rwnx_close() waits up to 4 seconds for drv_conn_state to leave CONNECTING/DISCONNECTING, but that state only changes when the firmware answers the pending request. During USB teardown the bus is already down by the time rwnx_close() runs, and rwnx_send_msg() drops the message and returns 0, so the confirmation never arrives. The wait therefore always runs its full timeout and fires WARN_ON(1) for a condition that cannot be satisfied. Skip both waits when the bus is down, using the same condition already used a few lines below in the vif_started path. This removes the wait itself, not just the warning: previously teardown blocked for up to 4 (and 2) seconds waiting on a dead bus. Reproduced on 6.12.107+deb13 with an AIC8800D80: unplug while connected fires the warning on main and produces none with this change, while the replug path still re-enumerates and restores Wi-Fi and Bluetooth. Builds on 5.15.0-191, 6.1.0-53, 6.8.0-139, 6.11.0-8 and 6.12.107+deb13 with no new warnings. --- drivers/aic8800/aic8800_fdrv/rwnx_main.c | 55 ++++++++++++++++-------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/drivers/aic8800/aic8800_fdrv/rwnx_main.c b/drivers/aic8800/aic8800_fdrv/rwnx_main.c index 9180fe2..d96f62e 100644 --- a/drivers/aic8800/aic8800_fdrv/rwnx_main.c +++ b/drivers/aic8800/aic8800_fdrv/rwnx_main.c @@ -1518,18 +1518,30 @@ static int rwnx_close(struct net_device *dev) RWNX_DBG(RWNX_FN_ENTRY_STR); - test_counter = waiting_counter; - while(atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING|| - atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING){ - AICWFDBG(LOGDEBUG, "%s wifi is connecting or disconnecting, waiting 200ms for state to stable\r\n", __func__); - msleep(200); - test_counter--; - if(test_counter == 0){ - AICWFDBG(LOGERROR, "%s connecting or disconnecting, not finish\r\n", __func__); - WARN_ON(1); - break; - } - } + /* + * A CONNECTING/DISCONNECTING state is only left when the firmware answers + * the pending request. If the bus is already down the answer can never + * arrive: rwnx_send_msg() silently drops the message and returns 0 + * (rwnx_msg_tx.c), so the wait below would always run its full timeout and + * fire the spurious WARN_ON() during USB teardown. Skip the wait when the + * bus is down, using the same condition the vif_started path uses below. + */ + if (bus_if && + (usbdev == NULL || (usbdev->bus_if->state != BUS_DOWN_ST && + usbdev->state != USB_DOWN_ST))) { + test_counter = waiting_counter; + while(atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING|| + atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTING){ + AICWFDBG(LOGDEBUG, "%s wifi is connecting or disconnecting, waiting 200ms for state to stable\r\n", __func__); + msleep(200); + test_counter--; + if(test_counter == 0){ + AICWFDBG(LOGERROR, "%s connecting or disconnecting, not finish\r\n", __func__); + WARN_ON(1); + break; + } + } + } #if defined(AICWF_USB_SUPPORT) || defined(AICWF_SDIO_SUPPORT) if (rwnx_hw->scanning){ @@ -1587,13 +1599,18 @@ static int rwnx_close(struct net_device *dev) test_counter = waiting_counter; if(atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_CONNECTED){ rwnx_set_conn_state(rwnx_vif, &rwnx_vif->drv_conn_state, RWNX_DRV_STATUS_DISCONNECTING); - rwnx_send_sm_disconnect_req(rwnx_hw, rwnx_vif, 3); - while (atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING) { - AICWFDBG(LOGDEBUG, "%s wifi is disconnecting, waiting 100ms for state to stable\r\n", __func__); - msleep(100); - test_counter--; - if (test_counter ==0) - break; + /* Same reasoning as above: with the bus down the disconnect + * confirmation cannot arrive, so do not wait for it. */ + if (usbdev->bus_if->state != BUS_DOWN_ST && + usbdev->state != USB_DOWN_ST) { + rwnx_send_sm_disconnect_req(rwnx_hw, rwnx_vif, 3); + while (atomic_read(&rwnx_vif->drv_conn_state) == (int)RWNX_DRV_STATUS_DISCONNECTING) { + AICWFDBG(LOGDEBUG, "%s wifi is disconnecting, waiting 100ms for state to stable\r\n", __func__); + msleep(100); + test_counter--; + if (test_counter ==0) + break; + } } } }