The interrupt handler accesses the CRTC and the register map. Its
device-managed release currently happens after remove has unprepared
the clocks, and the CRTC is allocated after the interrupt is requested.
Consequently a running handler can outlive the resources it uses, both
on removal and when probe unwinds.

Request the interrupt after initializing the display objects, and
release it explicitly before tearing down the mode state and clocks.
Keep it available through drm_atomic_helper_shutdown(), which may need
vblank completion. Also release it on drm_dev_register() failure before
unwinding the display resources.

This issue was identified as part of our ongoing research while
reviewing kernel code.

Fixes: efeeaefe9be5 ("drm: Add support for the LogiCVC display controller")
Assisted-by: LLM
Co-developed-by: Ijae Kim <[email protected]>
Signed-off-by: Ijae Kim <[email protected]>
Signed-off-by: Myeonghun Pak <[email protected]>
---
Compile-tested with GCC, W=1 (arm64). No runtime testing.

 drivers/gpu/drm/logicvc/logicvc_drm.c | 21 +++++++++++++--------
 drivers/gpu/drm/logicvc/logicvc_drm.h |  1 +
 2 files changed, 14 insertions(+), 8 deletions(-)

diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.c 
b/drivers/gpu/drm/logicvc/logicvc_drm.c
index bbebf4f..cf60bbb 100644
--- a/drivers/gpu/drm/logicvc/logicvc_drm.c
+++ b/drivers/gpu/drm/logicvc/logicvc_drm.c
@@ -366,6 +366,7 @@ static int logicvc_drm_probe(struct platform_device *pdev)
        drm_dev = &logicvc->drm_dev;
 
        logicvc->regmap = regmap;
+       logicvc->irq = irq;
        INIT_LIST_HEAD(&logicvc->layers_list);
 
        caps = logicvc_drm_caps_match(logicvc);
@@ -385,13 +386,6 @@ static int logicvc_drm_probe(struct platform_device *pdev)
                goto error_reserved_mem;
        }
 
-       ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0,
-                              dev_name(dev), logicvc);
-       if (ret) {
-               drm_err(drm_dev, "Failed to request IRQ\n");
-               goto error_clocks;
-       }
-
        ret = logicvc_drm_config_parse(logicvc);
        if (ret && ret != -ENODEV) {
                drm_err(drm_dev, "Failed to parse config\n");
@@ -434,16 +428,26 @@ static int logicvc_drm_probe(struct platform_device *pdev)
                goto error_clocks;
        }
 
+       ret = devm_request_irq(dev, irq, logicvc_drm_irq_handler, 0,
+                              dev_name(dev), logicvc);
+       if (ret) {
+               drm_err(drm_dev, "Failed to request IRQ\n");
+               goto error_mode;
+       }
+
        ret = drm_dev_register(drm_dev, 0);
        if (ret) {
                drm_err(drm_dev, "Failed to register DRM device\n");
-               goto error_mode;
+               goto error_irq;
        }
 
        drm_client_setup(drm_dev, NULL);
 
        return 0;
 
+error_irq:
+       devm_free_irq(dev, irq, logicvc);
+
 error_mode:
        logicvc_mode_fini(logicvc);
 
@@ -466,6 +470,7 @@ static void logicvc_drm_remove(struct platform_device *pdev)
        drm_dev_unregister(drm_dev);
        drm_atomic_helper_shutdown(drm_dev);
 
+       devm_free_irq(dev, logicvc->irq, logicvc);
        logicvc_mode_fini(logicvc);
 
        logicvc_clocks_unprepare(logicvc);
diff --git a/drivers/gpu/drm/logicvc/logicvc_drm.h 
b/drivers/gpu/drm/logicvc/logicvc_drm.h
index e0f4787..f69538e 100644
--- a/drivers/gpu/drm/logicvc/logicvc_drm.h
+++ b/drivers/gpu/drm/logicvc/logicvc_drm.h
@@ -53,6 +53,7 @@ struct logicvc_drm {
        struct drm_device drm_dev;
        phys_addr_t reserved_mem_base;
        struct regmap *regmap;
+       int irq;
 
        struct clk *vclk;
        struct clk *vclk2;

Reply via email to