diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:05:36 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2021-04-26 11:05:36 -0700 |
commit | c01c0716ccf5db2086d9693033472f37de96a699 (patch) | |
tree | 330fbb4c8a386073a25fb17ee29be15e21266ab3 /drivers/base/swnode.c | |
parent | 8e3a3249502d8ff92d73d827fb41dd44c5a16f76 (diff) | |
parent | a943d76352dbb4707a5e5537bbe696c00f5ddd36 (diff) | |
download | linux-c01c0716ccf5db2086d9693033472f37de96a699.tar.gz linux-c01c0716ccf5db2086d9693033472f37de96a699.tar.bz2 linux-c01c0716ccf5db2086d9693033472f37de96a699.zip |
Merge tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core
Pull driver core updates from Greg KH:
"Here is the "big" set of driver core changes for 5.13-rc1.
Nothing major, just lots of little core changes and cleanups, notable
things are:
- finally set 'fw_devlink=on' by default.
All reported issues with this have been shaken out over the past 9
months or so, but we will be paying attention to any fallout here
in case we need to revert this as the default boot value (symptoms
of problems are a simple lack of booting)
- fixes found to be needed by fw_devlink=on value in some subsystems
(like clock).
- delayed work initialization cleanup
- driver core cleanups and minor updates
- software node cleanups and tweaks
- devtmpfs cleanups
- minor debugfs cleanups
All of these have been in linux-next for a while with no reported
issues"
* tag 'driver-core-5.13-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (53 commits)
devm-helpers: Fix devm_delayed_work_autocancel() kerneldoc
PM / wakeup: use dev_set_name() directly
software node: Allow node addition to already existing device
kunit: software node: adhear to KUNIT formatting standard
node: fix device cleanups in error handling code
kobject_uevent: remove warning in init_uevent_argv()
debugfs: Make debugfs_allow RO after init
Revert "driver core: platform: Make platform_get_irq_optional() optional"
media: ipu3-cio2: Switch to use SOFTWARE_NODE_REFERENCE()
software node: Introduce SOFTWARE_NODE_REFERENCE() helper macro
software node: Imply kobj_to_swnode() to be no-op
software node: Deduplicate code in fwnode_create_software_node()
software node: Introduce software_node_alloc()/software_node_free()
software node: Free resources explicitly when swnode_register() fails
debugfs: drop pointless nul-termination in debugfs_read_file_bool()
driver core: add helper for deferred probe reason setting
driver core: Improve fw_devlink & deferred_probe_timeout interaction
of: property: fw_devlink: Add support for remote-endpoint
driver core: platform: Make platform_get_irq_optional() optional
driver core: Replace printf() specifier and drop unneeded casting
...
Diffstat (limited to 'drivers/base/swnode.c')
-rw-r--r-- | drivers/base/swnode.c | 106 |
1 files changed, 60 insertions, 46 deletions
diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c index fa3719ef80e4..3cc11b813f28 100644 --- a/drivers/base/swnode.c +++ b/drivers/base/swnode.c @@ -12,10 +12,10 @@ #include <linux/slab.h> struct swnode { - int id; struct kobject kobj; struct fwnode_handle fwnode; const struct software_node *node; + int id; /* hierarchy */ struct ida child_ids; @@ -720,19 +720,30 @@ software_node_find_by_name(const struct software_node *parent, const char *name) } EXPORT_SYMBOL_GPL(software_node_find_by_name); -static int -software_node_register_properties(struct software_node *node, - const struct property_entry *properties) +static struct software_node *software_node_alloc(const struct property_entry *properties) { struct property_entry *props; + struct software_node *node; props = property_entries_dup(properties); if (IS_ERR(props)) - return PTR_ERR(props); + return ERR_CAST(props); + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + property_entries_free(props); + return ERR_PTR(-ENOMEM); + } node->properties = props; - return 0; + return node; +} + +static void software_node_free(const struct software_node *node) +{ + property_entries_free(node->properties); + kfree(node); } static void software_node_release(struct kobject *kobj) @@ -746,10 +757,9 @@ static void software_node_release(struct kobject *kobj) ida_simple_remove(&swnode_root_ids, swnode->id); } - if (swnode->allocated) { - property_entries_free(swnode->node->properties); - kfree(swnode->node); - } + if (swnode->allocated) + software_node_free(swnode->node); + ida_destroy(&swnode->child_ids); kfree(swnode); } @@ -767,22 +777,19 @@ swnode_register(const struct software_node *node, struct swnode *parent, int ret; swnode = kzalloc(sizeof(*swnode), GFP_KERNEL); - if (!swnode) { - ret = -ENOMEM; - goto out_err; - } + if (!swnode) + return ERR_PTR(-ENOMEM); ret = ida_simple_get(parent ? &parent->child_ids : &swnode_root_ids, 0, 0, GFP_KERNEL); if (ret < 0) { kfree(swnode); - goto out_err; + return ERR_PTR(ret); } swnode->id = ret; swnode->node = node; swnode->parent = parent; - swnode->allocated = allocated; swnode->kobj.kset = swnode_kset; fwnode_init(&swnode->fwnode, &software_node_ops); @@ -803,16 +810,17 @@ swnode_register(const struct software_node *node, struct swnode *parent, return ERR_PTR(ret); } + /* + * Assign the flag only in the successful case, so + * the above kobject_put() won't mess up with properties. + */ + swnode->allocated = allocated; + if (parent) list_add_tail(&swnode->entry, &parent->children); kobject_uevent(&swnode->kobj, KOBJ_ADD); return &swnode->fwnode; - -out_err: - if (allocated) - property_entries_free(node->properties); - return ERR_PTR(ret); } /** @@ -880,7 +888,11 @@ EXPORT_SYMBOL_GPL(software_node_unregister_nodes); * software_node_register_node_group - Register a group of software nodes * @node_group: NULL terminated array of software node pointers to be registered * - * Register multiple software nodes at once. + * Register multiple software nodes at once. If any node in the array + * has its .parent pointer set (which can only be to another software_node), + * then its parent **must** have been registered before it is; either outside + * of this function or by ordering the array such that parent comes before + * child. */ int software_node_register_node_group(const struct software_node **node_group) { @@ -906,10 +918,14 @@ EXPORT_SYMBOL_GPL(software_node_register_node_group); * software_node_unregister_node_group - Unregister a group of software nodes * @node_group: NULL terminated array of software node pointers to be unregistered * - * Unregister multiple software nodes at once. The array will be unwound in - * reverse order (i.e. last entry first) and thus if any members of the array are - * children of another member then the children must appear later in the list such - * that they are unregistered first. + * Unregister multiple software nodes at once. If parent pointers are set up + * in any of the software nodes then the array **must** be ordered such that + * parents come before their children. + * + * NOTE: If you are uncertain whether the array is ordered such that + * parents will be unregistered before their children, it is wiser to + * remove the nodes individually, in the correct order (child before + * parent). */ void software_node_unregister_node_group( const struct software_node **node_group) @@ -963,31 +979,28 @@ struct fwnode_handle * fwnode_create_software_node(const struct property_entry *properties, const struct fwnode_handle *parent) { + struct fwnode_handle *fwnode; struct software_node *node; - struct swnode *p = NULL; - int ret; + struct swnode *p; - if (parent) { - if (IS_ERR(parent)) - return ERR_CAST(parent); - if (!is_software_node(parent)) - return ERR_PTR(-EINVAL); - p = to_swnode(parent); - } + if (IS_ERR(parent)) + return ERR_CAST(parent); - node = kzalloc(sizeof(*node), GFP_KERNEL); - if (!node) - return ERR_PTR(-ENOMEM); + p = to_swnode(parent); + if (parent && !p) + return ERR_PTR(-EINVAL); - ret = software_node_register_properties(node, properties); - if (ret) { - kfree(node); - return ERR_PTR(ret); - } + node = software_node_alloc(properties); + if (IS_ERR(node)) + return ERR_CAST(node); node->parent = p ? p->node : NULL; - return swnode_register(node, p, 1); + fwnode = swnode_register(node, p, 1); + if (IS_ERR(fwnode)) + software_node_free(node); + + return fwnode; } EXPORT_SYMBOL_GPL(fwnode_create_software_node); @@ -1032,6 +1045,7 @@ int device_add_software_node(struct device *dev, const struct software_node *nod } set_secondary_fwnode(dev, &swnode->fwnode); + software_node_notify(dev, KOBJ_ADD); return 0; } @@ -1105,8 +1119,8 @@ int software_node_notify(struct device *dev, unsigned long action) switch (action) { case KOBJ_ADD: - ret = sysfs_create_link(&dev->kobj, &swnode->kobj, - "software_node"); + ret = sysfs_create_link_nowarn(&dev->kobj, &swnode->kobj, + "software_node"); if (ret) break; |