* [PATCH v2 01/14] drm/msm: fail the snapshot init when its worker cannot be created
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 02/14] drm/msm/mdp5: forget the hardware blocks of a previous bind Dmitry Baryshkov
` (12 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
msm_disp_snapshot_init() only logs a failure of kthread_run_worker() and
returns success, leaving the error pointer in kms->dump_worker. Both of
its users then dereference it: msm_disp_snapshot_state() queues the dump
work on it on the first display error, and msm_disp_snapshot_destroy()
only checks the pointer for NULL before passing it to
kthread_destroy_worker().
Clear the pointer and return the error instead, so that the KMS init
fails rather than leaving a snapshot facility which crashes when used.
Fixes: 98659487b845 ("drm/msm: add support to take dpu snapshot")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/msm_disp_snapshot.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c
index d99771684728..d1b10656e41a 100644
--- a/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c
+++ b/drivers/gpu/drm/msm/disp/msm_disp_snapshot.c
@@ -98,6 +98,7 @@ int msm_disp_snapshot_init(struct drm_device *drm_dev)
{
struct msm_drm_private *priv;
struct msm_kms *kms;
+ int ret;
if (!drm_dev) {
DRM_ERROR("invalid params\n");
@@ -110,12 +111,21 @@ int msm_disp_snapshot_init(struct drm_device *drm_dev)
mutex_init(&kms->dump_mutex);
kms->dump_worker = kthread_run_worker(0, "%s", "disp_snapshot");
- if (IS_ERR(kms->dump_worker))
+ if (IS_ERR(kms->dump_worker)) {
+ ret = PTR_ERR(kms->dump_worker);
DRM_ERROR("failed to create disp state task\n");
+ goto err_destroy_mutex;
+ }
kthread_init_work(&kms->dump_work, _msm_disp_snapshot_work);
return 0;
+
+err_destroy_mutex:
+ kms->dump_worker = NULL;
+ mutex_destroy(&kms->dump_mutex);
+
+ return ret;
}
void msm_disp_snapshot_destroy(struct drm_device *drm_dev)
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 02/14] drm/msm/mdp5: forget the hardware blocks of a previous bind
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 01/14] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 03/14] drm/msm: unwind msm_drm_kms_init() on failure Dmitry Baryshkov
` (11 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
Binding the MDP5 driver again, after an unbind or after a failed bind,
corrupts struct mdp5_kms. On DB820c, binding it after a failed bind
oopses in hwmixer_init():
Unable to handle kernel paging request at virtual address ffff0004d411f9d8
pc : mdp5_kms_init+0x264/0x7f8
Since mdp5_kms is allocated in mdp5_dev_probe(), it outlives the bind,
but hwpipe_init(), hwmixer_init() and interface_init() still run on
each bind and append to the hwpipes[], hwmixers[] and intfs[] arrays
without resetting their counts. The second bind writes past the end of
hwpipes[] into the fields which follow it.
Reset the counts when the hardware blocks are torn down. The blocks
themselves are devm-allocated on the MDP5 device on each bind and are
only freed when it is unbound; that leak is left for a separate change.
Fixes: 1f50db2f3e1e ("drm/msm/mdp5: move resource allocation to the _probe function")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
index 3934cd060b27..085c23820f46 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -570,6 +570,10 @@ static void mdp5_destroy(struct mdp5_kms *mdp5_kms)
pm_runtime_disable(&mdp5_kms->pdev->dev);
drm_atomic_private_obj_fini(&mdp5_kms->glob_state);
+
+ mdp5_kms->num_hwpipes = 0;
+ mdp5_kms->num_hwmixers = 0;
+ mdp5_kms->num_intfs = 0;
}
static int construct_pipes(struct mdp5_kms *mdp5_kms, int cnt,
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 03/14] drm/msm: unwind msm_drm_kms_init() on failure
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 01/14] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 02/14] drm/msm/mdp5: forget the hardware blocks of a previous bind Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 04/14] drm/msm: order the display snapshot sources around its worker Dmitry Baryshkov
` (10 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
msm_drm_kms_init() leaves behind whatever it has already set up when it
fails and lets the caller sort it out: msm_drm_init() jumps to
msm_drm_uninit(), which runs msm_drm_kms_uninit() for every device which
has a kms. That is the teardown of a fully initialised KMS -- it flushes
kms->wq, calls ->irq_uninstall() and frees the IRQ, none of which exist
yet when the failure happened early.
The kms driver's own init is the first step which can fail, and since
commit a409b78fcdf7 ("drm/msm: move wq handling to KMS code") the
workqueue is created by msm_kms_init() rather than by msm_drm_init(), so a
failure there leaves a NULL kms->wq for flush_workqueue().
Undo the steps which have completed instead, and let msm_drm_init() unwind
its own error paths rather than calling the full teardown.
Fixes: a409b78fcdf7 ("drm/msm: move wq handling to KMS code")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_drv.c | 29 ++++++++++++++++++++---------
drivers/gpu/drm/msm/msm_kms.c | 36 ++++++++++++++++++++++++------------
2 files changed, 44 insertions(+), 21 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index d157d227566a..6b24f353de7c 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -181,35 +181,46 @@ static int msm_drm_init(struct device *dev, const struct drm_driver *drv,
ret = msm_gem_shrinker_init(ddev);
if (ret)
- goto err_msm_uninit;
+ goto err_unbind;
msm_gem_thp_init(ddev);
if (priv->kms_init) {
ret = msm_drm_kms_init(dev, drv);
if (ret)
- goto err_msm_uninit;
+ goto err_shrinker_cleanup;
}
ret = drm_dev_register(ddev, 0);
if (ret)
- goto err_msm_uninit;
+ goto err_kms_uninit;
ret = msm_debugfs_late_init(ddev);
if (ret)
- goto err_msm_uninit;
+ goto err_unregister;
if (priv->kms_init)
msm_drm_kms_post_init(dev);
return 0;
-err_msm_uninit:
- msm_drm_uninit(dev, gpu_ops);
-
- return ret;
-
+err_unregister:
+ drm_dev_unregister(ddev);
+ if (priv->kms_init)
+ msm_drm_kms_unregister(dev);
+ msm_rd_debugfs_cleanup(priv);
+err_kms_uninit:
+ if (priv->kms_init)
+ msm_drm_kms_uninit(dev);
+err_shrinker_cleanup:
+ msm_gem_shrinker_cleanup(ddev);
+err_unbind:
+ if (gpu_ops)
+ gpu_ops->unbind(dev, dev, NULL);
+ else
+ component_unbind_all(dev, ddev);
err_put_dev:
+ ddev->dev_private = NULL;
drm_dev_put(ddev);
return ret;
diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
index e5d0ea629448..f3e39c3907a9 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -225,13 +225,23 @@ void msm_drm_kms_unregister(struct device *dev)
drm_atomic_helper_shutdown(ddev);
}
+static void msm_drm_kms_destroy_event_threads(struct msm_kms *kms)
+{
+ int i;
+
+ for (i = 0; i < MAX_CRTCS; i++) {
+ if (kms->event_thread[i].worker)
+ kthread_destroy_worker(kms->event_thread[i].worker);
+ kms->event_thread[i].worker = NULL;
+ }
+}
+
void msm_drm_kms_uninit(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct msm_drm_private *priv = platform_get_drvdata(pdev);
struct drm_device *ddev = priv->dev;
struct msm_kms *kms = priv->kms;
- int i;
BUG_ON(!kms);
@@ -242,11 +252,7 @@ void msm_drm_kms_uninit(struct device *dev)
flush_workqueue(kms->wq);
- /* clean up event worker threads */
- for (i = 0; i < MAX_CRTCS; i++) {
- if (kms->event_thread[i].worker)
- kthread_destroy_worker(kms->event_thread[i].worker);
- }
+ msm_drm_kms_destroy_event_threads(kms);
drm_kms_helper_poll_fini(ddev);
@@ -282,7 +288,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ret = priv->kms_init(ddev);
if (ret) {
DRM_DEV_ERROR(dev, "failed to load kms\n");
- goto err_msm_uninit;
+ goto err_destroy_kms;
}
/* Enable normalization of plane zpos */
@@ -295,7 +301,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ret = kms->funcs->hw_init(kms);
if (ret) {
DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret);
- goto err_msm_uninit;
+ goto err_destroy_kms;
}
drm_helper_move_panel_connectors_to_head(ddev);
@@ -311,7 +317,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ret = PTR_ERR(ev_thread->worker);
DRM_DEV_ERROR(dev, "failed to create crtc_event kthread\n");
ev_thread->worker = NULL;
- goto err_msm_uninit;
+ goto err_destroy_event_threads;
}
sched_set_fifo(ev_thread->worker->task);
@@ -320,7 +326,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ret = drm_vblank_init(ddev, ddev->mode_config.num_crtc);
if (ret < 0) {
DRM_DEV_ERROR(dev, "failed to initialize vblank\n");
- goto err_msm_uninit;
+ goto err_destroy_event_threads;
}
pm_runtime_get_sync(dev);
@@ -328,14 +334,20 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
pm_runtime_put_sync(dev);
if (ret < 0) {
DRM_DEV_ERROR(dev, "failed to install IRQ handler\n");
- goto err_msm_uninit;
+ goto err_destroy_event_threads;
}
drm_mode_config_reset(ddev);
return 0;
-err_msm_uninit:
+err_destroy_event_threads:
+ msm_drm_kms_destroy_event_threads(kms);
+err_destroy_kms:
+ msm_disp_snapshot_destroy(ddev);
+ if (kms->funcs)
+ kms->funcs->destroy(kms);
+
return ret;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 04/14] drm/msm: order the display snapshot sources around its worker
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (2 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 03/14] drm/msm: unwind msm_drm_kms_init() on failure Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 16:00 ` sashiko-bot
2026-10-23 15:31 ` [PATCH v2 05/14] drm/msm: clean up after a failed msm_kms_init() Dmitry Baryshkov
` (9 subsequent siblings)
13 siblings, 1 reply; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
The display snapshot work is queued on kms->dump_worker by the IOMMU
fault handler of the KMS and by the DPU interrupt handlers, but neither
of them follows the lifetime of the worker:
- the fault handler is registered by msm_kms_init_vm() in the middle of
kms_init() and is never unregistered, so a fault during kms_init(),
or in the error path of msm_drm_kms_init(), dumps a KMS which is
still being set up or already being torn down, and a fault after
msm_drm_kms_uninit() queues the work on a destroyed worker;
- msm_drm_kms_uninit() destroys the worker before it uninstalls the
interrupt handlers, so an underrun or a timeout in between queues the
work on a destroyed worker as well.
Start the sources only once the KMS is fully set up and stop them before
the worker goes away: register the fault handler as the last step of
msm_drm_kms_init(), unregister it first thing in msm_drm_kms_uninit(),
and destroy the worker after the interrupts have been uninstalled but
before the KMS itself, so that the dumps still pending run against an
intact KMS.
A fault during kms_init() therefore no longer produces a dump; the KMS
is not complete enough to describe one at that point. The handler is
cleared with the kms argument left in place, so that a fault handler
already running still gets a valid one.
Fixes: 2ec5b3dc18ba ("drm/msm: add disp snapshot points across dpu driver")
Fixes: eabba31a839a ("drm/msm: register a fault handler for display mmu faults")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_kms.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
index f3e39c3907a9..a94d2888a21e 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -182,8 +182,6 @@ struct drm_gpuvm *msm_kms_init_vm(struct drm_device *dev, struct device *mdss_de
struct drm_gpuvm *vm;
struct msm_mmu *mmu;
struct device *mdp_dev = dev->dev;
- struct msm_drm_private *priv = dev->dev_private;
- struct msm_kms *kms = priv->kms;
struct device *iommu_dev;
/*
@@ -211,8 +209,6 @@ struct drm_gpuvm *msm_kms_init_vm(struct drm_device *dev, struct device *mdss_de
return vm;
}
- msm_mmu_set_fault_handler(to_msm_vm(vm)->mmu, kms, msm_kms_fault_handler);
-
return vm;
}
@@ -245,6 +241,8 @@ void msm_drm_kms_uninit(struct device *dev)
BUG_ON(!kms);
+ msm_mmu_set_fault_handler(to_msm_vm(kms->vm)->mmu, kms, NULL);
+
/* We must cancel and cleanup any pending vblank enable/disable
* work before msm_irq_uninstall() to avoid work re-enabling an
* irq after uninstall has disabled it.
@@ -256,12 +254,12 @@ void msm_drm_kms_uninit(struct device *dev)
drm_kms_helper_poll_fini(ddev);
- msm_disp_snapshot_destroy(ddev);
-
pm_runtime_get_sync(dev);
msm_irq_uninstall(ddev);
pm_runtime_put_sync(dev);
+ msm_disp_snapshot_destroy(ddev);
+
if (kms && kms->funcs)
kms->funcs->destroy(kms);
}
@@ -339,6 +337,8 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
drm_mode_config_reset(ddev);
+ msm_mmu_set_fault_handler(to_msm_vm(kms->vm)->mmu, kms, msm_kms_fault_handler);
+
return 0;
err_destroy_event_threads:
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* Re: [PATCH v2 04/14] drm/msm: order the display snapshot sources around its worker
2026-10-23 15:31 ` [PATCH v2 04/14] drm/msm: order the display snapshot sources around its worker Dmitry Baryshkov
@ 2026-10-23 16:00 ` sashiko-bot
0 siblings, 0 replies; 16+ messages in thread
From: sashiko-bot @ 2026-10-23 16:00 UTC (permalink / raw)
To: Dmitry Baryshkov; +Cc: dri-devel
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] drm/msm: unsynchronized fault handler update in msm_mmu_set_fault_handler()
--
commit 9dc038a2fc1d880292f387fc89c8359b97d9cf93
Author: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
drm/msm: order the display snapshot sources around its worker
This commit reorders the registration and teardown of the display IOMMU
fault handler and display snapshot worker in msm_drm_kms_init() and
msm_drm_kms_uninit() so that snapshot sources are only active while the
KMS and its dump worker are initialized.
> diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
> index f3e39c3907a93..a94d2888a21e5 100644
> --- a/drivers/gpu/drm/msm/msm_kms.c
> +++ b/drivers/gpu/drm/msm/msm_kms.c
[ ... ]
> @@ -245,6 +241,8 @@ void msm_drm_kms_uninit(struct device *dev)
>
> BUG_ON(!kms);
>
> + msm_mmu_set_fault_handler(to_msm_vm(kms->vm)->mmu, kms, NULL);
> +
> /* We must cancel and cleanup any pending vblank enable/disable
> * work before msm_irq_uninstall() to avoid work re-enabling an
> * irq after uninstall has disabled it.
> @@ -256,12 +254,12 @@ void msm_drm_kms_uninit(struct device *dev)
>
> drm_kms_helper_poll_fini(ddev);
>
> - msm_disp_snapshot_destroy(ddev);
> -
> pm_runtime_get_sync(dev);
> msm_irq_uninstall(ddev);
> pm_runtime_put_sync(dev);
>
> + msm_disp_snapshot_destroy(ddev);
> +
> if (kms && kms->funcs)
> kms->funcs->destroy(kms);
> }
> @@ -339,6 +337,8 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
>
> drm_mode_config_reset(ddev);
>
> + msm_mmu_set_fault_handler(to_msm_vm(kms->vm)->mmu, kms, msm_kms_fault_handler);
[Severity: High]
Can msm_mmu_set_fault_handler() in drivers/gpu/drm/msm/msm_kms.c race with
an in-flight display IOMMU fault during msm_drm_kms_init() or
msm_drm_kms_uninit()?
In drivers/gpu/drm/msm/msm_mmu.h, msm_mmu_set_fault_handler() performs
plain unlocked stores:
static inline void msm_mmu_set_fault_handler(struct msm_mmu *mmu, void *arg,
int (*handler)(void *arg, unsigned long iova, int flags, void *data))
{
mmu->arg = arg;
mmu->handler = handler;
}
And in drivers/gpu/drm/msm/msm_iommu.c, msm_disp_fault_handler() reads and
calls those fields without locking or memory barriers:
static int msm_disp_fault_handler(struct iommu_domain *domain, struct device *dev,
unsigned long iova, int flags, void *arg)
{
struct msm_iommu *iommu = arg;
if (iommu->base.handler)
return iommu->base.handler(iommu->base.arg, iova, flags, NULL);
return -ENOSYS;
}
During msm_drm_kms_uninit(), msm_irq_uninstall() only frees the display
controller interrupt (kms->irq), while the SMMU context fault interrupt
remains active until kms->funcs->destroy(kms) detaches the IOMMU at the end
of msm_drm_kms_uninit().
If msm_disp_fault_handler() is already executing on another CPU when
msm_drm_kms_uninit() clears mmu->handler to NULL, nothing waits for
msm_disp_fault_handler() -> msm_kms_fault_handler() to finish before
msm_disp_snapshot_destroy() calls kthread_destroy_worker(kms->dump_worker).
Could an in-flight msm_kms_fault_handler() then call
msm_disp_snapshot_state() and queue kms->dump_work onto the freed
kms->dump_worker, or could a compiler reload of iommu->base.handler in
msm_disp_fault_handler() call a NULL function pointer?
Similarly, during msm_drm_kms_init(), msm_kms_init_vm() ->
msm_iommu_disp_new() attaches the IOMMU domain before
msm_mmu_set_fault_handler() is called. Without release/acquire ordering
when publishing mmu->arg and mmu->handler, could a concurrent fault on
weakly ordered architectures observe a non-NULL iommu->base.handler while
iommu->base.arg is still NULL?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261023-msm-kms-destroy-fixes-v2-0-83727c122de7@oss.qualcomm.com?part=4
^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH v2 05/14] drm/msm: clean up after a failed msm_kms_init()
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (3 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 04/14] drm/msm: order the display snapshot sources around its worker Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 06/14] drm/msm: let the kms drivers clean up a failed kms_init() Dmitry Baryshkov
` (8 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
msm_kms_init() returns early when it fails to allocate its workqueue or
to create one of the pending timer workers, leaving behind whatever it
has already set up. The kms drivers' ->destroy() callbacks still run in
that case and reach msm_kms_destroy(), which passes a workqueue that was
never allocated straight to destroy_workqueue().
Tear down the timers and the workqueue created so far when
msm_kms_init() fails. Until every kms driver has stopped relying on
->destroy() to clean up a failed init, let msm_kms_destroy() skip what
has already been destroyed.
Fixes: a409b78fcdf7 ("drm/msm: move wq handling to KMS code")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_atomic.c | 1 +
drivers/gpu/drm/msm/msm_kms.h | 20 +++++++++++++++-----
2 files changed, 16 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index a8babf1dbe0d..c26b0c7acdfa 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -134,6 +134,7 @@ void msm_atomic_destroy_pending_timer(struct msm_pending_timer *timer)
{
if (timer->worker)
kthread_destroy_worker(timer->worker);
+ timer->worker = NULL;
}
static bool can_do_async(struct drm_atomic_commit *state,
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index f25b31e502d2..ee98393b9855 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -175,7 +175,8 @@ struct msm_kms {
static inline int msm_kms_init(struct msm_kms *kms,
const struct msm_kms_funcs *funcs)
{
- unsigned i, ret;
+ unsigned int i;
+ int ret;
for (i = 0; i < ARRAY_SIZE(kms->commit_lock); i++)
mutex_init(&kms->commit_lock[i]);
@@ -188,12 +189,19 @@ static inline int msm_kms_init(struct msm_kms *kms,
for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++) {
ret = msm_atomic_init_pending_timer(&kms->pending_timers[i], kms, i);
- if (ret) {
- return ret;
- }
+ if (ret)
+ goto err_destroy_timers;
}
return 0;
+
+err_destroy_timers:
+ while (i--)
+ msm_atomic_destroy_pending_timer(&kms->pending_timers[i]);
+ destroy_workqueue(kms->wq);
+ kms->wq = NULL;
+
+ return ret;
}
static inline void msm_kms_destroy(struct msm_kms *kms)
@@ -203,7 +211,9 @@ static inline void msm_kms_destroy(struct msm_kms *kms)
for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++)
msm_atomic_destroy_pending_timer(&kms->pending_timers[i]);
- destroy_workqueue(kms->wq);
+ /* the kms drivers' ->destroy() also runs after a failed init */
+ if (kms->wq)
+ destroy_workqueue(kms->wq);
}
#define for_each_crtc_mask(dev, crtc, crtc_mask) \
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 06/14] drm/msm: let the kms drivers clean up a failed kms_init()
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (4 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 05/14] drm/msm: clean up after a failed msm_kms_init() Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 07/14] drm/msm/mdp5: unwind a failed mdp5_kms_init() Dmitry Baryshkov
` (7 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
A failed priv->kms_init() or ->hw_init() is cleaned up by the ->destroy()
callback, which makes ->destroy() responsible for tearing down a KMS in
any state between "not initialised at all" and "fully initialised". The
kernel convention is the opposite: a function which fails undoes its own
steps, and the teardown only ever sees a fully set up object.
Let the kms drivers switch to that convention one at a time: skip
->destroy() after a failed kms_init() of a driver which sets
init_unwinds, and let the drivers fold their hardware setup into
kms_init() and drop ->hw_init(). Set up the mode config before calling
kms_init(), so that it is already in place for the hardware setup.
While at it, tear the snapshot worker down after the KMS, in the reverse
order of its setup. Nothing queues a snapshot until msm_drm_kms_init()
has succeeded, so no dump can be pending when the KMS goes away.
The flag and the optional ->hw_init() go away once all the drivers have
been converted.
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/msm_kms.c | 23 ++++++++++++++---------
drivers/gpu/drm/msm/msm_kms.h | 3 +++
2 files changed, 17 insertions(+), 9 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
index a94d2888a21e..e4d61935a6df 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -283,12 +283,6 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
return ret;
}
- ret = priv->kms_init(ddev);
- if (ret) {
- DRM_DEV_ERROR(dev, "failed to load kms\n");
- goto err_destroy_kms;
- }
-
/* Enable normalization of plane zpos */
ddev->mode_config.normalize_zpos = true;
@@ -296,12 +290,22 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ddev->mode_config.helper_private = &mode_config_helper_funcs;
kms->dev = ddev;
- ret = kms->funcs->hw_init(kms);
+ ret = priv->kms_init(ddev);
if (ret) {
- DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret);
+ DRM_DEV_ERROR(dev, "failed to load kms\n");
+ if (kms->init_unwinds)
+ goto err_destroy_snapshot;
goto err_destroy_kms;
}
+ if (kms->funcs->hw_init) {
+ ret = kms->funcs->hw_init(kms);
+ if (ret) {
+ DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret);
+ goto err_destroy_kms;
+ }
+ }
+
drm_helper_move_panel_connectors_to_head(ddev);
drm_for_each_crtc(crtc, ddev) {
@@ -344,9 +348,10 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
err_destroy_event_threads:
msm_drm_kms_destroy_event_threads(kms);
err_destroy_kms:
- msm_disp_snapshot_destroy(ddev);
if (kms->funcs)
kms->funcs->destroy(kms);
+err_destroy_snapshot:
+ msm_disp_snapshot_destroy(ddev);
return ret;
}
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index ee98393b9855..2f097e23e2e9 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -149,6 +149,9 @@ struct msm_kms {
int irq;
bool irq_requested;
+ /* set by the kms drivers whose kms_init() undoes its own failures */
+ bool init_unwinds;
+
/* rate limit the snapshot capture to once per attach */
atomic_t fault_snapshot_capture;
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 07/14] drm/msm/mdp5: unwind a failed mdp5_kms_init()
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (5 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 06/14] drm/msm: let the kms drivers clean up a failed kms_init() Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 08/14] drm/msm/mdp4: unwind a failed mdp4_kms_init() Dmitry Baryshkov
` (6 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
mdp5_kms_init() returns straight away when one of its later steps fails,
leaving behind the kms, the runtime PM reference taken around the address
space setup and the address space itself, and lets the ->destroy()
callback, which the caller runs afterwards, clean up. When mdp5_init()
fails, however, it has already run mdp5_destroy() itself, and ->destroy()
runs it a second time: the global state object is finalised twice.
Undo the steps which have completed, in the reverse order, drop the
runtime PM reference on the error path as well, and tell the caller not
to run ->destroy() after a failed init. Fold the hardware setup, which
can't fail, into mdp5_kms_init().
Fixes: 8d58ef346f30 ("drm/msm/mdp5: Add global state as a private atomic object")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 45 ++++++++++++++++++++------------
1 file changed, 28 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
index 085c23820f46..30af8ef51e4c 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -19,9 +19,8 @@
#include "msm_mmu.h"
#include "mdp5_kms.h"
-static int mdp5_hw_init(struct msm_kms *kms)
+static void mdp5_hw_init(struct mdp5_kms *mdp5_kms)
{
- struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
struct device *dev = &mdp5_kms->pdev->dev;
unsigned long flags;
@@ -58,8 +57,6 @@ static int mdp5_hw_init(struct msm_kms *kms)
mdp5_ctlm_hw_reset(mdp5_kms->ctlm);
pm_runtime_put_sync(dev);
-
- return 0;
}
/* Global/shared object state funcs */
@@ -198,24 +195,26 @@ static void mdp5_complete_commit(struct msm_kms *kms, unsigned crtc_mask)
static void mdp5_destroy(struct mdp5_kms *mdp5_kms);
-static void mdp5_kms_destroy(struct msm_kms *kms)
+static void mdp5_kms_destroy_vm(struct msm_kms *kms)
{
- struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ struct msm_mmu *mmu = to_msm_vm(kms->vm)->mmu;
- if (kms->vm) {
- struct msm_mmu *mmu = to_msm_vm(kms->vm)->mmu;
+ mmu->funcs->detach(mmu);
+ drm_gpuvm_put(kms->vm);
+ kms->vm = NULL;
+}
- mmu->funcs->detach(mmu);
- drm_gpuvm_put(kms->vm);
- }
+static void mdp5_kms_destroy(struct msm_kms *kms)
+{
+ struct mdp5_kms *mdp5_kms = to_mdp5_kms(to_mdp_kms(kms));
+ mdp5_kms_destroy_vm(kms);
mdp_kms_destroy(&mdp5_kms->base);
mdp5_destroy(mdp5_kms);
}
static const struct mdp_kms_funcs kms_funcs = {
.base = {
- .hw_init = mdp5_hw_init,
.irq_preinstall = mdp5_irq_preinstall,
.irq_postinstall = mdp5_irq_postinstall,
.irq_uninstall = mdp5_irq_uninstall,
@@ -506,6 +505,8 @@ static int mdp5_kms_init(struct drm_device *dev)
struct drm_gpuvm *vm;
int i, ret;
+ kms->init_unwinds = true;
+
ret = mdp5_init(to_platform_device(dev->dev), dev);
if (ret)
return ret;
@@ -517,7 +518,7 @@ static int mdp5_kms_init(struct drm_device *dev)
ret = mdp_kms_init(&mdp5_kms->base, &kms_funcs);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "failed to init kms\n");
- return ret;
+ goto err_mdp5_destroy;
}
config = mdp5_cfg_get_config(mdp5_kms->cfg);
@@ -538,19 +539,18 @@ static int mdp5_kms_init(struct drm_device *dev)
mdelay(16);
vm = msm_kms_init_vm(mdp5_kms->dev, pdev->dev.parent);
+ pm_runtime_put_sync(&pdev->dev);
if (IS_ERR(vm)) {
ret = PTR_ERR(vm);
- return ret;
+ goto err_kms_destroy;
}
kms->vm = vm;
- pm_runtime_put_sync(&pdev->dev);
-
ret = modeset_init(mdp5_kms);
if (ret) {
DRM_DEV_ERROR(&pdev->dev, "modeset_init failed: %d\n", ret);
- return ret;
+ goto err_destroy_vm;
}
dev->mode_config.min_width = 0;
@@ -561,7 +561,18 @@ static int mdp5_kms_init(struct drm_device *dev)
dev->max_vblank_count = 0; /* max_vblank_count is set on each CRTC */
dev->vblank_disable_immediate = true;
+ mdp5_hw_init(mdp5_kms);
+
return 0;
+
+err_destroy_vm:
+ mdp5_kms_destroy_vm(kms);
+err_kms_destroy:
+ mdp_kms_destroy(&mdp5_kms->base);
+err_mdp5_destroy:
+ mdp5_destroy(mdp5_kms);
+
+ return ret;
}
static void mdp5_destroy(struct mdp5_kms *mdp5_kms)
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 08/14] drm/msm/mdp4: unwind a failed mdp4_kms_init()
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (6 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 07/14] drm/msm/mdp5: unwind a failed mdp5_kms_init() Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 09/14] drm/msm/dpu: don't tear down a failed hw_init twice Dmitry Baryshkov
` (5 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
mdp4_kms_init() returns straight away when one of its steps fails and
lets the ->destroy() callback, which the caller runs afterwards, clean
up. mdp4_destroy() however dereferences mdp4_kms->dev, which is only set
after mdp_kms_init() has succeeded, so a failure there turns into a NULL
pointer dereference. It also leaves the vdd regulator enabled.
Set mdp4_kms->dev before anything can fail, undo the steps which have
completed, in the reverse order, and tell the caller not to run
->destroy() after a failed init. Fold the hardware setup, which can't
fail, into mdp4_kms_init().
Fixes: 93c125e4ea98 ("drm/msm: don't tear down KMS twice when KMS init fails")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 61 ++++++++++++++++++++------------
1 file changed, 38 insertions(+), 23 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
index 9b1d1982e683..cc6f202aa011 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -15,9 +15,8 @@
#include "msm_mmu.h"
#include "mdp4_kms.h"
-static int mdp4_hw_init(struct msm_kms *kms)
+static void mdp4_hw_init(struct mdp4_kms *mdp4_kms)
{
- struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
struct drm_device *dev = mdp4_kms->dev;
u32 dmap_cfg, vg_cfg;
unsigned long clk;
@@ -70,8 +69,6 @@ static int mdp4_hw_init(struct msm_kms *kms)
mdp4_write(mdp4_kms, REG_MDP4_RESET_STATUS, 1);
pm_runtime_put_sync(dev->dev);
-
- return 0;
}
static void mdp4_enable_commit(struct msm_kms *kms)
@@ -118,6 +115,15 @@ static long mdp4_round_pixclk(struct msm_kms *kms, unsigned long rate,
}
}
+static void mdp4_destroy_vm(struct msm_kms *kms)
+{
+ struct msm_mmu *mmu = to_msm_vm(kms->vm)->mmu;
+
+ mmu->funcs->detach(mmu);
+ drm_gpuvm_put(kms->vm);
+ kms->vm = NULL;
+}
+
static void mdp4_destroy(struct msm_kms *kms)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
@@ -127,12 +133,7 @@ static void mdp4_destroy(struct msm_kms *kms)
msm_gem_unpin_iova(mdp4_kms->blank_cursor_bo, kms->vm);
drm_gem_object_put(mdp4_kms->blank_cursor_bo);
- if (kms->vm) {
- struct msm_mmu *mmu = to_msm_vm(kms->vm)->mmu;
-
- mmu->funcs->detach(mmu);
- drm_gpuvm_put(kms->vm);
- }
+ mdp4_destroy_vm(kms);
if (mdp4_kms->rpm_enabled)
pm_runtime_disable(dev);
@@ -142,7 +143,6 @@ static void mdp4_destroy(struct msm_kms *kms)
static const struct mdp_kms_funcs kms_funcs = {
.base = {
- .hw_init = mdp4_hw_init,
.irq_preinstall = mdp4_irq_preinstall,
.irq_postinstall = mdp4_irq_postinstall,
.irq_uninstall = mdp4_irq_uninstall,
@@ -386,7 +386,7 @@ static int mdp4_kms_init(struct drm_device *dev)
{
struct msm_drm_private *priv = dev->dev_private;
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(priv->kms));
- struct msm_kms *kms = NULL;
+ struct msm_kms *kms = priv->kms;
struct drm_gpuvm *vm;
int ret;
u32 major, minor;
@@ -395,21 +395,20 @@ static int mdp4_kms_init(struct drm_device *dev)
/* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */
max_clk = 266667000;
+ kms->init_unwinds = true;
+ mdp4_kms->dev = dev;
+
ret = mdp_kms_init(&mdp4_kms->base, &kms_funcs);
if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to init kms\n");
return ret;
}
- kms = priv->kms;
-
- mdp4_kms->dev = dev;
-
if (mdp4_kms->vdd) {
ret = regulator_enable(mdp4_kms->vdd);
if (ret) {
DRM_DEV_ERROR(dev->dev, "failed to enable regulator vdd: %d\n", ret);
- return ret;
+ goto err_kms_destroy;
}
}
@@ -421,7 +420,7 @@ static int mdp4_kms_init(struct drm_device *dev)
DRM_DEV_ERROR(dev->dev, "unexpected MDP version: v%d.%d\n",
major, minor);
ret = -ENXIO;
- return ret;
+ goto err_disable_vdd;
}
mdp4_kms->rev = minor;
@@ -430,7 +429,7 @@ static int mdp4_kms_init(struct drm_device *dev)
if (!mdp4_kms->lut_clk) {
DRM_DEV_ERROR(dev->dev, "failed to get lut_clk\n");
ret = -ENODEV;
- return ret;
+ goto err_disable_vdd;
}
clk_set_rate(mdp4_kms->lut_clk, max_clk);
}
@@ -452,7 +451,7 @@ static int mdp4_kms_init(struct drm_device *dev)
vm = msm_kms_init_vm(mdp4_kms->dev, NULL);
if (IS_ERR(vm)) {
ret = PTR_ERR(vm);
- return ret;
+ goto err_disable_rpm;
}
kms->vm = vm;
@@ -460,7 +459,7 @@ static int mdp4_kms_init(struct drm_device *dev)
ret = modeset_init(mdp4_kms);
if (ret) {
DRM_DEV_ERROR(dev->dev, "modeset_init failed: %d\n", ret);
- return ret;
+ goto err_destroy_vm;
}
mdp4_kms->blank_cursor_bo = msm_gem_new(dev, SZ_16K, MSM_BO_WC | MSM_BO_SCANOUT, NULL);
@@ -468,14 +467,14 @@ static int mdp4_kms_init(struct drm_device *dev)
ret = PTR_ERR(mdp4_kms->blank_cursor_bo);
DRM_DEV_ERROR(dev->dev, "could not allocate blank-cursor bo: %d\n", ret);
mdp4_kms->blank_cursor_bo = NULL;
- return ret;
+ goto err_destroy_vm;
}
ret = msm_gem_get_and_pin_iova(mdp4_kms->blank_cursor_bo, kms->vm,
&mdp4_kms->blank_cursor_iova);
if (ret) {
DRM_DEV_ERROR(dev->dev, "could not pin blank-cursor bo: %d\n", ret);
- return ret;
+ goto err_put_cursor;
}
dev->mode_config.min_width = 0;
@@ -483,7 +482,23 @@ static int mdp4_kms_init(struct drm_device *dev)
dev->mode_config.max_width = 2048;
dev->mode_config.max_height = 2048;
+ mdp4_hw_init(mdp4_kms);
+
return 0;
+
+err_put_cursor:
+ drm_gem_object_put(mdp4_kms->blank_cursor_bo);
+err_destroy_vm:
+ mdp4_destroy_vm(kms);
+err_disable_rpm:
+ pm_runtime_disable(dev->dev);
+err_disable_vdd:
+ if (mdp4_kms->vdd)
+ regulator_disable(mdp4_kms->vdd);
+err_kms_destroy:
+ mdp_kms_destroy(&mdp4_kms->base);
+
+ return ret;
}
static const struct dev_pm_ops mdp4_pm_ops = {
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 09/14] drm/msm/dpu: don't tear down a failed hw_init twice
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (7 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 08/14] drm/msm/mdp4: unwind a failed mdp4_kms_init() Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 10/14] drm/msm: tear down only a successfully initialised KMS Dmitry Baryshkov
` (4 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
When dpu_kms_hw_init() fails it calls _dpu_kms_hw_destroy() itself, and
the caller then runs the ->destroy() callback, which calls
_dpu_kms_hw_destroy() a second time. Since the global state object is
finalised there, the second call deletes its list entry again and frees
its state twice.
Fold the hardware setup into dpu_kms_init(), undo the steps of
dpu_kms_init() when it fails, and tell the caller not to run ->destroy()
after a failed init.
Fixes: 49e27d3c9cd6 ("drm/msm/dpu: finalise global state object")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 29 +++++++++++++++--------------
1 file changed, 15 insertions(+), 14 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index da3556eb6ecc..255beb83fc94 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -55,7 +55,6 @@
bool dpu_use_virtual_planes = true;
module_param(dpu_use_virtual_planes, bool, 0);
-static int dpu_kms_hw_init(struct msm_kms *kms);
static void _dpu_kms_mmu_destroy(struct dpu_kms *dpu_kms);
#ifdef CONFIG_DEBUG_FS
@@ -1067,7 +1066,6 @@ static void dpu_kms_mdp_snapshot(struct msm_disp_state *disp_state, struct msm_k
}
static const struct msm_kms_funcs kms_funcs = {
- .hw_init = dpu_kms_hw_init,
.irq_preinstall = dpu_core_irq_preinstall,
.irq_postinstall = dpu_irq_postinstall,
.irq_uninstall = dpu_core_irq_uninstall,
@@ -1135,21 +1133,12 @@ unsigned long dpu_kms_get_clk_rate(struct dpu_kms *dpu_kms, char *clock_name)
#define DPU_PERF_DEFAULT_MAX_CORE_CLK_RATE 412500000
-static int dpu_kms_hw_init(struct msm_kms *kms)
+static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
{
- struct dpu_kms *dpu_kms;
- struct drm_device *dev;
- int rc = -EINVAL;
+ struct drm_device *dev = dpu_kms->dev;
unsigned long max_core_clk_rate;
u32 core_rev;
-
- if (!kms) {
- DPU_ERROR("invalid kms\n");
- return rc;
- }
-
- dpu_kms = to_dpu_kms(kms);
- dev = dpu_kms->dev;
+ int rc;
dev->mode_config.cursor_width = 512;
dev->mode_config.cursor_height = 512;
@@ -1301,6 +1290,8 @@ static int dpu_kms_init(struct drm_device *ddev)
int ret = 0;
unsigned long max_freq = ULONG_MAX;
+ dpu_kms->base.init_unwinds = true;
+
opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
if (!IS_ERR(opp))
dev_pm_opp_put(opp);
@@ -1317,7 +1308,17 @@ static int dpu_kms_init(struct drm_device *ddev)
pm_runtime_enable(&pdev->dev);
dpu_kms->rpm_enabled = true;
+ ret = dpu_kms_hw_init(dpu_kms);
+ if (ret)
+ goto err_disable_rpm;
+
return 0;
+
+err_disable_rpm:
+ pm_runtime_disable(&pdev->dev);
+ msm_kms_destroy(&dpu_kms->base);
+
+ return ret;
}
static int dpu_kms_mmap_mdp5(struct dpu_kms *dpu_kms)
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 10/14] drm/msm: tear down only a successfully initialised KMS
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (8 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 09/14] drm/msm/dpu: don't tear down a failed hw_init twice Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 11/14] drm/msm/dpu: clean up the self-refresh helpers on teardown Dmitry Baryshkov
` (3 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
All the kms drivers now undo their own failures in kms_init() and have
folded their hardware setup into it.
Drop ->hw_init(), call ->destroy() only for a KMS which was initialised
successfully, and remove the flag which let the drivers opt in to that
one at a time, as well as the handling of a half torn down KMS in
msm_kms_destroy().
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 2 --
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 1 -
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 2 --
drivers/gpu/drm/msm/msm_kms.c | 16 ++--------------
drivers/gpu/drm/msm/msm_kms.h | 10 ++--------
5 files changed, 4 insertions(+), 27 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 255beb83fc94..65ba8fa697e9 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -1290,8 +1290,6 @@ static int dpu_kms_init(struct drm_device *ddev)
int ret = 0;
unsigned long max_freq = ULONG_MAX;
- dpu_kms->base.init_unwinds = true;
-
opp = dev_pm_opp_find_freq_floor(dev, &max_freq);
if (!IS_ERR(opp))
dev_pm_opp_put(opp);
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
index cc6f202aa011..b6615427cebd 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -395,7 +395,6 @@ static int mdp4_kms_init(struct drm_device *dev)
/* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */
max_clk = 266667000;
- kms->init_unwinds = true;
mdp4_kms->dev = dev;
ret = mdp_kms_init(&mdp4_kms->base, &kms_funcs);
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
index 30af8ef51e4c..c349eedd55a6 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -505,8 +505,6 @@ static int mdp5_kms_init(struct drm_device *dev)
struct drm_gpuvm *vm;
int i, ret;
- kms->init_unwinds = true;
-
ret = mdp5_init(to_platform_device(dev->dev), dev);
if (ret)
return ret;
diff --git a/drivers/gpu/drm/msm/msm_kms.c b/drivers/gpu/drm/msm/msm_kms.c
index e4d61935a6df..9a207c1e8583 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -293,17 +293,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
ret = priv->kms_init(ddev);
if (ret) {
DRM_DEV_ERROR(dev, "failed to load kms\n");
- if (kms->init_unwinds)
- goto err_destroy_snapshot;
- goto err_destroy_kms;
- }
-
- if (kms->funcs->hw_init) {
- ret = kms->funcs->hw_init(kms);
- if (ret) {
- DRM_DEV_ERROR(dev, "kms hw init failed: %d\n", ret);
- goto err_destroy_kms;
- }
+ goto err_destroy_snapshot;
}
drm_helper_move_panel_connectors_to_head(ddev);
@@ -347,9 +337,7 @@ int msm_drm_kms_init(struct device *dev, const struct drm_driver *drv)
err_destroy_event_threads:
msm_drm_kms_destroy_event_threads(kms);
-err_destroy_kms:
- if (kms->funcs)
- kms->funcs->destroy(kms);
+ kms->funcs->destroy(kms);
err_destroy_snapshot:
msm_disp_snapshot_destroy(ddev);
diff --git a/drivers/gpu/drm/msm/msm_kms.h b/drivers/gpu/drm/msm/msm_kms.h
index 2f097e23e2e9..b5e1cd38cd8a 100644
--- a/drivers/gpu/drm/msm/msm_kms.h
+++ b/drivers/gpu/drm/msm/msm_kms.h
@@ -23,8 +23,6 @@
* for constructing the appropriate planes/crtcs/encoders/connectors.
*/
struct msm_kms_funcs {
- /* hw initialization: */
- int (*hw_init)(struct msm_kms *kms);
/* irq handling: */
void (*irq_preinstall)(struct msm_kms *kms);
int (*irq_postinstall)(struct msm_kms *kms);
@@ -149,9 +147,6 @@ struct msm_kms {
int irq;
bool irq_requested;
- /* set by the kms drivers whose kms_init() undoes its own failures */
- bool init_unwinds;
-
/* rate limit the snapshot capture to once per attach */
atomic_t fault_snapshot_capture;
@@ -214,9 +209,8 @@ static inline void msm_kms_destroy(struct msm_kms *kms)
for (i = 0; i < ARRAY_SIZE(kms->pending_timers); i++)
msm_atomic_destroy_pending_timer(&kms->pending_timers[i]);
- /* the kms drivers' ->destroy() also runs after a failed init */
- if (kms->wq)
- destroy_workqueue(kms->wq);
+ destroy_workqueue(kms->wq);
+ kms->wq = NULL;
}
#define for_each_crtc_mask(dev, crtc, crtc_mask) \
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 11/14] drm/msm/dpu: clean up the self-refresh helpers on teardown
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (9 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 10/14] drm/msm: tear down only a successfully initialised KMS Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 12/14] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step Dmitry Baryshkov
` (2 subsequent siblings)
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
Unbinding the display driver on MSM8996 with DPU oopses about a second
later:
Unable to handle kernel NULL pointer dereference at virtual address 0000000000000870
pc : drm_atomic_commit_alloc+0x10/0x9c
lr : drm_self_refresh_helper_entry_work+0x5c/0x1dc
dpu_crtc_init() sets up the self-refresh helper for each CRTC, but
nothing ever calls drm_self_refresh_helper_cleanup(). Every modeset
commit, including the one which shuts the display down on unbind, queues
the delayed entry work of the helper, which then runs after the CRTC
and the DRM device have been freed, and its data is leaked.
Clean the helpers up when tearing down the DPU hardware, which cancels
the pending work while the KMS is still intact. Doing it from a managed
action instead would not be enough: the DRM device, and its managed
resources with it, outlives the unbind as long as userspace holds it
open.
Fixes: 1122697810e5 ("drm/msm/disp/dpu: add PSR support for eDP interface in dpu driver")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 65ba8fa697e9..934ba47b2dae 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -17,6 +17,7 @@
#include <drm/drm_crtc.h>
#include <drm/drm_file.h>
#include <drm/drm_framebuffer.h>
+#include <drm/drm_self_refresh_helper.h>
#include <drm/drm_vblank.h>
#include <drm/drm_writeback.h>
@@ -885,8 +886,18 @@ static int _dpu_kms_drm_obj_init(struct dpu_kms *dpu_kms)
return 0;
}
+static void dpu_kms_self_refresh_cleanup(struct dpu_kms *dpu_kms)
+{
+ struct drm_crtc *crtc;
+
+ drm_for_each_crtc(crtc, dpu_kms->dev)
+ drm_self_refresh_helper_cleanup(crtc);
+}
+
static void _dpu_kms_hw_destroy(struct dpu_kms *dpu_kms)
{
+ dpu_kms_self_refresh_cleanup(dpu_kms);
+
dpu_kms->hw_intr = NULL;
/* safe to call these more than once during shutdown */
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 12/14] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (10 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 11/14] drm/msm/dpu: clean up the self-refresh helpers on teardown Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 13/14] drm/msm/mdp4: disable the vdd regulator on teardown Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 14/14] drm/msm: drop the runtime PM flags from the KMS teardown Dmitry Baryshkov
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
When dpu_kms_hw_init() fails it calls _dpu_kms_hw_destroy(), the teardown
of a fully initialised KMS, whatever step it failed at.
Undo only the steps which have completed, in the reverse order, as the
kernel convention wants.
The hw_mdp, hw_vbif and hw_intr blocks are allocated with drmm and go
away with the DRM device, while dpu_kms stays around for the next bind
attempt, so clear the pointers to them as _dpu_kms_hw_destroy() did.
Otherwise dpu_runtime_resume() of the next bind attempt passes the stale
hw_vbif to dpu_vbif_init_memtypes() before dpu_kms_hw_init() sets it.
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 31 ++++++++++++++++++-------------
1 file changed, 18 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 934ba47b2dae..3546efacba3c 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -1161,7 +1161,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
rc = pm_runtime_resume_and_get(&dpu_kms->pdev->dev);
if (rc < 0)
- goto error;
+ goto err_global_obj_fini;
core_rev = readl_relaxed(dpu_kms->mmio + 0x0);
@@ -1188,19 +1188,19 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
if (IS_ERR(dpu_kms->mdss)) {
rc = PTR_ERR(dpu_kms->mdss);
DPU_ERROR("failed to get UBWC config data: %d\n", rc);
- goto err_pm_put;
+ goto err_mmu_destroy;
}
if (!dpu_kms->mdss) {
rc = -EINVAL;
DPU_ERROR("NULL MDSS data\n");
- goto err_pm_put;
+ goto err_mmu_destroy;
}
rc = dpu_rm_init(dev, &dpu_kms->rm, dpu_kms->catalog, dpu_kms->mdss, dpu_kms->mmio);
if (rc) {
DPU_ERROR("rm init failed: %d\n", rc);
- goto err_pm_put;
+ goto err_mmu_destroy;
}
dpu_kms->hw_mdp = dpu_hw_mdptop_init(dev,
@@ -1210,8 +1210,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
if (IS_ERR(dpu_kms->hw_mdp)) {
rc = PTR_ERR(dpu_kms->hw_mdp);
DPU_ERROR("failed to get hw_mdp: %d\n", rc);
- dpu_kms->hw_mdp = NULL;
- goto err_pm_put;
+ goto err_mmu_destroy;
}
struct dpu_hw_vbif *hw;
@@ -1221,7 +1220,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
if (IS_ERR(hw)) {
rc = PTR_ERR(hw);
DPU_ERROR("failed to init vbif: %d\n", rc);
- goto err_pm_put;
+ goto err_mmu_destroy;
}
dpu_kms->hw_vbif = hw;
@@ -1236,7 +1235,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
rc = dpu_core_perf_init(&dpu_kms->perf, dpu_kms->catalog->perf, max_core_clk_rate);
if (rc) {
DPU_ERROR("failed to init perf %d\n", rc);
- goto err_pm_put;
+ goto err_mmu_destroy;
}
/*
@@ -1253,8 +1252,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
if (IS_ERR(dpu_kms->hw_intr)) {
rc = PTR_ERR(dpu_kms->hw_intr);
DPU_ERROR("hw_intr init failed: %d\n", rc);
- dpu_kms->hw_intr = NULL;
- goto err_pm_put;
+ goto err_mmu_destroy;
}
dev->mode_config.min_width = 0;
@@ -1274,7 +1272,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
rc = _dpu_kms_drm_obj_init(dpu_kms);
if (rc) {
DPU_ERROR("modeset init failed: %d\n", rc);
- goto err_pm_put;
+ goto err_self_refresh_cleanup;
}
dpu_vbif_init_memtypes(dpu_kms);
@@ -1283,10 +1281,17 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
return 0;
+err_self_refresh_cleanup:
+ dpu_kms_self_refresh_cleanup(dpu_kms);
+err_mmu_destroy:
+ dpu_kms->hw_intr = NULL;
+ dpu_kms->hw_vbif = NULL;
+ dpu_kms->hw_mdp = NULL;
+ _dpu_kms_mmu_destroy(dpu_kms);
err_pm_put:
pm_runtime_put_sync(&dpu_kms->pdev->dev);
-error:
- _dpu_kms_hw_destroy(dpu_kms);
+err_global_obj_fini:
+ dpu_kms_global_obj_fini(dpu_kms);
return rc;
}
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 13/14] drm/msm/mdp4: disable the vdd regulator on teardown
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (11 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 12/14] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
2026-10-23 15:31 ` [PATCH v2 14/14] drm/msm: drop the runtime PM flags from the KMS teardown Dmitry Baryshkov
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
mdp4_kms_init() enables the optional vdd regulator, but only its error
path disables it again: unbinding the display leaves it on, and the
regulator core warns when the devm-managed exclusive handle is released
while enabled.
Disable it again in mdp4_destroy().
Fixes: c8afe684c95c ("drm/msm: basic KMS driver for snapdragon")
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
index b6615427cebd..58a269cae31d 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -138,6 +138,9 @@ static void mdp4_destroy(struct msm_kms *kms)
if (mdp4_kms->rpm_enabled)
pm_runtime_disable(dev);
+ if (mdp4_kms->vdd)
+ regulator_disable(mdp4_kms->vdd);
+
mdp_kms_destroy(&mdp4_kms->base);
}
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread* [PATCH v2 14/14] drm/msm: drop the runtime PM flags from the KMS teardown
2026-10-23 15:31 [PATCH v2 00/14] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (12 preceding siblings ...)
2026-10-23 15:31 ` [PATCH v2 13/14] drm/msm/mdp4: disable the vdd regulator on teardown Dmitry Baryshkov
@ 2026-10-23 15:31 ` Dmitry Baryshkov
13 siblings, 0 replies; 16+ messages in thread
From: Dmitry Baryshkov @ 2026-10-23 15:31 UTC (permalink / raw)
To: Rob Clark, Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang,
Sean Paul, Marijn Suijten, David Airlie, Simona Vetter
Cc: linux-arm-msm, dri-devel, freedreno, linux-kernel
The DPU, MDP4 and MDP5 drivers track whether they have enabled runtime PM,
and MDP4 whether it has pinned its blank cursor, so that ->destroy() can
tear down a KMS whose init failed half way. ->destroy() now only runs for
a KMS which was initialised successfully, where both are always true.
Drop the flags and the checks.
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 4 +---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 1 -
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 7 ++-----
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h | 2 --
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 5 +----
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h | 2 --
6 files changed, 4 insertions(+), 17 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 3546efacba3c..e994fd267c5b 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -927,8 +927,7 @@ static void dpu_kms_destroy(struct msm_kms *kms)
msm_kms_destroy(&dpu_kms->base);
- if (dpu_kms->rpm_enabled)
- pm_runtime_disable(&dpu_kms->pdev->dev);
+ pm_runtime_disable(&dpu_kms->pdev->dev);
}
static int dpu_irq_postinstall(struct msm_kms *kms)
@@ -1320,7 +1319,6 @@ static int dpu_kms_init(struct drm_device *ddev)
dpu_kms->dev = ddev;
pm_runtime_enable(&pdev->dev);
- dpu_kms->rpm_enabled = true;
ret = dpu_kms_hw_init(dpu_kms);
if (ret)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
index e39831a397b1..769b8ce6332f 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h
@@ -87,7 +87,6 @@ struct dpu_kms {
bool has_danger_ctrl;
struct platform_device *pdev;
- bool rpm_enabled;
struct clk_bulk_data *clocks;
size_t num_clocks;
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
index 58a269cae31d..0668ae79f6f1 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -129,14 +129,12 @@ static void mdp4_destroy(struct msm_kms *kms)
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
struct device *dev = mdp4_kms->dev->dev;
- if (mdp4_kms->blank_cursor_iova)
- msm_gem_unpin_iova(mdp4_kms->blank_cursor_bo, kms->vm);
+ msm_gem_unpin_iova(mdp4_kms->blank_cursor_bo, kms->vm);
drm_gem_object_put(mdp4_kms->blank_cursor_bo);
mdp4_destroy_vm(kms);
- if (mdp4_kms->rpm_enabled)
- pm_runtime_disable(dev);
+ pm_runtime_disable(dev);
if (mdp4_kms->vdd)
regulator_disable(mdp4_kms->vdd);
@@ -437,7 +435,6 @@ static int mdp4_kms_init(struct drm_device *dev)
}
pm_runtime_enable(dev->dev);
- mdp4_kms->rpm_enabled = true;
/* make sure things are off before attaching iommu (bootloader could
* have left things on, in which case we'll start getting faults if
diff --git a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h
index 06458d4ee48c..9dd829ec9401 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h
@@ -34,8 +34,6 @@ struct mdp4_kms {
struct mdp_irq error_handler;
- bool rpm_enabled;
-
/* empty/blank cursor bo to use when cursor is "disabled" */
struct drm_gem_object *blank_cursor_bo;
uint64_t blank_cursor_iova;
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
index c349eedd55a6..bbf459bff35d 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -575,9 +575,7 @@ static int mdp5_kms_init(struct drm_device *dev)
static void mdp5_destroy(struct mdp5_kms *mdp5_kms)
{
- if (mdp5_kms->rpm_enabled)
- pm_runtime_disable(&mdp5_kms->pdev->dev);
-
+ pm_runtime_disable(&mdp5_kms->pdev->dev);
drm_atomic_private_obj_fini(&mdp5_kms->glob_state);
mdp5_kms->num_hwpipes = 0;
@@ -734,7 +732,6 @@ static int mdp5_init(struct platform_device *pdev, struct drm_device *dev)
clk_set_rate(mdp5_kms->core_clk, 200000000);
pm_runtime_enable(&pdev->dev);
- mdp5_kms->rpm_enabled = true;
read_mdp_hw_revision(mdp5_kms, &major, &minor);
diff --git a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
index 7bf2172fce0b..c0b163c4baa3 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h
@@ -62,8 +62,6 @@ struct mdp5_kms {
*/
spinlock_t resource_lock;
- bool rpm_enabled;
-
struct mdp_irq error_handler;
int enable_count;
--
2.47.3
^ permalink raw reply related [flat|nested] 16+ messages in thread