From: Nikhil Devshatwar Date: Fri, 18 Nov 2016 23:20:35 +0000 (-0200) Subject: [media] media: ti-vpe: vpdma: Fix race condition for firmware loading X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=c786595beb89d74ff709b2ee382f34a1e0040d88;p=linux-beck.git [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 Signed-off-by: Benoit Parrot Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- diff --git a/drivers/media/platform/ti-vpe/vpdma.c b/drivers/media/platform/ti-vpe/vpdma.c index 8f0d608c70f6..070937fe1af6 100644 --- a/drivers/media/platform/ti-vpe/vpdma.c +++ b/drivers/media/platform/ti-vpe/vpdma.c @@ -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); diff --git a/drivers/media/platform/ti-vpe/vpdma.h b/drivers/media/platform/ti-vpe/vpdma.h index 405a6febc254..0df156b7c1cf 100644 --- a/drivers/media/platform/ti-vpe/vpdma.h +++ b/drivers/media/platform/ti-vpe/vpdma.h @@ -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 diff --git a/drivers/media/platform/ti-vpe/vpe.c b/drivers/media/platform/ti-vpe/vpe.c index f92ad7a473c1..15e846b95719 100644 --- a/drivers/media/platform/ti-vpe/vpe.c +++ b/drivers/media/platform/ti-vpe/vpe.c @@ -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;