git: 009d92b25f7c - main - mt76: mt7921: prevent PM from scheduling another delayed work on detach
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Sun, 14 Jun 2026 22:32:28 UTC
The branch main has been updated by bz:
URL: https://cgit.FreeBSD.org/src/commit/?id=009d92b25f7c2d6ddf3fb4202d0a6a4612a716f1
commit 009d92b25f7c2d6ddf3fb4202d0a6a4612a716f1
Author: Bjoern A. Zeeb <bz@FreeBSD.org>
AuthorDate: 2026-06-14 16:37:10 +0000
Commit: Bjoern A. Zeeb <bz@FreeBSD.org>
CommitDate: 2026-06-14 22:31:38 +0000
mt76: mt7921: prevent PM from scheduling another delayed work on detach
Amongst others mt76_connac_pm_unref() is calling mt76_connac_power_save_sched()
which will (normaly) re-schedule the pm_work.
In various parts we also cancel that work, also during PCI detach ("shutdown",
"remove" in LinuxKPI terms).
However we also keep calling mt76_connac_pm_unref() in the detach path and thus
we get to a point where we re-scheduled the work but then the device goes away.
At that point LinuxKPI delayed work has a callput pending which is embedded in
the work structure (pm_work). The moment we free the device that structure
and callout is gone but the callout is still on the list and once that list
is walked we panic.
Simply prevent mt76_connac_power_save_sched() from getting to the point of
possibly re-scheduling the pm_work by setting pm->enable to false in the
beginning of the detach path.
The are likely more paths which will need the same treatment as the code
is by far anything from "symmetric" (that is the attach path is highly
bus independent while the detach path is implemented per-bus). Also
other chipsets share the same "logical paths" with their own names, so
they will need this too once we get to them.
Sponsored by: The FreeBSD Foundation
MFC after: 3 days
---
sys/contrib/dev/mediatek/mt76/mt7921/pci.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/sys/contrib/dev/mediatek/mt76/mt7921/pci.c b/sys/contrib/dev/mediatek/mt76/mt7921/pci.c
index 46b59c4d0390..b70335aba6bf 100644
--- a/sys/contrib/dev/mediatek/mt76/mt7921/pci.c
+++ b/sys/contrib/dev/mediatek/mt76/mt7921/pci.c
@@ -51,6 +51,17 @@ static void mt7921e_unregister_device(struct mt792x_dev *dev)
if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN)
wiphy_rfkill_stop_polling(hw->wiphy);
+#if defined(__FreeBSD__)
+ /*
+ * Prevent scheduling ps_work again in mt76_connac_power_save_sched().
+ * Otherwise upon shutdown we may have delayed work pending, which on
+ * FreeBSD means there is a callout running, but the ps_work will be
+ * freed along with the mt792x_dev and so there is a callout on a list
+ * which no longer exists.
+ */
+ pm->enable = false;
+#endif
+
cancel_work_sync(&dev->init_work);
mt76_unregister_device(&dev->mt76);
mt76_for_each_q_rx(&dev->mt76, i)