If we pass a software node to a newly created device using struct
platform_device_info, it will not be removed when the device is
released. This may happen when a module creating the device is removed
or on failure in platform_device_add().

When we try to reuse that software node in a subsequent call to
platform_device_register_full(), it will fails with -EBUSY. Add the
missing call to device_remove_software_node() in release path.

In addition to the above change, make sure that we still function
correctly if a software node is used as the primary firmware node as
well as disallow using two software nodes for platform devices as
device_add_software_node() does not handle this case correctly (in fact
a comment inside it states that only one software node per device is
allowed but it will not bail out if two are used so we need to handle it
here).

Fixes: 0fc434bc2c45 ("driver core: platform: allow attaching software nodes 
when creating devices")
Reviewed-by: Andy Shevchenko <[email protected]>
Signed-off-by: Bartosz Golaszewski <[email protected]>
---
 drivers/base/platform.c | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 
75b4698d0e582e67adafa78c312d75c72fd654cf..22834be0a3aeabf538f57ec6815ab3cd6851c16b
 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -599,6 +599,7 @@ static void platform_device_release(struct device *dev)
        struct platform_object *pa = container_of(dev, struct platform_object,
                                                  pdev.dev);
 
+       device_remove_software_node(dev);
        of_node_put(pa->pdev.dev.of_node);
        kfree(pa->pdev.dev.platform_data);
        kfree(pa->pdev.mfd_cell);
@@ -848,7 +849,13 @@ struct platform_device 
*platform_device_register_full(const struct platform_devi
        int ret;
        struct platform_device *pdev;
 
-       if (pdevinfo->swnode && pdevinfo->properties)
+       /*
+        * Only one software node per device is allowed. Make sure we don't
+        * accept or create two.
+        */
+       if ((pdevinfo->swnode && pdevinfo->properties) ||
+           (pdevinfo->swnode && is_software_node(pdevinfo->fwnode)) ||
+           (pdevinfo->properties && is_software_node(pdevinfo->fwnode)))
                return ERR_PTR(-EINVAL);
 
        pdev = platform_device_alloc(pdevinfo->name, pdevinfo->id);
@@ -866,6 +873,16 @@ struct platform_device 
*platform_device_register_full(const struct platform_devi
                pdev->dev.coherent_dma_mask = pdevinfo->dma_mask;
        }
 
+       /*
+        * If the primary firmware node is a software node and there's no
+        * secondary firmware node, the primary will be affected by the call
+        * to device_remove_software_node() in platform_device_release() and
+        * its reference count will be dropped by one. Take another reference
+        * here to make it have no effect.
+        */
+       if (is_software_node(pdevinfo->fwnode))
+               fwnode_handle_get(pdevinfo->fwnode);
+
        ret = platform_device_add_resources(pdev, pdevinfo->res, 
pdevinfo->num_res);
        if (ret)
                goto err;

-- 
2.47.3


Reply via email to