diff mbox series

[v2,04/12] software_node: Enforce parent before child ordering of nodes arrays

Message ID 20201217234337.1983732-5-djrscally@gmail.com
State New
Headers show
Series Add functionality to ipu3-cio2 driver allowing software_node connections to sensors on platforms designed for Windows | expand

Commit Message

Daniel Scally Dec. 17, 2020, 11:43 p.m. UTC
Registering software_nodes with the .parent member set to point to a
currently unregistered software_node has the potential for problems,
so enforce parent -> child ordering in arrays passed in to
software_node_register_nodes().

Software nodes that are children of another software node should be
unregistered before their parent. To allow easy unregistering of an array
of software_nodes ordered parent to child, reverse the order in which
software_node_unregister_nodes() unregisters software_nodes.

Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Daniel Scally <djrscally@gmail.com>
---
Changes in v2:

	- Squashed the patches that originally touched these separately
	- Updated documentation

 drivers/base/swnode.c | 43 ++++++++++++++++++++++++++++++-------------
 1 file changed, 30 insertions(+), 13 deletions(-)

Comments

Laurent Pinchart Dec. 18, 2020, 4:02 p.m. UTC | #1
Hi Daniel,

Thank you for the patch.

On Thu, Dec 17, 2020 at 11:43:29PM +0000, Daniel Scally wrote:
> Registering software_nodes with the .parent member set to point to a
> currently unregistered software_node has the potential for problems,
> so enforce parent -> child ordering in arrays passed in to
> software_node_register_nodes().
> 
> Software nodes that are children of another software node should be
> unregistered before their parent. To allow easy unregistering of an array
> of software_nodes ordered parent to child, reverse the order in which
> software_node_unregister_nodes() unregisters software_nodes.
> 
> Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Signed-off-by: Daniel Scally <djrscally@gmail.com>
> ---
> Changes in v2:
> 
> 	- Squashed the patches that originally touched these separately
> 	- Updated documentation
> 
>  drivers/base/swnode.c | 43 ++++++++++++++++++++++++++++++-------------
>  1 file changed, 30 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
> index 615a0c93e116..cfd1faea48a7 100644
> --- a/drivers/base/swnode.c
> +++ b/drivers/base/swnode.c
> @@ -692,7 +692,10 @@ swnode_register(const struct software_node *node, struct swnode *parent,
>   * software_node_register_nodes - Register an array of software nodes
>   * @nodes: Zero terminated array of software nodes to be registered
>   *
> - * Register multiple software nodes at once.
> + * Register multiple software nodes at once. If any node in the array
> + * has it's .parent pointer set, then it's 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_nodes(const struct software_node *nodes)
>  {
> @@ -700,33 +703,47 @@ int software_node_register_nodes(const struct software_node *nodes)
>  	int i;
>  
>  	for (i = 0; nodes[i].name; i++) {
> -		ret = software_node_register(&nodes[i]);
> -		if (ret) {
> -			software_node_unregister_nodes(nodes);
> -			return ret;
> +		const struct software_node *parent = nodes[i].parent;
> +
> +		if (parent && !software_node_to_swnode(parent)) {
> +			ret = -EINVAL;
> +			goto err_unregister_nodes;
>  		}
> +
> +		ret = software_node_register(&nodes[i]);
> +		if (ret)
> +			goto err_unregister_nodes;
>  	}
>  
>  	return 0;
> +
> +err_unregister_nodes:
> +	software_node_unregister_nodes(nodes);
> +	return ret;
>  }
>  EXPORT_SYMBOL_GPL(software_node_register_nodes);
>  
>  /**
>   * software_node_unregister_nodes - Unregister an array of software nodes
> - * @nodes: Zero terminated array of software nodes to be unregistered
> + * @nodes: Zero terminated array of software nodes to be unregistered.

Not sure if this is needed.

>   *
> - * Unregister multiple software nodes at once.
> + * 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

I'd either replace **must** above with MUST, or use **must** here. I'm
not sure if kerneldoc handles emphasis with **must**, if it does that
seems a bit nicer to me, but it's really up to you.

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> + * parents come before their children.
>   *
> - * NOTE: Be careful using this call if the nodes had parent pointers set up in
> - * them before registering.  If so, it is wiser to remove the nodes
> - * individually, in the correct order (child before parent) instead of relying
> - * on the sequential order of the list of nodes in the array.
> + * 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_nodes(const struct software_node *nodes)
>  {
> -	int i;
> +	unsigned int i = 0;
> +
> +	while (nodes[i].name)
> +		i++;
>  
> -	for (i = 0; nodes[i].name; i++)
> +	while (i--)
>  		software_node_unregister(&nodes[i]);
>  }
>  EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
Andy Shevchenko Dec. 18, 2020, 8:29 p.m. UTC | #2
On Thu, Dec 17, 2020 at 11:43:29PM +0000, Daniel Scally wrote:
> Registering software_nodes with the .parent member set to point to a
> currently unregistered software_node has the potential for problems,
> so enforce parent -> child ordering in arrays passed in to
> software_node_register_nodes().
> 
> Software nodes that are children of another software node should be
> unregistered before their parent. To allow easy unregistering of an array
> of software_nodes ordered parent to child, reverse the order in which
> software_node_unregister_nodes() unregisters software_nodes.

...

> + * Register multiple software nodes at once. If any node in the array
> + * has it's .parent pointer set, then it's parent **must** have been

it's => its in both cases?


> + * registered before it is; either outside of this function or by
> + * ordering the array such that parent comes before child.
>   */

...

> +		const struct software_node *parent = nodes[i].parent;
> +
> +		if (parent && !software_node_to_swnode(parent)) {

Can we have parent of swnode in an array not being an swnode?
Either comment that parent for swnode can be swnode only (Heikki, was it an
idea?) or check if parent is of swnode type and only that apply this
requirement.

> +			ret = -EINVAL;
> +			goto err_unregister_nodes;
>  		}

...

> + * 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.

Shouldn't be consistent with above, i.e. **must** ?
Daniel Scally Dec. 18, 2020, 10:19 p.m. UTC | #3
Hi Laurent

On 18/12/2020 16:02, Laurent Pinchart wrote:
> Hi Daniel,
> 
> Thank you for the patch.
> 
> On Thu, Dec 17, 2020 at 11:43:29PM +0000, Daniel Scally wrote:
>> Registering software_nodes with the .parent member set to point to a
>> currently unregistered software_node has the potential for problems,
>> so enforce parent -> child ordering in arrays passed in to
>> software_node_register_nodes().
>>
>> Software nodes that are children of another software node should be
>> unregistered before their parent. To allow easy unregistering of an array
>> of software_nodes ordered parent to child, reverse the order in which
>> software_node_unregister_nodes() unregisters software_nodes.
>>
>> Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
>> Signed-off-by: Daniel Scally <djrscally@gmail.com>
>> ---
>> Changes in v2:
>>
>> 	- Squashed the patches that originally touched these separately
>> 	- Updated documentation
>>
>>  drivers/base/swnode.c | 43 ++++++++++++++++++++++++++++++-------------
>>  1 file changed, 30 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
>> index 615a0c93e116..cfd1faea48a7 100644
>> --- a/drivers/base/swnode.c
>> +++ b/drivers/base/swnode.c
>> @@ -692,7 +692,10 @@ swnode_register(const struct software_node *node, struct swnode *parent,
>>   * software_node_register_nodes - Register an array of software nodes
>>   * @nodes: Zero terminated array of software nodes to be registered
>>   *
>> - * Register multiple software nodes at once.
>> + * Register multiple software nodes at once. If any node in the array
>> + * has it's .parent pointer set, then it's 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_nodes(const struct software_node *nodes)
>>  {
>> @@ -700,33 +703,47 @@ int software_node_register_nodes(const struct software_node *nodes)
>>  	int i;
>>  
>>  	for (i = 0; nodes[i].name; i++) {
>> -		ret = software_node_register(&nodes[i]);
>> -		if (ret) {
>> -			software_node_unregister_nodes(nodes);
>> -			return ret;
>> +		const struct software_node *parent = nodes[i].parent;
>> +
>> +		if (parent && !software_node_to_swnode(parent)) {
>> +			ret = -EINVAL;
>> +			goto err_unregister_nodes;
>>  		}
>> +
>> +		ret = software_node_register(&nodes[i]);
>> +		if (ret)
>> +			goto err_unregister_nodes;
>>  	}
>>  
>>  	return 0;
>> +
>> +err_unregister_nodes:
>> +	software_node_unregister_nodes(nodes);
>> +	return ret;
>>  }
>>  EXPORT_SYMBOL_GPL(software_node_register_nodes);
>>  
>>  /**
>>   * software_node_unregister_nodes - Unregister an array of software nodes
>> - * @nodes: Zero terminated array of software nodes to be unregistered
>> + * @nodes: Zero terminated array of software nodes to be unregistered.
> 
> Not sure if this is needed.

Hah, of course. Hangover from the last version (when I had made that
line two sentences)
> 
>>   *
>> - * Unregister multiple software nodes at once.
>> + * 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
> 
> I'd either replace **must** above with MUST, or use **must** here. I'm
> not sure if kerneldoc handles emphasis with **must**, if it does that
> seems a bit nicer to me, but it's really up to you.

Honestly I haven't delved into kerneldoc yet, but either way I think
**must** is better in both places - will change.

> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Thank you!
> 
>> + * parents come before their children.
>>   *
>> - * NOTE: Be careful using this call if the nodes had parent pointers set up in
>> - * them before registering.  If so, it is wiser to remove the nodes
>> - * individually, in the correct order (child before parent) instead of relying
>> - * on the sequential order of the list of nodes in the array.
>> + * 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_nodes(const struct software_node *nodes)
>>  {
>> -	int i;
>> +	unsigned int i = 0;
>> +
>> +	while (nodes[i].name)
>> +		i++;
>>  
>> -	for (i = 0; nodes[i].name; i++)
>> +	while (i--)
>>  		software_node_unregister(&nodes[i]);
>>  }
>>  EXPORT_SYMBOL_GPL(software_node_unregister_nodes);
>
diff mbox series

Patch

diff --git a/drivers/base/swnode.c b/drivers/base/swnode.c
index 615a0c93e116..cfd1faea48a7 100644
--- a/drivers/base/swnode.c
+++ b/drivers/base/swnode.c
@@ -692,7 +692,10 @@  swnode_register(const struct software_node *node, struct swnode *parent,
  * software_node_register_nodes - Register an array of software nodes
  * @nodes: Zero terminated array of software nodes to be registered
  *
- * Register multiple software nodes at once.
+ * Register multiple software nodes at once. If any node in the array
+ * has it's .parent pointer set, then it's 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_nodes(const struct software_node *nodes)
 {
@@ -700,33 +703,47 @@  int software_node_register_nodes(const struct software_node *nodes)
 	int i;
 
 	for (i = 0; nodes[i].name; i++) {
-		ret = software_node_register(&nodes[i]);
-		if (ret) {
-			software_node_unregister_nodes(nodes);
-			return ret;
+		const struct software_node *parent = nodes[i].parent;
+
+		if (parent && !software_node_to_swnode(parent)) {
+			ret = -EINVAL;
+			goto err_unregister_nodes;
 		}
+
+		ret = software_node_register(&nodes[i]);
+		if (ret)
+			goto err_unregister_nodes;
 	}
 
 	return 0;
+
+err_unregister_nodes:
+	software_node_unregister_nodes(nodes);
+	return ret;
 }
 EXPORT_SYMBOL_GPL(software_node_register_nodes);
 
 /**
  * software_node_unregister_nodes - Unregister an array of software nodes
- * @nodes: Zero terminated array of software nodes to be unregistered
+ * @nodes: Zero terminated array of software nodes to be unregistered.
  *
- * Unregister multiple software nodes at once.
+ * 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: Be careful using this call if the nodes had parent pointers set up in
- * them before registering.  If so, it is wiser to remove the nodes
- * individually, in the correct order (child before parent) instead of relying
- * on the sequential order of the list of nodes in the array.
+ * 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_nodes(const struct software_node *nodes)
 {
-	int i;
+	unsigned int i = 0;
+
+	while (nodes[i].name)
+		i++;
 
-	for (i = 0; nodes[i].name; i++)
+	while (i--)
 		software_node_unregister(&nodes[i]);
 }
 EXPORT_SYMBOL_GPL(software_node_unregister_nodes);