Re: [PATCH 1/2] of: platform: introduce platform data length for auxdata

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Thu, Dec 10, 2020 at 7:42 AM Peter Chen <peter.chen@xxxxxxxxxx> wrote:
>
> From: Peter Chen <peter.chen@xxxxxxx>
>
> When a platform device is released, it frees the device platform_data
> memory region using kfree, if the memory is not allocated by kmalloc,
> it may run into trouble. See the below comments from kfree API.
>
>          * Don't free memory not originally allocated by kmalloc()
>          * or you will run into trouble.
>
> For the device which is created dynamically using of_platform_populate,
> if the platform_data is existed at of_dev_auxdata structure, the OF code
> simply assigns the platform_data pointer to newly created device, but
> not using platform_device_add_data to allocate one. For most of platform
> data region at device driver, which may not be allocated by kmalloc, they
> are at global data region or at stack region at some situations.

auxdata is a "temporary" thing for transitioning to DT which I want to
remove. So I don't really want to see it expanded nor new users. We've
got about a dozen arm32 platforms and 5 cases under drivers/.

> It fixed below oops during module unload process at imx8qxp mek platform
> for Cadence USB3 driver.
>
> log1:
> [  333.501593] Unable to handle kernel paging request at virtual address 000000000004ae48
> [  333.509558] Mem abort info:
> [  333.512369]   ESR = 0x96000006
> [  333.515476]   EC = 0x25: DABT (current EL), IL = 32 bits
> [  333.520847]   SET = 0, FnV = 0
> [  333.523986]   EA = 0, S1PTW = 0
> [  333.527193] Data abort info:
> [  333.530124]   ISV = 0, ISS = 0x00000006
> [  333.534004]   CM = 0, WnR = 0
> [  333.536988] user pgtable: 4k pages, 48-bit VAs, pgdp=0000000885d73000
> [  333.543497] [000000000004ae48] pgd=0000000000000000, p4d=0000000000000000
> [  333.550354] Internal error: Oops: 96000006 [#1] PREEMPT SMP
> [  333.555936] Modules linked in: fsl_jr_uio caam_jr caamkeyblob_desc caamhash_desc caamalg_desc crypto_engine rng_core authenc libdes crct10dif_
> ce mxc_jpeg_encdec phy_mxs_usb phy_cadence_salvo imx8_media_dev(C) caam error [last unloaded: cdns3]
> [  333.577484] CPU: 3 PID: 168 Comm: kworker/3:38 Tainted: G         C        5.10.0-rc4-04454-gd479340c061 #30
> [  333.587313] Hardware name: Freescale i.MX8QXP MEK (DT)
> [  333.592471] Workqueue: rcu_gp srcu_invoke_callbacks
> [  333.597359] pstate: a0000005 (NzCv daif -PAN -UAO -TCO BTYPE=--)
> [  333.603373] pc : kfree+0x78/0x3a0
> [  333.606690] lr : platform_device_release+0x28/0x58
> [  333.611481] sp : ffff8000135bbc20
> [  333.614800] x29: ffff8000135bbc20 x28: ffff800012146000
> [  333.620125] x27: ffff00083fa06470 x26: ffff800010108cb0
> [  333.625442] x25: 0080000000000000 x24: ffff80001240f000
> [  333.630767] x23: ffff0008012ef000 x22: ffff800010875478
> [  333.636093] x21: ffff8000092b90c8 x20: ffff000804bf1b00
> [  333.641418] x19: 000000000004ae40 x18: 0000000000000000
> [  333.646735] x17: 0000000000000000 x16: 0000000000000000
> [  333.652052] x15: 0000000000000000 x14: 0000000000000000
> [  333.657368] x13: 0000000000000002 x12: 0000000000000000
> [  333.662685] x11: 0000000000000040 x10: 0000000000000a00
> [  333.668000] x9 : ffff800010875478 x8 : fefefefefefefeff
> [  333.673319] x7 : 0000000000000018 x6 : ffff00080101006c
> [  333.678644] x5 : ffff00083fa5c300 x4 : 0000000000000000
> [  333.683969] x3 : 0000000000000002 x2 : 0000000000000000
> [  333.689286] x1 : 0000000000000030 x0 : fffffdffffe00000
> [  333.694605] Call trace:
> [  333.697059]  kfree+0x78/0x3a0
> [  333.700027]  platform_device_release+0x28/0x58
> [  333.704476]  device_release+0x38/0x90
> [  333.708144]  kobject_put+0x78/0x208
> [  333.711633]  __device_link_free_srcu+0x50/0x78
> [  333.716082]  srcu_invoke_callbacks+0xf4/0x168
> [  333.720445]  process_one_work+0x1c8/0x480
> [  333.724460]  worker_thread+0x50/0x420
> [  333.728124]  kthread+0x148/0x168
> [  333.731355]  ret_from_fork+0x10/0x18
> [  333.734940] Code: b26babe0 d34cfe73 f2dfbfe0 8b131813 (f9400660)
> [  333.741049] ---[ end trace 9a41d9fcbc0885e6 ]---
> [  333.745671] Kernel panic - not syncing: Oops: Fatal exception in interrupt
> [  333.752551] SMP: stopping secondary CPUs
> [  333.756485] Kernel Offset: disabled
> [  333.759979] CPU features: 0x0240002,20002008
> [  333.764251] Memory Limit: none
>
> log2:
> [  160.400179]  kfree+0x78/0x3a0
> [  160.403147]  platform_device_release+0x28/0x58
> [  160.407595]  device_release+0x38/0x90
> [  160.411264]  kobject_put+0x78/0x208
> [  160.414753]  klist_children_put+0x1c/0x28
> [  160.418766]  klist_next+0xac/0x128
> [  160.422172]  device_for_each_child+0x4c/0xa8
> [  160.426453]  cdns_imx_remove+0x2c/0x40 [cdns3_imx]
> [  160.431251]  platform_drv_remove+0x30/0x50
> [  160.435353]  device_release_driver_internal+0x114/0x1e8
> [  160.440580]  driver_detach+0x54/0xe0
> [  160.444163]  bus_remove_driver+0x60/0xd8
> [  160.448087]  driver_unregister+0x34/0x60
> [  160.452013]  platform_driver_unregister+0x18/0x20
> [  160.456726]  cdns_imx_driver_exit+0x14/0x478 [cdns3_imx]
> [  160.462042]  __arm64_sys_delete_module+0x180/0x258
> [  160.466837]  el0_svc_common.constprop.0+0x70/0x168
> [  160.471631]  do_el0_svc+0x28/0x88
> [  160.474950]  el0_sync_handler+0x158/0x160
> [  160.478961]  el0_sync+0x140/0x180
>
> Signed-off-by: Peter Chen <peter.chen@xxxxxxx>
> ---
>  drivers/of/platform.c       | 29 +++++++++++++++++++++--------
>  include/linux/of_platform.h |  8 ++++++--
>  2 files changed, 27 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/of/platform.c b/drivers/of/platform.c
> index b557a0fcd4ba..4afb775779f3 100644
> --- a/drivers/of/platform.c
> +++ b/drivers/of/platform.c
> @@ -157,7 +157,8 @@ EXPORT_SYMBOL(of_device_alloc);
>   * of_platform_device_create_pdata - Alloc, initialize and register an of_device
>   * @np: pointer to node to create device for
>   * @bus_id: name to assign device
> - * @platform_data: pointer to populate platform_data pointer with
> + * @platform_data: platform_data pointer from device driver
> + * @platform_data_length: the length of platform_data
>   * @parent: Linux device model parent device.
>   *
>   * Returns pointer to created platform device, or NULL if a device was not
> @@ -167,6 +168,7 @@ static struct platform_device *of_platform_device_create_pdata(
>                                         struct device_node *np,
>                                         const char *bus_id,
>                                         void *platform_data,
> +                                       int platform_data_length,
>                                         struct device *parent)
>  {
>         struct platform_device *dev;
> @@ -183,16 +185,22 @@ static struct platform_device *of_platform_device_create_pdata(
>         if (!dev->dev.dma_mask)
>                 dev->dev.dma_mask = &dev->dev.coherent_dma_mask;
>         dev->dev.bus = &platform_bus_type;
> -       dev->dev.platform_data = platform_data;
> +       if (platform_data_length) {
> +               if (platform_device_add_data(dev, platform_data, platform_data_length))
> +                       goto err_put_device;
> +       } else {
> +               dev->dev.platform_data = platform_data;
> +       }
> +
>         of_msi_configure(&dev->dev, dev->dev.of_node);
>
> -       if (of_device_add(dev) != 0) {
> -               platform_device_put(dev);
> -               goto err_clear_flag;
> -       }
> +       if (of_device_add(dev) != 0)
> +               goto err_put_device;
>
>         return dev;
>
> +err_put_device:
> +       platform_device_put(dev);
>  err_clear_flag:
>         of_node_clear_flag(np, OF_POPULATED);
>         return NULL;
> @@ -211,7 +219,7 @@ struct platform_device *of_platform_device_create(struct device_node *np,
>                                             const char *bus_id,
>                                             struct device *parent)
>  {
> -       return of_platform_device_create_pdata(np, bus_id, NULL, parent);
> +       return of_platform_device_create_pdata(np, bus_id, NULL, 0, parent);
>  }
>  EXPORT_SYMBOL(of_platform_device_create);
>
> @@ -353,6 +361,7 @@ static int of_platform_bus_create(struct device_node *bus,
>         struct platform_device *dev;
>         const char *bus_id = NULL;
>         void *platform_data = NULL;
> +       int platform_data_length = 0;
>         int rc = 0;
>
>         /* Make sure it has a compatible property */
> @@ -378,6 +387,9 @@ static int of_platform_bus_create(struct device_node *bus,
>         if (auxdata) {
>                 bus_id = auxdata->name;
>                 platform_data = auxdata->platform_data;
> +               platform_data_length = auxdata->platform_data_length;
> +               if (platform_data && !platform_data_length)
> +                       pr_warn("Make sure platform_data is allocated by kmalloc\n");

Isn't this going to warn on the majority of users as static data is the norm.

>         }
>
>         if (of_device_is_compatible(bus, "arm,primecell")) {
> @@ -389,7 +401,8 @@ static int of_platform_bus_create(struct device_node *bus,
>                 return 0;
>         }
>
> -       dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
> +       dev = of_platform_device_create_pdata(bus, bus_id, platform_data,
> +                                       platform_data_length, parent);
>         if (!dev || !of_match_node(matches, bus))
>                 return 0;
>
> diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
> index 84a966623e78..c3b02236b110 100644
> --- a/include/linux/of_platform.h
> +++ b/include/linux/of_platform.h
> @@ -18,11 +18,14 @@
>   * @phys_addr: Start address of registers to match against node
>   * @name: Name to assign for matching nodes
>   * @platform_data: platform_data to assign for matching nodes
> + * @platform_data_length: the length of platform data
>   *
>   * This lookup table allows the caller of of_platform_populate() to override
>   * the names of devices when creating devices from the device tree.  The table
> - * should be terminated with an empty entry.  It also allows the platform_data
> - * pointer to be set.
> + * should be terminated with an empty entry. The platform_data_length should be
> + * given if the platform_data is existed and is not allocated by kmalloc, it
> + * could avoid potential kfree issue when the platform_data is freed by
> + * platform_device_release.
>   *
>   * The reason for this functionality is that some Linux infrastructure uses
>   * the device name to look up a specific device, but the Linux-specific names
> @@ -39,6 +42,7 @@ struct of_dev_auxdata {
>         resource_size_t phys_addr;
>         char *name;
>         void *platform_data;
> +       int platform_data_length;
>  };
>
>  /* Macro to simplify populating a lookup table */
> --
> 2.17.1
>




[Index of Archives]     [Device Tree Compilter]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux PCI Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [XFree86]     [Yosemite Backpacking]


  Powered by Linux