[PATCH v4 2/2] Bluetooth: btmtksdio: call cancel_work_sync() out of host lock scope
From: Sergey Senozhatsky <senozhatsky@chromium.org>
Date: 2026-06-18 03:14:10
Also in:
linux-bluetooth, linux-mediatek, lkml, stable
Subsystem:
bluetooth drivers, the rest · Maintainers:
Marcel Holtmann, Luiz Augusto von Dentz, Linus Torvalds
cancel_work_sync() should be called outside of host lock scope
in order to avoid circular locking scenario:
CPU0 CPU1
close()/reset()
sdio_claim_host()
txrx_work
sdio_claim_host() // sleeps
cancel_work_sync() // sleeps
In addition, when txrx_work() runs concurrently with close()/reset()
it better not to re-enable interrupts by testing for BTMTKSDIO_FUNC_ENABLED
and not BTMTKSDIO_HW_RESET_ACTIVE before C_INT_EN_SET write. However,
btmtksdio_close() clears the BTMTKSDIO_FUNC_ENABLED too late (after
cancel_work_sync() call). Move BTMTKSDIO_FUNC_ENABLED bit-clear earlier
so that txrx_work can see concurrent close().
Fixes: 26270bc189ea4 ("Bluetooth: btmtksdio: move interrupt service to work")
Cc: stable@vger.kernel.org
Signed-off-by: Sergey Senozhatsky <senozhatsky@chromium.org>
---
drivers/bluetooth/btmtksdio.c | 12 ++++++++++--
1 file changed, 10 insertions(+), 2 deletions(-)
diff --git a/drivers/bluetooth/btmtksdio.c b/drivers/bluetooth/btmtksdio.c
index d8c8d2857527..207d04cc2282 100644
--- a/drivers/bluetooth/btmtksdio.c
+++ b/drivers/bluetooth/btmtksdio.c@@ -625,7 +625,9 @@ static void btmtksdio_txrx_work(struct work_struct *work) } while (int_status && time_is_after_jiffies(txrx_timeout)); /* Enable interrupt */ - if (bdev->func->irq_handler) + if (bdev->func->irq_handler && + test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state) && + !test_bit(BTMTKSDIO_HW_RESET_ACTIVE, &bdev->tx_state)) sdio_writel(bdev->func, C_INT_EN_SET, MTK_REG_CHLPCR, NULL); sdio_release_host(bdev->func);
@@ -741,6 +743,8 @@ static int btmtksdio_close(struct hci_dev *hdev) if (!test_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state)) return 0; + clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state); + sdio_claim_host(bdev->func); /* Disable interrupt */
@@ -748,11 +752,12 @@ static int btmtksdio_close(struct hci_dev *hdev) sdio_release_irq(bdev->func); + sdio_release_host(bdev->func); cancel_work_sync(&bdev->txrx_work); + sdio_claim_host(bdev->func); btmtksdio_fw_pmctrl(bdev); - clear_bit(BTMTKSDIO_FUNC_ENABLED, &bdev->tx_state); sdio_disable_func(bdev->func); sdio_release_host(bdev->func);
@@ -1295,7 +1300,10 @@ static void btmtksdio_reset(struct hci_dev *hdev) sdio_writel(bdev->func, C_INT_EN_CLR, MTK_REG_CHLPCR, NULL); skb_queue_purge(&bdev->txq); + + sdio_release_host(bdev->func); cancel_work_sync(&bdev->txrx_work); + sdio_claim_host(bdev->func); gpiod_set_value_cansleep(bdev->reset, 1); msleep(100);
--
2.54.0.1189.g8c84645362-goog