[media] media: ti-vpe: vpdma: Fix race condition for firmware loading
authorNikhil Devshatwar <nikhil.nd@ti.com>
Fri, 18 Nov 2016 23:20:35 +0000 (21:20 -0200)
committerMauro Carvalho Chehab <mchehab@s-opensource.com>
Tue, 22 Nov 2016 10:07:28 +0000 (08:07 -0200)
vpdma_create API is supposed to allocated the struct vpdma_data and
return it to the driver. Also, it would call the callback function
when the VPDMA firmware is loaded.

Typically, VPE driver have following function call:
    dev->vpdma = vpdma_create(pdev, firmware_load_callback);
And the callback implementation would continue the probe further.
Also, the dev->vpdma is accessed from the callback implementation.

This may lead to race condition between assignment of dev->vpdma
and the callback function being triggered.
This would lead to kernel crash because of NULL pointer access.

Fix this by passing a driver wrapped &vpdma_data instead of allocating
inside vpdma_create.
Change the vpdma_create prototype accordingly and fix return paths.

Also, update the VPE driver to use the updated API and
initialize the dev->vpdma before hand so that the race condition
is avoided.

Signed-off-by: Nikhil Devshatwar <nikhil.nd@ti.com>
Signed-off-by: Benoit Parrot <bparrot@ti.com>
Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
drivers/media/platform/ti-vpe/vpdma.c
drivers/media/platform/ti-vpe/vpdma.h
drivers/media/platform/ti-vpe/vpe.c

index 8f0d608c70f6c063de5ed2ad30fb076b4ee8261d..070937fe1af6c7e7524ea60b53497d4c55254543 100644 (file)
@@ -1130,21 +1130,14 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma)
        return 0;
 }
 
-struct vpdma_data *vpdma_create(struct platform_device *pdev,
+int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma,
                void (*cb)(struct platform_device *pdev))
 {
        struct resource *res;
-       struct vpdma_data *vpdma;
        int r;
 
        dev_dbg(&pdev->dev, "vpdma_create\n");
 
-       vpdma = devm_kzalloc(&pdev->dev, sizeof(*vpdma), GFP_KERNEL);
-       if (!vpdma) {
-               dev_err(&pdev->dev, "couldn't alloc vpdma_dev\n");
-               return ERR_PTR(-ENOMEM);
-       }
-
        vpdma->pdev = pdev;
        vpdma->cb = cb;
        spin_lock_init(&vpdma->lock);
@@ -1152,22 +1145,22 @@ struct vpdma_data *vpdma_create(struct platform_device *pdev,
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
        if (res == NULL) {
                dev_err(&pdev->dev, "missing platform resources data\n");
-               return ERR_PTR(-ENODEV);
+               return -ENODEV;
        }
 
        vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
        if (!vpdma->base) {
                dev_err(&pdev->dev, "failed to ioremap\n");
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
        }
 
        r = vpdma_load_firmware(vpdma);
        if (r) {
                pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE);
-               return ERR_PTR(r);
+               return r;
        }
 
-       return vpdma;
+       return 0;
 }
 EXPORT_SYMBOL(vpdma_create);
 
index 405a6febc254f24ab68dd43e2fd77f937173ea0e..0df156b7c1cf9c287dc2644422676e6d7161b0a7 100644 (file)
@@ -273,7 +273,7 @@ void vpdma_set_bg_color(struct vpdma_data *vpdma,
 void vpdma_dump_regs(struct vpdma_data *vpdma);
 
 /* initialize vpdma, passed with VPE's platform device pointer */
-struct vpdma_data *vpdma_create(struct platform_device *pdev,
+int vpdma_create(struct platform_device *pdev, struct vpdma_data *vpdma,
                void (*cb)(struct platform_device *pdev));
 
 #endif
index f92ad7a473c1ca6cc1873b99d40b7e5099dbeca3..15e846b9571941c91f5a57a8ea0b4f97875c8356 100644 (file)
@@ -383,6 +383,7 @@ struct vpe_dev {
        void __iomem            *base;
        struct resource         *res;
 
+       struct vpdma_data       vpdma_data;
        struct vpdma_data       *vpdma;         /* vpdma data handle */
        struct sc_data          *sc;            /* scaler data handle */
        struct csc_data         *csc;           /* csc data handle */
@@ -2462,11 +2463,10 @@ static int vpe_probe(struct platform_device *pdev)
                goto runtime_put;
        }
 
-       dev->vpdma = vpdma_create(pdev, vpe_fw_cb);
-       if (IS_ERR(dev->vpdma)) {
-               ret = PTR_ERR(dev->vpdma);
+       dev->vpdma = &dev->vpdma_data;
+       ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb);
+       if (ret)
                goto runtime_put;
-       }
 
        return 0;