MLK-19673-1: drm/imx/core: add a workqueue and a commit object
authorLaurentiu Palcu <laurentiu.palcu@nxp.com>
Fri, 5 Oct 2018 12:35:54 +0000 (15:35 +0300)
committerLeonard Crestez <leonard.crestez@nxp.com>
Wed, 17 Apr 2019 23:51:34 +0000 (02:51 +0300)
This change adds a workqueue and a commit object that can be used by the
drivers to protect pending commits (non-blocking ones) from concurent
commits using legacy API (for example).

A non-blocking commit will defer the work to a workqueue and it may wait
for fences to be cleared. Waiting for fences to be cleared is
interruptible. Hence, if a SETPLANE IOCTL is performed (to disable a
plane), it may preempt the current commit and will mess up the atomic
states.  When the legacy calls finish, the non-blocking commit worker
will resume, but the crtc and/or FBs of some planes are already NULL.
Hence, the non-blocking commit will crash in
drm_atomic_helper_commit_planes() with NULL pointer dereference.

This particular patch does not affect existing drivers in any way.

Signed-off-by: Laurentiu Palcu <laurentiu.palcu@nxp.com>
CC: Ying Liu <victor.liu@nxp.com>
CC: Fancy Fang <chen.fang@nxp.com>
drivers/gpu/drm/imx/imx-drm-core.c
drivers/gpu/drm/imx/imx-drm.h

index 158e55c..6a58462 100644 (file)
@@ -188,12 +188,30 @@ static inline bool has_dcss(struct device *dev)
 static int imx_drm_bind(struct device *dev)
 {
        struct drm_device *drm;
+       struct imx_drm_device *imxdrm;
        int ret;
 
        drm = drm_dev_alloc(&imx_drm_driver, dev);
        if (IS_ERR(drm))
                return PTR_ERR(drm);
 
+       imxdrm = devm_kzalloc(dev, sizeof(*imxdrm), GFP_KERNEL);
+       if (!imxdrm) {
+               ret = -ENOMEM;
+               goto err_unref;
+       }
+
+       imxdrm->drm = drm;
+       drm->dev_private = imxdrm;
+
+       imxdrm->wq = alloc_ordered_workqueue("imxdrm", 0);
+       if (!imxdrm->wq) {
+               ret = -ENOMEM;
+               goto err_unref;
+       }
+
+       init_waitqueue_head(&imxdrm->commit.wait);
+
        /*
         * enable drm irq mode.
         * - with irq_enabled = true, we can use the vblank feature.
@@ -272,6 +290,10 @@ err_unbind:
        component_unbind_all(drm->dev, drm);
 err_kms:
        drm_mode_config_cleanup(drm);
+
+       destroy_workqueue(imxdrm->wq);
+
+err_unref:
        drm_dev_put(drm);
 
        return ret;
@@ -280,6 +302,9 @@ err_kms:
 static void imx_drm_unbind(struct device *dev)
 {
        struct drm_device *drm = dev_get_drvdata(dev);
+       struct imx_drm_device *imxdrm = drm->dev_private;
+
+       flush_workqueue(imxdrm->wq);
 
        drm_dev_unregister(drm);
 
@@ -292,6 +317,8 @@ static void imx_drm_unbind(struct device *dev)
        component_unbind_all(drm->dev, drm);
        dev_set_drvdata(dev, NULL);
 
+       destroy_workqueue(imxdrm->wq);
+
        drm_dev_put(drm);
 }
 
index 16ab561..9f1f002 100644 (file)
@@ -19,6 +19,12 @@ struct imx_drm_device {
        unsigned int                            pipes;
        struct drm_fbdev_cma                    *fbhelper;
        struct drm_atomic_state                 *state;
+
+       struct workqueue_struct *wq;
+       struct {
+               wait_queue_head_t wait;
+               bool pending;
+       } commit;
 };
 
 struct imx_crtc_state {