* [PATCH 00/11] drm/msm: fix the error handling of the KMS init
@ 2026-10-03 0:27 Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 01/11] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
` (10 more replies)
0 siblings, 11 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 setting up the KMS fails, msm_drm_kms_init() cleans up by calling
the ->destroy() callback of the kms driver. That makes ->destroy()
responsible for tearing down a KMS in any state, from "not initialised at
all" to "fully initialised", and the drivers get it wrong in several
ways:
- dpu_kms_hw_init() and mdp5_init() already clean up after their own
failures, and ->destroy() then finalises the global state object a
second time;
- mdp4_destroy() dereferences mdp4_kms->dev, which isn't set yet when
mdp_kms_init() fails;
- msm_kms_init() leaves a NULL workqueue behind when it fails to
allocate it, which msm_kms_destroy() passes to destroy_workqueue();
- msm_drm_kms_init() hands a half initialised KMS to the full teardown
of msm_drm_kms_uninit(), and the snapshot init ignores the failure to
create its worker.
Most of these were pointed out by Sashiko while reviewing the "drm/msm:
fix SMMU fault dumps" series, which has been carrying the first fixes.
Rather than teaching ->destroy() about ever more partial states, this
series switches the KMS init to the usual kernel convention: every init
function undoes its own steps when it fails, and ->destroy() only ever
tears down a KMS which was initialised successfully. This reverses the
approach of commit 93c125e4ea98 ("drm/msm: don't tear down KMS twice when
KMS init fails"), which moved all of the cleanup into ->destroy().
To keep every step safe and bisectable, the drivers are converted one at
a time: a temporary flag tells msm_drm_kms_init() that a driver cleans up
its own failures, and both the flag and ->hw_init(), which gets folded
into the drivers' kms_init(), are removed once all the drivers have been
converted. The last patches are follow-ups which the new rules allow:
unwinding the DPU hardware setup step by step, disabling the MDP4 vdd
regulator on teardown, and dropping the runtime PM flags which
->destroy() no longer needs.
Tested on DB820c (MSM8996) with both the MDP5 and the DPU drivers, with
failures injected into the msm_kms_init(), the MDP5 kms_init(), the DPU
hardware setup and after a successful kms_init(): each of them now fails
the bind cleanly, without warnings. MDP4 is build-tested only.
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
Dmitry Baryshkov (11):
drm/msm: fail the snapshot init when its worker cannot be created
drm/msm: unwind msm_drm_kms_init() on failure
drm/msm: clean up after a failed msm_kms_init()
drm/msm: let the kms drivers clean up a failed kms_init()
drm/msm/mdp5: unwind a failed mdp5_kms_init()
drm/msm/mdp4: unwind a failed mdp4_kms_init()
drm/msm/dpu: don't tear down a failed hw_init twice
drm/msm: tear down only a successfully initialised KMS
drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step
drm/msm/mdp4: disable the vdd regulator on teardown
drm/msm: drop the runtime PM flags from the KMS teardown
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 55 ++++++++++++------------
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.h | 1 -
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c | 63 +++++++++++++++++-----------
drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.h | 2 -
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c | 47 +++++++++++----------
drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.h | 2 -
drivers/gpu/drm/msm/disp/msm_disp_snapshot.c | 12 +++++-
drivers/gpu/drm/msm/msm_drv.c | 27 +++++++++---
drivers/gpu/drm/msm/msm_kms.c | 43 ++++++++++---------
drivers/gpu/drm/msm/msm_kms.h | 17 +++++---
10 files changed, 159 insertions(+), 110 deletions(-)
---
base-commit: a15fac810c76397ec9f62a6fc26c4d7ab6e238a7
change-id: 20260930-msm-kms-destroy-fixes-0f04b1d39b83
Best regards,
--
With best wishes
Dmitry
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 01/11] drm/msm: fail the snapshot init when its worker cannot be created
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 02/11] drm/msm: unwind msm_drm_kms_init() on failure Dmitry Baryshkov
` (9 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 [flat|nested] 12+ messages in thread
* [PATCH 02/11] drm/msm: unwind msm_drm_kms_init() on failure
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 01/11] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 03/11] drm/msm: clean up after a failed msm_kms_init() Dmitry Baryshkov
` (8 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 | 27 +++++++++++++++++++++------
drivers/gpu/drm/msm/msm_kms.c | 36 ++++++++++++++++++++++++------------
2 files changed, 45 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c
index f3d2eaa04f14..d0848a831e8b 100644
--- a/drivers/gpu/drm/msm/msm_drv.c
+++ b/drivers/gpu/drm/msm/msm_drv.c
@@ -159,29 +159,44 @@ 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;
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);
+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);
+ 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 [flat|nested] 12+ messages in thread
* [PATCH 03/11] drm/msm: clean up after a failed msm_kms_init()
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 01/11] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 02/11] drm/msm: unwind msm_drm_kms_init() on failure Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 04/11] drm/msm: let the kms drivers clean up a failed kms_init() Dmitry Baryshkov
` (7 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 [flat|nested] 12+ messages in thread
* [PATCH 04/11] drm/msm: let the kms drivers clean up a failed kms_init()
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (2 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 03/11] drm/msm: clean up after a failed msm_kms_init() Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 05/11] drm/msm/mdp5: unwind a failed mdp5_kms_init() Dmitry Baryshkov
` (6 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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.
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 f3e39c3907a9..f65774b04c6f 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -285,12 +285,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;
@@ -298,12 +292,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 [flat|nested] 12+ messages in thread
* [PATCH 05/11] drm/msm/mdp5: unwind a failed mdp5_kms_init()
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (3 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 04/11] drm/msm: let the kms drivers clean up a failed kms_init() Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 06/11] drm/msm/mdp4: unwind a failed mdp4_kms_init() Dmitry Baryshkov
` (5 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 | 44 ++++++++++++++++++++------------
1 file changed, 27 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 3934cd060b27..8fc8cdcf7d8c 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,25 @@ 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);
+}
- 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 +504,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 +517,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 +538,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 +560,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 [flat|nested] 12+ messages in thread
* [PATCH 06/11] drm/msm/mdp4: unwind a failed mdp4_kms_init()
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (4 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 05/11] drm/msm/mdp5: unwind a failed mdp5_kms_init() Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 07/11] drm/msm/dpu: don't tear down a failed hw_init twice Dmitry Baryshkov
` (4 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 | 56 ++++++++++++++++++++------------
1 file changed, 36 insertions(+), 20 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..8e51305f8dde 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,14 @@ 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);
+}
+
static void mdp4_destroy(struct msm_kms *kms)
{
struct mdp4_kms *mdp4_kms = to_mdp4_kms(to_mdp_kms(kms));
@@ -127,12 +132,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 +142,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,
@@ -395,6 +394,9 @@ static int mdp4_kms_init(struct drm_device *dev)
/* TODO: Chips that aren't apq8064 have a 200 Mhz max_clk */
max_clk = 266667000;
+ priv->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");
@@ -403,13 +405,11 @@ static int mdp4_kms_init(struct drm_device *dev)
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 +421,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 +430,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 +452,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 +460,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 +468,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 +483,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 [flat|nested] 12+ messages in thread
* [PATCH 07/11] drm/msm/dpu: don't tear down a failed hw_init twice
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (5 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 06/11] drm/msm/mdp4: unwind a failed mdp4_kms_init() Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 08/11] drm/msm: tear down only a successfully initialised KMS Dmitry Baryshkov
` (3 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 [flat|nested] 12+ messages in thread
* [PATCH 08/11] drm/msm: tear down only a successfully initialised KMS
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (6 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 07/11] drm/msm/dpu: don't tear down a failed hw_init twice Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 09/11] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step Dmitry Baryshkov
` (2 subsequent siblings)
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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_atomic.c | 1 -
drivers/gpu/drm/msm/msm_kms.c | 16 ++--------------
drivers/gpu/drm/msm/msm_kms.h | 10 +---------
6 files changed, 3 insertions(+), 29 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 8e51305f8dde..62a79bdfca19 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -394,7 +394,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;
- priv->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 8fc8cdcf7d8c..80bb6ac56618 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -504,8 +504,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_atomic.c b/drivers/gpu/drm/msm/msm_atomic.c
index c26b0c7acdfa..a8babf1dbe0d 100644
--- a/drivers/gpu/drm/msm/msm_atomic.c
+++ b/drivers/gpu/drm/msm/msm_atomic.c
@@ -134,7 +134,6 @@ 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.c b/drivers/gpu/drm/msm/msm_kms.c
index f65774b04c6f..7e1df472592d 100644
--- a/drivers/gpu/drm/msm/msm_kms.c
+++ b/drivers/gpu/drm/msm/msm_kms.c
@@ -295,17 +295,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..517622367cc0 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;
@@ -202,7 +197,6 @@ static inline int msm_kms_init(struct msm_kms *kms,
while (i--)
msm_atomic_destroy_pending_timer(&kms->pending_timers[i]);
destroy_workqueue(kms->wq);
- kms->wq = NULL;
return ret;
}
@@ -214,9 +208,7 @@ 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);
}
#define for_each_crtc_mask(dev, crtc, crtc_mask) \
--
2.47.3
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 09/11] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (7 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 08/11] drm/msm: tear down only a successfully initialised KMS Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 10/11] drm/msm/mdp4: disable the vdd regulator on teardown Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 11/11] drm/msm: drop the runtime PM flags from the KMS teardown Dmitry Baryshkov
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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.
Assisted-by: LLM
Signed-off-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
---
drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c | 24 +++++++++++++-----------
1 file changed, 13 insertions(+), 11 deletions(-)
diff --git a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
index 65ba8fa697e9..2de4d881d13d 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -1150,7 +1150,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);
@@ -1177,19 +1177,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,
@@ -1200,7 +1200,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
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;
@@ -1210,7 +1210,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;
@@ -1225,7 +1225,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;
}
/*
@@ -1243,7 +1243,7 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
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;
@@ -1263,7 +1263,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_mmu_destroy;
}
dpu_vbif_init_memtypes(dpu_kms);
@@ -1272,10 +1272,12 @@ static int dpu_kms_hw_init(struct dpu_kms *dpu_kms)
return 0;
+err_mmu_destroy:
+ _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 [flat|nested] 12+ messages in thread
* [PATCH 10/11] drm/msm/mdp4: disable the vdd regulator on teardown
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (8 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 09/11] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 11/11] drm/msm: drop the runtime PM flags from the KMS teardown Dmitry Baryshkov
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 62a79bdfca19..8529e7b2008d 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -137,6 +137,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 [flat|nested] 12+ messages in thread
* [PATCH 11/11] drm/msm: drop the runtime PM flags from the KMS teardown
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
` (9 preceding siblings ...)
2026-10-03 0:27 ` [PATCH 10/11] drm/msm/mdp4: disable the vdd regulator on teardown Dmitry Baryshkov
@ 2026-10-03 0:27 ` Dmitry Baryshkov
10 siblings, 0 replies; 12+ messages in thread
From: Dmitry Baryshkov @ 2026-10-03 0:27 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 2de4d881d13d..9756ebcce1b1 100644
--- a/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
+++ b/drivers/gpu/drm/msm/disp/dpu1/dpu_kms.c
@@ -916,8 +916,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)
@@ -1306,7 +1305,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 8529e7b2008d..daca49641811 100644
--- a/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp4/mdp4_kms.c
@@ -128,14 +128,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);
@@ -438,7 +436,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 80bb6ac56618..af9ac108c06c 100644
--- a/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
+++ b/drivers/gpu/drm/msm/disp/mdp5/mdp5_kms.c
@@ -574,9 +574,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);
}
@@ -729,7 +727,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 [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-10-03 0:27 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-03 0:27 [PATCH 00/11] drm/msm: fix the error handling of the KMS init Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 01/11] drm/msm: fail the snapshot init when its worker cannot be created Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 02/11] drm/msm: unwind msm_drm_kms_init() on failure Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 03/11] drm/msm: clean up after a failed msm_kms_init() Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 04/11] drm/msm: let the kms drivers clean up a failed kms_init() Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 05/11] drm/msm/mdp5: unwind a failed mdp5_kms_init() Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 06/11] drm/msm/mdp4: unwind a failed mdp4_kms_init() Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 07/11] drm/msm/dpu: don't tear down a failed hw_init twice Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 08/11] drm/msm: tear down only a successfully initialised KMS Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 09/11] drm/msm/dpu: unwind a failed dpu_kms_hw_init() step by step Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 10/11] drm/msm/mdp4: disable the vdd regulator on teardown Dmitry Baryshkov
2026-10-03 0:27 ` [PATCH 11/11] drm/msm: drop the runtime PM flags from the KMS teardown Dmitry Baryshkov
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®