3.14-stable review patch.  If anyone has any objections, please let me know.

------------------

From: Ezequiel Garcia <ezequ...@vanguardiasur.com.ar>

commit b478e336b3e75505707a11e78ef8b964ef0a03af upstream.

The current error path calls tilcdc_unload() in case of an error to release
the resources. However, this is wrong because not all resources have been
allocated by the time an error occurs in tilcdc_load().

To fix it, this commit adds proper labels to bail out at the different
stages in the load function, and release only the resources actually allocated.

Tested-by: Darren Etheridge <detheri...@ti.com>
Tested-by: Johannes Pointner <johannes.point...@br-automation.com>
Signed-off-by: Ezequiel Garcia <ezequ...@vanguardiasur.com.ar>
Signed-off-by: Dave Airlie <airl...@redhat.com>
Fixes: 3a49012224ca ("drm/tilcdc: panel: fix leak when unloading the module")
Signed-off-by: Matwey V. Kornilov <matwey.korni...@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gre...@linuxfoundation.org>

---
 drivers/gpu/drm/tilcdc/tilcdc_drv.c |   60 ++++++++++++++++++++++++++++++------
 1 file changed, 50 insertions(+), 10 deletions(-)

--- a/drivers/gpu/drm/tilcdc/tilcdc_drv.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_drv.c
@@ -84,6 +84,7 @@ static int modeset_init(struct drm_devic
        if ((priv->num_encoders == 0) || (priv->num_connectors == 0)) {
                /* oh nos! */
                dev_err(dev->dev, "no encoders/connectors found\n");
+               drm_mode_config_cleanup(dev);
                return -ENXIO;
        }
 
@@ -178,33 +179,37 @@ static int tilcdc_load(struct drm_device
        dev->dev_private = priv;
 
        priv->wq = alloc_ordered_workqueue("tilcdc", 0);
+       if (!priv->wq) {
+               ret = -ENOMEM;
+               goto fail_free_priv;
+       }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (!res) {
                dev_err(dev->dev, "failed to get memory resource\n");
                ret = -EINVAL;
-               goto fail;
+               goto fail_free_wq;
        }
 
        priv->mmio = ioremap_nocache(res->start, resource_size(res));
        if (!priv->mmio) {
                dev_err(dev->dev, "failed to ioremap\n");
                ret = -ENOMEM;
-               goto fail;
+               goto fail_free_wq;
        }
 
        priv->clk = clk_get(dev->dev, "fck");
        if (IS_ERR(priv->clk)) {
                dev_err(dev->dev, "failed to get functional clock\n");
                ret = -ENODEV;
-               goto fail;
+               goto fail_iounmap;
        }
 
        priv->disp_clk = clk_get(dev->dev, "dpll_disp_ck");
        if (IS_ERR(priv->clk)) {
                dev_err(dev->dev, "failed to get display clock\n");
                ret = -ENODEV;
-               goto fail;
+               goto fail_put_clk;
        }
 
 #ifdef CONFIG_CPU_FREQ
@@ -214,7 +219,7 @@ static int tilcdc_load(struct drm_device
                        CPUFREQ_TRANSITION_NOTIFIER);
        if (ret) {
                dev_err(dev->dev, "failed to register cpufreq notifier\n");
-               goto fail;
+               goto fail_put_disp_clk;
        }
 #endif
 
@@ -259,13 +264,13 @@ static int tilcdc_load(struct drm_device
        ret = modeset_init(dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to initialize mode setting\n");
-               goto fail;
+               goto fail_cpufreq_unregister;
        }
 
        ret = drm_vblank_init(dev, 1);
        if (ret < 0) {
                dev_err(dev->dev, "failed to initialize vblank\n");
-               goto fail;
+               goto fail_mode_config_cleanup;
        }
 
        pm_runtime_get_sync(dev->dev);
@@ -273,7 +278,7 @@ static int tilcdc_load(struct drm_device
        pm_runtime_put_sync(dev->dev);
        if (ret < 0) {
                dev_err(dev->dev, "failed to install IRQ handler\n");
-               goto fail;
+               goto fail_vblank_cleanup;
        }
 
        platform_set_drvdata(pdev, dev);
@@ -289,13 +294,48 @@ static int tilcdc_load(struct drm_device
        priv->fbdev = drm_fbdev_cma_init(dev, bpp,
                        dev->mode_config.num_crtc,
                        dev->mode_config.num_connector);
+       if (IS_ERR(priv->fbdev)) {
+               ret = PTR_ERR(priv->fbdev);
+               goto fail_irq_uninstall;
+       }
 
        drm_kms_helper_poll_init(dev);
 
        return 0;
 
-fail:
-       tilcdc_unload(dev);
+fail_irq_uninstall:
+       pm_runtime_get_sync(dev->dev);
+       drm_irq_uninstall(dev);
+       pm_runtime_put_sync(dev->dev);
+
+fail_vblank_cleanup:
+       drm_vblank_cleanup(dev);
+
+fail_mode_config_cleanup:
+       drm_mode_config_cleanup(dev);
+
+fail_cpufreq_unregister:
+       pm_runtime_disable(dev->dev);
+#ifdef CONFIG_CPU_FREQ
+       cpufreq_unregister_notifier(&priv->freq_transition,
+                       CPUFREQ_TRANSITION_NOTIFIER);
+fail_put_disp_clk:
+       clk_put(priv->disp_clk);
+#endif
+
+fail_put_clk:
+       clk_put(priv->clk);
+
+fail_iounmap:
+       iounmap(priv->mmio);
+
+fail_free_wq:
+       flush_workqueue(priv->wq);
+       destroy_workqueue(priv->wq);
+
+fail_free_priv:
+       dev->dev_private = NULL;
+       kfree(priv);
        return ret;
 }
 


--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to