drm/tilcdc: Fix the error path in tilcdc_load()
authorEzequiel Garcia <ezequiel@vanguardiasur.com.ar>
Tue, 2 Sep 2014 12:51:15 +0000 (09:51 -0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Nov 2014 16:47:56 +0000 (08:47 -0800)
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 <detheridge@ti.com>
Tested-by: Johannes Pointner <johannes.pointner@br-automation.com>
Signed-off-by: Ezequiel Garcia <ezequiel@vanguardiasur.com.ar>
Signed-off-by: Dave Airlie <airlied@redhat.com>
Fixes: 3a49012224ca ("drm/tilcdc: panel: fix leak when unloading the module")
Signed-off-by: Matwey V. Kornilov <matwey.kornilov@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/gpu/drm/tilcdc/tilcdc_drv.c

index f060b7487c346241934c7a992b882fa37e2a0c0e..f5ddd35507965598818af3a28a2d7899cec877e0 100644 (file)
@@ -78,6 +78,7 @@ static int modeset_init(struct drm_device *dev)
        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;
        }
 
@@ -170,33 +171,37 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        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
@@ -206,7 +211,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
                        CPUFREQ_TRANSITION_NOTIFIER);
        if (ret) {
                dev_err(dev->dev, "failed to register cpufreq notifier\n");
-               goto fail;
+               goto fail_put_disp_clk;
        }
 #endif
 
@@ -238,13 +243,13 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        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);
@@ -252,7 +257,7 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        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);
@@ -260,13 +265,48 @@ static int tilcdc_load(struct drm_device *dev, unsigned long flags)
        priv->fbdev = drm_fbdev_cma_init(dev, 16,
                        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;
 }