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: [email protected]
Assisted-by: LLM
Signed-off-by: Ryan Brue <[email protected]>
---
 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

Reply via email to