diff mbox

ASoC: fix balance of of_node_get()/of_node_put()

Message ID 20170725214952.6491-1-borneo.antonio@gmail.com (mailing list archive)
State New, archived
Headers show

Commit Message

Antonio Borneo July 25, 2017, 9:49 p.m. UTC
On Hikey target board, enabling CONFIG_OF_DYNAMIC triggers several
errors at kernel boot, like the following one
OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0/endpoint
each followed by stack dump.

Fixed by:
- removing of_node_put() in the body of of_for_each_phandle(){},
  since already provided at each iteration. Add it in case the
  loop is break out;
- adding of_node_get() before calling of_graph_get_port_parent()
  or asoc_graph_card_dai_link_of().

Tested with kernel v4.13-rc1 with hikey_defconfig taken from
https://git.linaro.org/people/john.stultz/android-dev.git
branch dev/hikey-mainline-WIP

Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
---
To: Liam Girdwood <lgirdwood@gmail.com>
To: Mark Brown <broonie@kernel.org>
To: Jaroslav Kysela <perex@perex.cz>
To: Takashi Iwai <tiwai@suse.com>
To: alsa-devel@alsa-project.org
Cc: linux-kernel@vger.kernel.org
Cc: Wei Xu <xuwei5@hisilicon.com>
Cc: John Stultz <john.stultz@linaro.org>
Cc: linux-arm-kernel@lists.infradead.org
---
 sound/soc/generic/audio-graph-card.c  | 14 +++++++++-----
 sound/soc/generic/simple-card-utils.c |  5 +++++
 sound/soc/soc-core.c                  |  5 +++++
 3 files changed, 19 insertions(+), 5 deletions(-)

Comments

Kuninori Morimoto July 26, 2017, 1:42 a.m. UTC | #1
Hi

> On Hikey target board, enabling CONFIG_OF_DYNAMIC triggers several
> errors at kernel boot, like the following one
> OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0/endpoint
> each followed by stack dump.
> 
> Fixed by:
> - removing of_node_put() in the body of of_for_each_phandle(){},
>   since already provided at each iteration. Add it in case the
>   loop is break out;
> - adding of_node_get() before calling of_graph_get_port_parent()
>   or asoc_graph_card_dai_link_of().
> 
> Tested with kernel v4.13-rc1 with hikey_defconfig taken from
> https://git.linaro.org/people/john.stultz/android-dev.git
> branch dev/hikey-mainline-WIP
> 
> Signed-off-by: Antonio Borneo <borneo.antonio@gmail.com>
> ---

I got kernel boot error on my board (= Renesas R-Car Salvator-X) + CONFIG_OF_DYNAMIC.
This patch solved this issue.

Tested-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>

> To: Liam Girdwood <lgirdwood@gmail.com>
> To: Mark Brown <broonie@kernel.org>
> To: Jaroslav Kysela <perex@perex.cz>
> To: Takashi Iwai <tiwai@suse.com>
> To: alsa-devel@alsa-project.org
> Cc: linux-kernel@vger.kernel.org
> Cc: Wei Xu <xuwei5@hisilicon.com>
> Cc: John Stultz <john.stultz@linaro.org>
> Cc: linux-arm-kernel@lists.infradead.org
> ---
>  sound/soc/generic/audio-graph-card.c  | 14 +++++++++-----
>  sound/soc/generic/simple-card-utils.c |  5 +++++
>  sound/soc/soc-core.c                  |  5 +++++
>  3 files changed, 19 insertions(+), 5 deletions(-)
> 
> diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
> index c0f08a6..7574c5f 100644
> --- a/sound/soc/generic/audio-graph-card.c
> +++ b/sound/soc/generic/audio-graph-card.c
> @@ -228,10 +228,16 @@ static int asoc_graph_card_parse_of(struct graph_card_data *priv)
>  	 */
>  
>  	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
> +		/*
> +		 * asoc_graph_card_dai_link_of() will call
> +		 * of_node_put(). So, call of_node_get() here
> +		 */
> +		of_node_get(it.node);
>  		ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
> -		of_node_put(it.node);
> -		if (ret < 0)
> +		if (ret < 0) {
> +			of_node_put(it.node);
>  			return ret;
> +		}
>  	}
>  
>  	return asoc_simple_card_parse_card_name(card, NULL);
> @@ -244,10 +250,8 @@ static int asoc_graph_get_dais_count(struct device *dev)
>  	int count = 0;
>  	int rc;
>  
> -	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
> +	of_for_each_phandle(&it, rc, node, "dais", NULL, 0)
>  		count++;
> -		of_node_put(it.node);
> -	}
>  
>  	return count;
>  }
> diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
> index 26d64fa..144954b 100644
> --- a/sound/soc/generic/simple-card-utils.c
> +++ b/sound/soc/generic/simple-card-utils.c
> @@ -250,6 +250,11 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep)
>  	if (ret != -ENOTSUPP)
>  		return ret;
>  
> +	/*
> +	 * of_graph_get_port_parent() will call
> +	 * of_node_put(). So, call of_node_get() here
> +	 */
> +	of_node_get(ep);
>  	node = of_graph_get_port_parent(ep);
>  
>  	/*
> diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
> index 921622a..a0f39de 100644
> --- a/sound/soc/soc-core.c
> +++ b/sound/soc/soc-core.c
> @@ -4087,6 +4087,11 @@ int snd_soc_get_dai_id(struct device_node *ep)
>  	struct device_node *node;
>  	int ret;
>  
> +	/*
> +	 * of_graph_get_port_parent() will call
> +	 * of_node_put(). So, call of_node_get() here
> +	 */
> +	of_node_get(ep);
>  	node = of_graph_get_port_parent(ep);
>  
>  	/*
> -- 
> 1.9.1
> 
> _______________________________________________
> Alsa-devel mailing list
> Alsa-devel@alsa-project.org
> http://mailman.alsa-project.org/mailman/listinfo/alsa-devel
Mark Brown July 26, 2017, 11:37 a.m. UTC | #2
On Tue, Jul 25, 2017 at 11:49:52PM +0200, Antonio Borneo wrote:

> Fixed by:
> - removing of_node_put() in the body of of_for_each_phandle(){},
>   since already provided at each iteration. Add it in case the
>   loop is break out;
> - adding of_node_get() before calling of_graph_get_port_parent()
>   or asoc_graph_card_dai_link_of().

>  sound/soc/generic/audio-graph-card.c  | 14 +++++++++-----
>  sound/soc/generic/simple-card-utils.c |  5 +++++
>  sound/soc/soc-core.c                  |  5 +++++
>  3 files changed, 19 insertions(+), 5 deletions(-)

This is a series of different changes to fix different (although
related) problems which should be being submitted individually.  Sending
multiple changes in one patch makes it harder to review things and for
fixes like this makes it harder to backport the fixes where not all the
code being fixed was introduced in a single kernel version.

>  	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
> +		/*
> +		 * asoc_graph_card_dai_link_of() will call
> +		 * of_node_put(). So, call of_node_get() here
> +		 */
> +		of_node_get(it.node);
>  		ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);

Why is this the most sensible fix?  It is really not at all obvious why
asoc_graph_card_dai_link_of() would drop a reference, or in what
situations callers might have a reference they're OK with dropping.

> +	/*
> +	 * of_graph_get_port_parent() will call
> +	 * of_node_put(). So, call of_node_get() here
> +	 */
> +	of_node_get(ep);
>  	node = of_graph_get_port_parent(ep);

Same here, why does this make sense?  It is not in the least bit obvious
to me why looking up the parent of a node would cause us to drop the
reference to the current node, this seems like an error prone and
confusing API which would be better fixed.
Antonio Borneo July 27, 2017, 11:20 p.m. UTC | #3
Hi Mark,
thanks for the review.

On Wed, Jul 26, 2017 at 1:37 PM, Mark Brown <broonie@kernel.org> wrote:
> On Tue, Jul 25, 2017 at 11:49:52PM +0200, Antonio Borneo wrote:
>
>> Fixed by:
>> - removing of_node_put() in the body of of_for_each_phandle(){},
>>   since already provided at each iteration. Add it in case the
>>   loop is break out;
>> - adding of_node_get() before calling of_graph_get_port_parent()
>>   or asoc_graph_card_dai_link_of().
>
>>  sound/soc/generic/audio-graph-card.c  | 14 +++++++++-----
>>  sound/soc/generic/simple-card-utils.c |  5 +++++
>>  sound/soc/soc-core.c                  |  5 +++++
>>  3 files changed, 19 insertions(+), 5 deletions(-)
>
> This is a series of different changes to fix different (although
> related) problems which should be being submitted individually.  Sending
> multiple changes in one patch makes it harder to review things and for
> fixes like this makes it harder to backport the fixes where not all the
> code being fixed was introduced in a single kernel version.

Agree! Will split it and send a v2.

>>       of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
>> +             /*
>> +              * asoc_graph_card_dai_link_of() will call
>> +              * of_node_put(). So, call of_node_get() here
>> +              */
>> +             of_node_get(it.node);
>>               ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
>
> Why is this the most sensible fix?  It is really not at all obvious why
> asoc_graph_card_dai_link_of() would drop a reference, or in what
> situations callers might have a reference they're OK with dropping.

Actually this part is wrong. Splitting for v2 and rechecking every
step I get that it's not this function that drops the reference; the
fix is already covered by the rest of the patch. Sorry for the
mistake.

>> +     /*
>> +      * of_graph_get_port_parent() will call
>> +      * of_node_put(). So, call of_node_get() here
>> +      */
>> +     of_node_get(ep);
>>       node = of_graph_get_port_parent(ep);
>
> Same here, why does this make sense?  It is not in the least bit obvious
> to me why looking up the parent of a node would cause us to drop the
> reference to the current node, this seems like an error prone and
> confusing API which would be better fixed.

I tend to agree with you, quite error prone and confusing. I spent
some time to identify who is dropping what.
Actually there is a bunch of functions like this in driver/of/; they
take a node as parameter and return a node, while doing a put of the
parameter. While questionable, looks like there is at least some
consistency.
Maybe the "get" in the API name should match the implicit
of_node_get() of the returned node (not sure it is the case on all
such API). Something else in the name should indicate if the input
node will be of_node_put(). Suggestions are welcome, but I think the
discussion goes beyond the scope of this fix.
In this specific case, I lazily reused some code already upstream here
http://elixir.free-electrons.com/linux/v4.13-rc2/source/sound/soc/generic/simple-card-utils.c#L285
I added in copy Kuninori Morimoto, the developer of the lines above,
in case he has any hint.

Antonio
Antonio Borneo July 27, 2017, 11:26 p.m. UTC | #4
From: Antonio Borneo <borneo.antonio@gmail.com>

On Hikey target board, enabling CONFIG_OF_DYNAMIC triggers several
errors at kernel boot, like
OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0
OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0/endpoint
each followed by stack dump.

Fixed by:
- removing of_node_put() in the body of of_for_each_phandle(){},
  since already provided at each iteration. Add it in case the
  loop is break out;
- adding of_node_get() before calling of_graph_get_port_parent().

Tested with kernel v4.13-rc2 with hikey_defconfig taken from
https://git.linaro.org/people/john.stultz/android-dev.git
branch dev/hikey-mainline-WIP

v1 -> v2:
- modify subject "s/fix balance of/fix unbalanced/";
- split the patch per each individual issue. It also simplify the
  backport;
- add ref to the commit being fixed;
- drop one fix not needed.

Antonio Borneo (3):
  ASoC: fix use of of_node_put() in of_for_each_phandle() loops
  ASoC: soc-core: fix unbalanced of_node_get()/of_node_put()
  ASoC: simple-card-utils: fix unbalanced of_node_get()/of_node_put()

 sound/soc/generic/audio-graph-card.c  | 9 ++++-----
 sound/soc/generic/simple-card-utils.c | 5 +++++
 sound/soc/soc-core.c                  | 5 +++++
 3 files changed, 14 insertions(+), 5 deletions(-)
Mark Brown July 28, 2017, 10:07 a.m. UTC | #5
On Fri, Jul 28, 2017 at 01:26:09AM +0200, Antonio Borneo wrote:
> From: Antonio Borneo <borneo.antonio@gmail.com>
> 
> On Hikey target board, enabling CONFIG_OF_DYNAMIC triggers several
> errors at kernel boot, like
> OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0
> OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0/endpoint
> each followed by stack dump.

Please take a look at the patches that Tony Lindgren has posted in the
past day for this issue which go into the of_graph API to make it less
error prone.
Antonio Borneo July 30, 2017, 8:34 p.m. UTC | #6
On Fri, Jul 28, 2017 at 12:07 PM, Mark Brown <broonie@kernel.org> wrote:
> On Fri, Jul 28, 2017 at 01:26:09AM +0200, Antonio Borneo wrote:
>> From: Antonio Borneo <borneo.antonio@gmail.com>
>>
>> On Hikey target board, enabling CONFIG_OF_DYNAMIC triggers several
>> errors at kernel boot, like
>> OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0
>> OF: ERROR: Bad of_node_put() on /soc/i2s@f7118000/ports/port@0/endpoint
>> each followed by stack dump.
>
> Please take a look at the patches that Tony Lindgren has posted in the
> past day for this issue which go into the of_graph API to make it less
> error prone.

Great, it fixes the issue!
Please drop this patchset, the issue is completely covered by Tony's work.

Best Regards
Antonio
diff mbox

Patch

diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c
index c0f08a6..7574c5f 100644
--- a/sound/soc/generic/audio-graph-card.c
+++ b/sound/soc/generic/audio-graph-card.c
@@ -228,10 +228,16 @@  static int asoc_graph_card_parse_of(struct graph_card_data *priv)
 	 */
 
 	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+		/*
+		 * asoc_graph_card_dai_link_of() will call
+		 * of_node_put(). So, call of_node_get() here
+		 */
+		of_node_get(it.node);
 		ret = asoc_graph_card_dai_link_of(it.node, priv, idx++);
-		of_node_put(it.node);
-		if (ret < 0)
+		if (ret < 0) {
+			of_node_put(it.node);
 			return ret;
+		}
 	}
 
 	return asoc_simple_card_parse_card_name(card, NULL);
@@ -244,10 +250,8 @@  static int asoc_graph_get_dais_count(struct device *dev)
 	int count = 0;
 	int rc;
 
-	of_for_each_phandle(&it, rc, node, "dais", NULL, 0) {
+	of_for_each_phandle(&it, rc, node, "dais", NULL, 0)
 		count++;
-		of_node_put(it.node);
-	}
 
 	return count;
 }
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
index 26d64fa..144954b 100644
--- a/sound/soc/generic/simple-card-utils.c
+++ b/sound/soc/generic/simple-card-utils.c
@@ -250,6 +250,11 @@  static int asoc_simple_card_get_dai_id(struct device_node *ep)
 	if (ret != -ENOTSUPP)
 		return ret;
 
+	/*
+	 * of_graph_get_port_parent() will call
+	 * of_node_put(). So, call of_node_get() here
+	 */
+	of_node_get(ep);
 	node = of_graph_get_port_parent(ep);
 
 	/*
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 921622a..a0f39de 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -4087,6 +4087,11 @@  int snd_soc_get_dai_id(struct device_node *ep)
 	struct device_node *node;
 	int ret;
 
+	/*
+	 * of_graph_get_port_parent() will call
+	 * of_node_put(). So, call of_node_get() here
+	 */
+	of_node_get(ep);
 	node = of_graph_get_port_parent(ep);
 
 	/*