[PATCH v3 3/8] drm/msm: unwind msm_drm_kms_init() on failure

From: Dmitry Baryshkov

Date: Sat Sep 12 2026 - 08:48:59 EST


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@xxxxxxxxxxxxxxxx>
---
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 db1b655dd055..c842321c9069 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