[media] media: ti-vpe: vpdma: Fix race condition for firmware loading

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>
This commit is contained in:
Nikhil Devshatwar 2016-11-18 21:20:35 -02:00 committed by Mauro Carvalho Chehab
parent dfe1349dc8
commit c786595beb
3 changed files with 10 additions and 17 deletions

View File

@ -1130,21 +1130,14 @@ static int vpdma_load_firmware(struct vpdma_data *vpdma)
return 0; 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)) void (*cb)(struct platform_device *pdev))
{ {
struct resource *res; struct resource *res;
struct vpdma_data *vpdma;
int r; int r;
dev_dbg(&pdev->dev, "vpdma_create\n"); 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->pdev = pdev;
vpdma->cb = cb; vpdma->cb = cb;
spin_lock_init(&vpdma->lock); 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"); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "vpdma");
if (res == NULL) { if (res == NULL) {
dev_err(&pdev->dev, "missing platform resources data\n"); 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)); vpdma->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
if (!vpdma->base) { if (!vpdma->base) {
dev_err(&pdev->dev, "failed to ioremap\n"); dev_err(&pdev->dev, "failed to ioremap\n");
return ERR_PTR(-ENOMEM); return -ENOMEM;
} }
r = vpdma_load_firmware(vpdma); r = vpdma_load_firmware(vpdma);
if (r) { if (r) {
pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE); pr_err("failed to load firmware %s\n", VPDMA_FIRMWARE);
return ERR_PTR(r); return r;
} }
return vpdma; return 0;
} }
EXPORT_SYMBOL(vpdma_create); EXPORT_SYMBOL(vpdma_create);

View File

@ -273,7 +273,7 @@ void vpdma_set_bg_color(struct vpdma_data *vpdma,
void vpdma_dump_regs(struct vpdma_data *vpdma); void vpdma_dump_regs(struct vpdma_data *vpdma);
/* initialize vpdma, passed with VPE's platform device pointer */ /* 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)); void (*cb)(struct platform_device *pdev));
#endif #endif

View File

@ -383,6 +383,7 @@ struct vpe_dev {
void __iomem *base; void __iomem *base;
struct resource *res; struct resource *res;
struct vpdma_data vpdma_data;
struct vpdma_data *vpdma; /* vpdma data handle */ struct vpdma_data *vpdma; /* vpdma data handle */
struct sc_data *sc; /* scaler data handle */ struct sc_data *sc; /* scaler data handle */
struct csc_data *csc; /* csc data handle */ struct csc_data *csc; /* csc data handle */
@ -2462,11 +2463,10 @@ static int vpe_probe(struct platform_device *pdev)
goto runtime_put; goto runtime_put;
} }
dev->vpdma = vpdma_create(pdev, vpe_fw_cb); dev->vpdma = &dev->vpdma_data;
if (IS_ERR(dev->vpdma)) { ret = vpdma_create(pdev, dev->vpdma, vpe_fw_cb);
ret = PTR_ERR(dev->vpdma); if (ret)
goto runtime_put; goto runtime_put;
}
return 0; return 0;