[PATCH v3 3/4] drm/imagination: stop the watchdog before tearing the device down
From: Ryan Brue
Date: Sun Sep 13 2026 - 22:11:46 EST
pvr_remove() runs pvr_watchdog_fini() after pvr_device_fini(), so the
watchdog work can still be queued while pvr_fw_fini() unmaps the
fwif_osdata it reads. The pm_runtime_suspend() earlier in remove
normally cancels the work on its way through pvr_power_fw_disable(), but
it returns -EAGAIN without calling the callback when the usage count is
raised, and -EINVAL when the device is in runtime_error, and then
nothing has cancelled the watchdog since the last pvr_power_fw_enable():
echo on > /sys/devices/platform/soc/13000000.gpu/power/control
echo 13000000.gpu > /sys/bus/platform/drivers/powervr/unbind
The worker fires within 500ms of the free and reads freed memory.
Cancel it first, before the unplug: the worker is one of the callers of
pvr_device_lost(), so waiting for it here also means the pvr_dev->lost
test that guards the unplug cannot race it.
Fixes: 727538a4bbff ("drm/imagination: Implement power management")
Cc: stable@xxxxxxxxxxxxxxx
Assisted-by: LLM
Signed-off-by: Ryan Brue <ryanbrue.dev@xxxxxxxxx>
---
drivers/gpu/drm/imagination/pvr_drv.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/imagination/pvr_drv.c b/drivers/gpu/drm/imagination/pvr_drv.c
index fc92a82a7208..20b27a468327 100644
--- a/drivers/gpu/drm/imagination/pvr_drv.c
+++ b/drivers/gpu/drm/imagination/pvr_drv.c
@@ -1469,6 +1469,9 @@ static void pvr_remove(struct platform_device *plat_dev)
struct drm_device *drm_dev = platform_get_drvdata(plat_dev);
struct pvr_device *pvr_dev = to_pvr_device(drm_dev);
+ /* Stop the watchdog before anything it reads is freed. */
+ pvr_watchdog_fini(pvr_dev);
+
/*
* Unplug before freeing anything, so no ioctl is still inside
* drm_dev_enter(). pvr_device_lost() may already have done it, and
@@ -1486,7 +1489,6 @@ static void pvr_remove(struct platform_device *plat_dev)
xa_destroy(&pvr_dev->free_list_ids);
pvr_device_fini(pvr_dev);
- pvr_watchdog_fini(pvr_dev);
pvr_queue_device_fini(pvr_dev);
pvr_context_device_fini(pvr_dev);
pvr_power_domains_fini(pvr_dev);
--
2.55.0