From nobody Sat Jul 25 04:31:11 2026 Received: from zg8tmja2lje4os43os4xodqa.icoremail.net (zg8tmja2lje4os43os4xodqa.icoremail.net [206.189.79.184]) by smtp.subspace.kernel.org (Postfix) with ESMTP id ABED025B09F; Sat, 18 Jul 2026 02:44:52 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=206.189.79.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784342699; cv=none; b=bfM08KBp4+fSaXnSxwIn5JQThHBTmRIakfCkPTslO9Ejax4G7REVJgzpVo+MDg0y7xRWq7+6Pgy1QPzk5yDAAuiTiJAIhLqevNNYCWEd4WYNosYNajjVJQAt+NW/NHyKesNGJY8fE6gBUyXUjaBwDdOH6vAfnT/XHXD33YIuFmc= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784342699; c=relaxed/simple; bh=67J5Fgv1g+WjNPQavQi2nkbOwU2YuDTAPjsDdjDvZk4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=TZIuV2SuI94pW3oWI0TDL7SeB65uUL46CfGQJwLKiRxa05GRm7oXxHgDe5g+AaiEmPXGKc1RFdowAN4puUNOYSVcxvOuYSY/GOv0LryL/uCFgTGO8xUHmbaZblTWSQnkcD//AlvtbCkCDZMHvWuCW8zQ0Lj3YgDtHf97MYKd3Og= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn; spf=pass smtp.mailfrom=zju.edu.cn; arc=none smtp.client-ip=206.189.79.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zju.edu.cn Received: from zju.edu.cn (unknown [10.98.66.117]) by mtasvr (Coremail) with SMTP id _____wC3NjWi6FpqgykLAA--.4810S3; Sat, 18 Jul 2026 10:44:50 +0800 (CST) Received: from localhost.localdomain (unknown [10.98.66.117]) by mail-app3 (Coremail) with SMTP id zS_KCgBnMXWg6Fpq+aBHAw--.27942S3; Sat, 18 Jul 2026 10:44:50 +0800 (CST) From: Fan Wu To: arend.vanspriel@broadcom.com, kvalo@kernel.org Cc: eddiephillips@google.com, franky.lin@broadcom.com, hante.meuleman@broadcom.com, wright.feng@infineon.com, linux-wireless@vger.kernel.org, brcm80211@lists.linux.dev, brcm80211-dev-list.pdl@broadcom.com, SHA-cyfmac-dev-list@infineon.com, linux-kernel@vger.kernel.org, Fan Wu , stable@vger.kernel.org Subject: [PATCH v2 1/2] wifi: brcmfmac: make release_scratchbuffers idempotent Date: Sat, 18 Jul 2026 02:43:52 +0000 Message-Id: <20260718024353.3147201-2-fanwu01@zju.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260718024353.3147201-1-fanwu01@zju.edu.cn> References: <20260718024353.3147201-1-fanwu01@zju.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: zS_KCgBnMXWg6Fpq+aBHAw--.27942S3 X-CM-SenderInfo: qrstjiaswqq6lmxovvfxof0/ X-CM-DELIVERINFO: =?B?CIqjvQXKKxbFmtjJiESix3B1w3vZ3A9ovKVTomAyoQazvoRs/NHSP8GI2EvgeEEW7R sfnZPoDCNGYdHSfuFmYJL54WNxrLdUUyojjsh/MFpH0EOURAe6WXmf9rU7cGu361cagswD 49fJYyanNG7eGj/tRo8nLJuT1O+x+sR/9gt+BHM5 X-Coremail-Antispam: 1Uk129KBj93XoW7ZryUKFW7tw4DCrWxAr4fWFX_yoW8KF1xpF y3KFWjyF4UArWkW393uF13ZFyFkanYy345C3yxJ34fu3y5AryrXr40kFy0vrZ8ArWftay2 qw48t345Xr15WacCm3ZEXasCq-sJn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUP0b4IE77IF4wAFF20E14v26r4j6ryUM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r106r15M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_tr0E3s1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIE14v26rxl6s0DM28EF7xvwVC2z280aVCY1x0267AK xVW0oVCq3wAac4AC62xK8xCEY4vEwIxC4wAS0I0E0xvYzxvE52x082IY62kv0487Mc804V CY07AIYIkI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AK xVWUJVWUGwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48Icx kI7VAKI48JM4x0Y48IcxkI7VAKI48G6xCjnVAKz4kxM4IIrI8v6xkF7I0E8cxan2IY04v7 MxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr 0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVWUtVW8ZwCIc40Y0x0E wIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JV WxJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAI cVC2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7IU8D5r7UUUUU== Content-Type: text/plain; charset="utf-8" brcmf_pcie_release_scratchbuffers() frees the shared.scratch and shared.ringupd DMA buffers with dma_free_coherent() but does not clear the pointers afterwards, unlike the sibling release_ringbuffers() which NULLs commonrings/flowrings/idxbuf on release. Both the bus_reset .reset callback (brcmf_pcie_reset) and brcmf_pcie_remove() call release_scratchbuffers. When reset teardown has run before removal, remove's own teardown would call dma_free_coherent() a second time on the already-freed DMA allocation. NULL the pointers after free, matching release_ringbuffers(), so a later release observes that the allocation has already been released. This patch makes repeated sequential release safe; the reset-work lifetime is handled separately by the following patch. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel --- drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/driv= ers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 13662aa4b..9f10b3fff 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -1383,16 +1383,20 @@ static int brcmf_pcie_init_ringbuffers(struct brcmf= _pciedev_info *devinfo) static void brcmf_pcie_release_scratchbuffers(struct brcmf_pciedev_info *devinfo) { - if (devinfo->shared.scratch) + if (devinfo->shared.scratch) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_SCRATCH_BUF_LEN, devinfo->shared.scratch, devinfo->shared.scratch_dmahandle); - if (devinfo->shared.ringupd) + devinfo->shared.scratch =3D NULL; + } + if (devinfo->shared.ringupd) { dma_free_coherent(&devinfo->pdev->dev, BRCMF_DMA_D2H_RINGUPD_BUF_LEN, devinfo->shared.ringupd, devinfo->shared.ringupd_dmahandle); + devinfo->shared.ringupd =3D NULL; + } } =20 static int brcmf_pcie_init_scratchbuffers(struct brcmf_pciedev_info *devin= fo) --=20 2.34.1 From nobody Sat Jul 25 04:31:11 2026 Received: from zg8tmja2lje4os43os4xodqa.icoremail.net (zg8tmja2lje4os43os4xodqa.icoremail.net [206.189.79.184]) by smtp.subspace.kernel.org (Postfix) with ESMTP id 152B1283C82; Sat, 18 Jul 2026 02:45:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=206.189.79.184 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784342704; cv=none; b=NIzV419124tJd2DC666CIHybpoWlxbUh/c+HS7+OZQIEbwArokcMHlhbLMv6Tb1R2RK0qc4ct+SLx2dQ3Gu+aiEHE6U1KEWyL2KYwIZsJ+aMoNo/ibWTVCil+1R/T7vlfhLyZIgYQ3SADm4hVIF/o312zItPtu9q04Bip+10kts= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1784342704; c=relaxed/simple; bh=eDXnffeKD0hzWcYfM3THd4dXYlOC7NHguoxXnlYHei4=; h=From:To:Cc:Subject:Date:Message-Id:In-Reply-To:References: MIME-Version; b=mDsNy32ynB55THcoVDsFvxuvPjnq/5uGHdHUakPjfFJUX8qYZE0sq9Of5vKI7LhCyKR2KOQpdBNebhQls9fymYkbUQBIjsq4HWnzQyZQ/UVnkVA1mpZWz6cGAzJp1pB3FSA6x3+SyIY/9hVYP6XOT8yLBKXidBaHsb5T5P/Jqi0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn; spf=pass smtp.mailfrom=zju.edu.cn; arc=none smtp.client-ip=206.189.79.184 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=zju.edu.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=zju.edu.cn Received: from zju.edu.cn (unknown [10.98.66.117]) by mtasvr (Coremail) with SMTP id _____wDHVTWi6FpqhCkLAA--.4831S3; Sat, 18 Jul 2026 10:44:50 +0800 (CST) Received: from localhost.localdomain (unknown [10.98.66.117]) by mail-app3 (Coremail) with SMTP id zS_KCgBnMXWg6Fpq+aBHAw--.27942S4; Sat, 18 Jul 2026 10:44:50 +0800 (CST) From: Fan Wu To: arend.vanspriel@broadcom.com, kvalo@kernel.org Cc: eddiephillips@google.com, franky.lin@broadcom.com, hante.meuleman@broadcom.com, wright.feng@infineon.com, linux-wireless@vger.kernel.org, brcm80211@lists.linux.dev, brcm80211-dev-list.pdl@broadcom.com, SHA-cyfmac-dev-list@infineon.com, linux-kernel@vger.kernel.org, Fan Wu , stable@vger.kernel.org Subject: [PATCH v2 2/2] wifi: brcmfmac: drain bus_reset work on device removal Date: Sat, 18 Jul 2026 02:43:53 +0000 Message-Id: <20260718024353.3147201-3-fanwu01@zju.edu.cn> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20260718024353.3147201-1-fanwu01@zju.edu.cn> References: <20260718024353.3147201-1-fanwu01@zju.edu.cn> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-CM-TRANSID: zS_KCgBnMXWg6Fpq+aBHAw--.27942S4 X-CM-SenderInfo: qrstjiaswqq6lmxovvfxof0/ X-CM-DELIVERINFO: =?B?ZrSLbgXKKxbFmtjJiESix3B1w3vZ3A9ovKVTomAyoQazvoRs/NHSP8GI2EvgeEEW7R sfnZPoDCNGYdHSfuFmYJL54WPgbIxlnL6Ber+3Nnye7S9ARAe6WXmf9rU7cGu361cagswD 49fJYyanNG7eGj/tRo+rk1kkTs0faXjOpS++uTCd X-Coremail-Antispam: 1Uk129KBj93XoW3uw48WF4fWrW3CrWDtw1DurX_yoWkKF4rp3 y3XFyjyr4DWrW5KrZ7JFsrAF1rKanakasrKrWUu3s3u3W5Ar1rJrW0kFy29r1DCrWxC342 vF4UXw13ZrsrKacCm3ZEXasCq-sJn29KB7ZKAUJUUUUU529EdanIXcx71UUUUU7KY7ZEXa sCq-sGcSsGvfJ3Ic02F40EFcxC0VAKzVAqx4xG6I80ebIjqfuFe4nvWSU5nxnvy29KBjDU 0xBIdaVrnRJUUUP0b4IE77IF4wAFF20E14v26r4j6ryUM7CY07I20VC2zVCF04k26cxKx2 IYs7xG6rWj6s0DM7CIcVAFz4kK6r1Y6r17M28lY4IEw2IIxxk0rwA2F7IY1VAKz4vEj48v e4kI8wA2z4x0Y4vE2Ix0cI8IcVAFwI0_tr0E3s1l84ACjcxK6xIIjxv20xvEc7CjxVAFwI 0_Gr1j6F4UJwA2z4x0Y4vEx4A2jsIE14v26rxl6s0DM28EF7xvwVC2z280aVCY1x0267AK xVW0oVCq3wAac4AC62xK8xCEY4vEwIxC4wAS0I0E0xvYzxvE52x082IY62kv0487Mc804V CY07AIYIkI8VC2zVCFFI0UMc02F40EFcxC0VAKzVAqx4xG6I80ewAv7VC0I7IYx2IY67AK xVWUGVWUXwAv7VC2z280aVAFwI0_Jr0_Gr1lOx8S6xCaFVCjc4AY6r1j6r4UM4x0Y48Icx kI7VAKI48JM4x0Y48IcxkI7VAKI48G6xCjnVAKz4kxM4IIrI8v6xkF7I0E8cxan2IY04v7 MxAIw28IcxkI7VAKI48JMxC20s026xCaFVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr 0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_JrWlx4CE17CEb7AF67AKxVWUtVW8ZwCIc40Y0x0E wIxGrwCI42IY6xIIjxv20xvE14v26r1j6r1xMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8JV WxJwCI42IY6xAIw20EY4v20xvaj40_Jr0_JF4lIxAIcVC2z280aVAFwI0_Jr0_Gr1lIxAI cVC2z280aVCY1x0267AKxVW8JVW8JrUvcSsGvfC2KfnxnUUI43ZEXa7IU8zc_3UUUUU== Content-Type: text/plain; charset="utf-8" brcmf_fw_crashed() and the debugfs "reset" entry both schedule drvr->bus_reset, whose callback recovers drvr through container_of() and dereferences it. The removal path frees drvr (brcmf_free -> wiphy_free) without draining the work, so a bus_reset callback pending or running during removal can outlive drvr. Cancellation cannot live in brcmf_detach() or brcmf_free(): the work callback reaches teardown through the bus .reset op (PCIe brcmf_pcie_reset -> brcmf_detach; SDIO brcmf_sdio_bus_reset -> brcmf_sdiod_remove -> brcmf_free), so cancelling there would wait for the running work and deadlock. Add a per-bus mutex (bus_reset_lock) and route all arming through brcmf_bus_schedule_reset(), which under the lock skips when the bus is marked removing. Each bus remove entry calls brcmf_bus_cancel_reset_work(), which under the same lock sets removing and cancels the work. Holding the mutex across cancel_work_sync() makes the set-removing + drain step atomic. Every producer reaches the arming path from process context -- the PCIe firmware-halt notification runs in the threaded IRQ handler (brcmf_pcie_isr_thread) and the SDIO hostmail path runs from the data workqueue -- so the mutex is taken only in sleepable contexts. Where applicable the remove entry first stops the firmware-crash producer: on PCIe mask the mailbox and synchronize_irq; on SDIO unregister the bus interrupt and cancel the data worker, which also reports firmware halts through brcmf_fw_crashed(). The mutex is initialized at bus allocation. The SDIO suspend power-off path frees drvr through the same brcmf_sdiod_remove() and takes the same lock; resume re-allows the work only on a successful re-probe. Also guard brcmf_fw_crashed() against a NULL bus_if/drvr: it can fire before brcmf_attach() wires up drvr, and it dereferences drvr (bphy_err/brcmf_dev_coredump) before reaching the arming gate. The bus_reset work is shared across buses, so the drain is applied to every remove path: PCIe (the .reset op introduced by the Fixes commit), SDIO (arms the same work through brcmf_fw_crashed()), and USB (via the debugfs "reset" entry). cancel_work_sync() drains a running or pending bus_reset work item before removal frees drvr, and patch 1/2 makes the scratch-buffer release safe when reset teardown has already released those DMA buffers. This patch fixes the lifetime of the bus_reset work item itself. It does not attempt to address the separate, pre-existing lifetime of the asynchronous firmware completion started by the PCIe reset path. That callback needs its own lifetime/ownership protocol and is being tracked separately. This issue was found by an in-house static analysis tool. Fixes: 4684997d9eea ("brcmfmac: reset PCIe bus on a firmware crash") Cc: stable@vger.kernel.org Signed-off-by: Fan Wu Assisted-by: Codex:gpt-5.6 Acked-by: Arend van Spriel --- .../broadcom/brcm80211/brcmfmac/bcmsdh.c | 13 ++++++ .../broadcom/brcm80211/brcmfmac/bus.h | 6 +++ .../broadcom/brcm80211/brcmfmac/core.c | 46 +++++++++++++++++-- .../broadcom/brcm80211/brcmfmac/pcie.c | 6 +++ .../broadcom/brcm80211/brcmfmac/sdio.c | 6 +++ .../broadcom/brcm80211/brcmfmac/sdio.h | 1 + .../broadcom/brcm80211/brcmfmac/usb.c | 3 ++ 7 files changed, 77 insertions(+), 4 deletions(-) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/dr= ivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index d24b80e49..ec487c6f2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1069,6 +1069,7 @@ static int brcmf_ops_sdio_probe(struct sdio_func *fun= c, bus_if =3D kzalloc_obj(*bus_if); if (!bus_if) return -ENOMEM; + mutex_init(&bus_if->bus_reset_lock); sdiodev =3D kzalloc_obj(*sdiodev); if (!sdiodev) { kfree(bus_if); @@ -1130,6 +1131,14 @@ static void brcmf_ops_sdio_remove(struct sdio_func *= func) if (func->num !=3D 1) return; =20 + /* Drain bus_reset before the shared brcmf_sdiod_remove() + * teardown, which the SDIO reset callback also reaches. The + * data worker can arm bus_reset via brcmf_fw_crashed(); cancel + * it first. + */ + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); + /* only proceed with rest of cleanup if func 1 */ brcmf_sdiod_remove(sdiodev); =20 @@ -1204,6 +1213,8 @@ static int brcmf_ops_sdio_suspend(struct device *dev) } else { /* power will be cut so remove device, probe again in resume */ brcmf_sdiod_intr_unregister(sdiodev); + brcmf_sdio_cancel_datawork(sdiodev->bus); + brcmf_bus_cancel_reset_work(bus_if); ret =3D brcmf_sdiod_remove(sdiodev); if (ret) brcmf_err("Failed to remove device on suspend\n"); @@ -1229,6 +1240,8 @@ static int brcmf_ops_sdio_resume(struct device *dev) ret =3D brcmf_sdiod_probe(sdiodev); if (ret) brcmf_err("Failed to probe device on resume\n"); + else + brcmf_bus_allow_reset_work(bus_if); } else { if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_support= ed) disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h b/drive= rs/net/wireless/broadcom/brcm80211/brcmfmac/bus.h index fe31051a9..9371c1489 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bus.h @@ -9,6 +9,7 @@ #include #include #include +#include #include "debug.h" =20 /* IDs of the 6 default common rings of msgbuf protocol */ @@ -179,6 +180,8 @@ struct brcmf_bus { enum brcmf_fwvendor fwvid; bool always_use_fws_queue; bool wowl_supported; + bool removing; /* device removal in progress; quiesce async work */ + struct mutex bus_reset_lock; =20 const struct brcmf_bus_ops *ops; struct brcmf_bus_msgbuf *msgbuf; @@ -186,6 +189,9 @@ struct brcmf_bus { struct list_head list; }; =20 +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if); +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if); + /* * callback wrappers */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/driv= ers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index ec1706478..dad6f4563 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1167,6 +1167,35 @@ static int brcmf_revinfo_read(struct seq_file *s, vo= id *data) return 0; } =20 +/* + * Serialize arming from debugfs reset and brcmf_fw_crashed() against + * teardown. The remove path sets ->removing and drains the work while + * holding bus_reset_lock, so a racing armer is either drained or skips it. + */ +static void brcmf_bus_schedule_reset(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + if (bus_if->drvr && bus_if->drvr->bus_reset.func && !bus_if->removing) + schedule_work(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_cancel_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing =3D true; + if (bus_if->drvr) + cancel_work_sync(&bus_if->drvr->bus_reset); + mutex_unlock(&bus_if->bus_reset_lock); +} + +void brcmf_bus_allow_reset_work(struct brcmf_bus *bus_if) +{ + mutex_lock(&bus_if->bus_reset_lock); + bus_if->removing =3D false; + mutex_unlock(&bus_if->bus_reset_lock); +} + static void brcmf_core_bus_reset(struct work_struct *work) { struct brcmf_pub *drvr =3D container_of(work, struct brcmf_pub, @@ -1187,7 +1216,7 @@ static ssize_t bus_reset_write(struct file *file, con= st char __user *user_buf, if (value !=3D 1) return -EINVAL; =20 - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(drvr->bus_if); =20 return count; } @@ -1417,14 +1446,23 @@ void brcmf_dev_coredump(struct device *dev) void brcmf_fw_crashed(struct device *dev) { struct brcmf_bus *bus_if =3D dev_get_drvdata(dev); - struct brcmf_pub *drvr =3D bus_if->drvr; + struct brcmf_pub *drvr; + + /* May fire before brcmf_attach() wires up drvr, or after removal + * has cleared it; guard the derefs below (and the arming gate in + * brcmf_bus_schedule_reset() already checks drvr/->removing). + */ + if (!bus_if) + return; + drvr =3D bus_if->drvr; + if (!drvr) + return; =20 bphy_err(drvr, "Firmware has halted or crashed\n"); =20 brcmf_dev_coredump(dev); =20 - if (drvr->bus_reset.func) - schedule_work(&drvr->bus_reset); + brcmf_bus_schedule_reset(bus_if); } =20 void brcmf_detach(struct device *dev) diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/driv= ers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index 9f10b3fff..55f4d7b97 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2503,6 +2503,7 @@ brcmf_pcie_probe(struct pci_dev *pdev, const struct p= ci_device_id *id) ret =3D -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); bus->msgbuf =3D kzalloc_obj(*bus->msgbuf); if (!bus->msgbuf) { ret =3D -ENOMEM; @@ -2598,6 +2599,11 @@ brcmf_pcie_remove(struct pci_dev *pdev) if (devinfo->ci) brcmf_pcie_intr_disable(devinfo); =20 + if (devinfo->irq_allocated) + synchronize_irq(pdev->irq); + + brcmf_bus_cancel_reset_work(bus); + brcmf_detach(&pdev->dev); brcmf_free(&pdev->dev); =20 diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/driv= ers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index 8fb595733..f541c0130 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4560,6 +4560,12 @@ int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev) return ret; } =20 +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus) +{ + if (bus) + cancel_work_sync(&bus->datawork); +} + /* Detach and free everything */ void brcmf_sdio_remove(struct brcmf_sdio *bus) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h b/driv= ers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h index 80180d5c6..b93d153a8 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.h @@ -361,6 +361,7 @@ int brcmf_sdiod_remove(struct brcmf_sdio_dev *sdiodev); int brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev); void brcmf_sdio_remove(struct brcmf_sdio *bus); void brcmf_sdio_isr(struct brcmf_sdio *bus, bool in_isr); +void brcmf_sdio_cancel_datawork(struct brcmf_sdio *bus); =20 void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active); void brcmf_sdio_wowl_config(struct device *dev, bool enabled); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drive= rs/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 0b52f968b..b41949a9b 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -1260,6 +1260,7 @@ static int brcmf_usb_probe_cb(struct brcmf_usbdev_inf= o *devinfo, ret =3D -ENOMEM; goto fail; } + mutex_init(&bus->bus_reset_lock); =20 bus->dev =3D dev; bus_pub->bus =3D bus; @@ -1329,6 +1330,8 @@ brcmf_usb_disconnect_cb(struct brcmf_usbdev_info *dev= info) return; brcmf_dbg(USB, "Enter, bus_pub %p\n", devinfo); =20 + brcmf_bus_cancel_reset_work(devinfo->bus_pub.bus); + brcmf_detach(devinfo->dev); brcmf_free(devinfo->dev); kfree(devinfo->bus_pub.bus); --=20 2.34.1