diff mbox series

[1/2] clk: Ensure new parent is looked up when changing parents

Message ID 20190430144412.20950-1-ckeepax@opensource.cirrus.com (mailing list archive)
State Changes Requested, archived
Headers show
Series [1/2] clk: Ensure new parent is looked up when changing parents | expand

Commit Message

Charles Keepax April 30, 2019, 2:44 p.m. UTC
clk_core_fill_parent_index is called from clk_mux_determine_rate_flags
and for the initial parent of the clock but seems to not get called on
the path changing a clocks parent. This can cause a clock parent change
to fail, fix this by adding a call in clk_fetch_parent_index.

Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names")
Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
---
 drivers/clk/clk.c | 3 +++
 1 file changed, 3 insertions(+)

Comments

Stephen Boyd April 30, 2019, 4:44 p.m. UTC | #1
Quoting Charles Keepax (2019-04-30 07:44:11)
> clk_core_fill_parent_index is called from clk_mux_determine_rate_flags
> and for the initial parent of the clock but seems to not get called on
> the path changing a clocks parent. This can cause a clock parent change
> to fail, fix this by adding a call in clk_fetch_parent_index.
> 
> Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names")
> Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> ---
>  drivers/clk/clk.c | 3 +++
>  1 file changed, 3 insertions(+)
> 
> diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> index ffd33b63c37eb..5aa180180ee95 100644
> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -1601,6 +1601,9 @@ static int clk_fetch_parent_index(struct clk_core *core,
>                 return -EINVAL;
>  
>         for (i = 0; i < core->num_parents; i++) {
> +               if (!core->parents[i].core)
> +                       clk_core_fill_parent_index(core, i);
> +

Hm... are you not specifying 'names' for the parent, so just clk_hw
pointer? Maybe we need to compare clk_hw pointers with clk_hw pointers
and then fill in the core pointer with what we have in hand. Pretty much
at all costs we shouldn't call clk_core_fill_parent_index() here because
drivers may fall into the trap of searching the entire clk tree for a
pointer we already have.

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 114f0bffd630..c4fa341330fa 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1619,6 +1619,11 @@ static int clk_fetch_parent_index(struct clk_core *core,
 		if (core->parents[i].core)
 			continue;
 
+		if (core->parents[i].hw == parent->hw) {
+			core->parents[i].core = parent;
+			return i;
+		}
+
 		/* Fallback to comparing globally unique names */
 		if (!strcmp(parent->name, core->parents[i].name)) {
 			core->parents[i].core = parent;
Charles Keepax May 1, 2019, 8:33 a.m. UTC | #2
On Tue, Apr 30, 2019 at 09:44:49AM -0700, Stephen Boyd wrote:
> Quoting Charles Keepax (2019-04-30 07:44:11)
> > clk_core_fill_parent_index is called from clk_mux_determine_rate_flags
> > and for the initial parent of the clock but seems to not get called on
> > the path changing a clocks parent. This can cause a clock parent change
> > to fail, fix this by adding a call in clk_fetch_parent_index.
> > 
> > Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names")
> > Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> > ---
> >  drivers/clk/clk.c | 3 +++
> >  1 file changed, 3 insertions(+)
> > 
> > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > index ffd33b63c37eb..5aa180180ee95 100644
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -1601,6 +1601,9 @@ static int clk_fetch_parent_index(struct clk_core *core,
> >                 return -EINVAL;
> >  
> >         for (i = 0; i < core->num_parents; i++) {
> > +               if (!core->parents[i].core)
> > +                       clk_core_fill_parent_index(core, i);
> > +
> 
> Hm... are you not specifying 'names' for the parent, so just clk_hw
> pointer? Maybe we need to compare clk_hw pointers with clk_hw pointers
> and then fill in the core pointer with what we have in hand. Pretty much
> at all costs we shouldn't call clk_core_fill_parent_index() here because
> drivers may fall into the trap of searching the entire clk tree for a
> pointer we already have.
> 

Apologies perhaps I am misunderstanding how this new system
works. In the event of the parent clocks being specified in
DT, whilst going round this loop would you expect the clock to
match on the core == parent check?  Or on the fallback unique
name check? My assumption was on the core == parent check, and
if that is the case how would you expect the parents[i].core
field to have been populated?

Thanks,
Charles
Stephen Boyd May 1, 2019, 7:59 p.m. UTC | #3
Quoting Charles Keepax (2019-05-01 01:33:17)
> On Tue, Apr 30, 2019 at 09:44:49AM -0700, Stephen Boyd wrote:
> > Quoting Charles Keepax (2019-04-30 07:44:11)
> > > clk_core_fill_parent_index is called from clk_mux_determine_rate_flags
> > > and for the initial parent of the clock but seems to not get called on
> > > the path changing a clocks parent. This can cause a clock parent change
> > > to fail, fix this by adding a call in clk_fetch_parent_index.
> > > 
> > > Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names")
> > > Signed-off-by: Charles Keepax <ckeepax@opensource.cirrus.com>
> > > ---
> > >  drivers/clk/clk.c | 3 +++
> > >  1 file changed, 3 insertions(+)
> > > 
> > > diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
> > > index ffd33b63c37eb..5aa180180ee95 100644
> > > --- a/drivers/clk/clk.c
> > > +++ b/drivers/clk/clk.c
> > > @@ -1601,6 +1601,9 @@ static int clk_fetch_parent_index(struct clk_core *core,
> > >                 return -EINVAL;
> > >  
> > >         for (i = 0; i < core->num_parents; i++) {
> > > +               if (!core->parents[i].core)
> > > +                       clk_core_fill_parent_index(core, i);
> > > +
> > 
> > Hm... are you not specifying 'names' for the parent, so just clk_hw
> > pointer? Maybe we need to compare clk_hw pointers with clk_hw pointers
> > and then fill in the core pointer with what we have in hand. Pretty much
> > at all costs we shouldn't call clk_core_fill_parent_index() here because
> > drivers may fall into the trap of searching the entire clk tree for a
> > pointer we already have.
> > 
> 
> Apologies perhaps I am misunderstanding how this new system
> works. In the event of the parent clocks being specified in
> DT, whilst going round this loop would you expect the clock to
> match on the core == parent check?  Or on the fallback unique
> name check? My assumption was on the core == parent check, and
> if that is the case how would you expect the parents[i].core
> field to have been populated?
> 

I don't expect parents[i].core to be populated until we do the fallback
string match in this function when the globally unique names match or if
it's already populated by some other path calling
clk_core_fill_parent_index(). The problem is we just fixed a long
standing regression in this function with commit ede77858473a ("clk:
Remove global clk traversal on fetch parent index"). Calling
clk_core_fill_parent_index() will bring that performance problem back,
so we need to figure out how to find the index for a clk without doing
the global search.

It seems that some clk providers specify parents that may never exist,
so we'll possibly spend time looping through the parents each time doing
a global string comparison on hundreds of clks. It would be best to
avoid that, so we shouldn't really do any sort of caching here except
for the one clk_core pointer we already have passed in.

So you're saying this happens in the clk_set_parent() path, where the
parent passed into this function has never been cached before?
Otherwise, it looks like the only other case where it might be a problem
would be if a .round_rate() or .determine_rate() clk_op returns a parent
that hasn't been cached yet, maybe via hard-coding of the parent hw
pointer or just because they don't call clk_hw_get_parent_by_index().
I'd rather not cater to those corner cases by trying to optimize them,
so perhaps we can use this patch and avoid the regression while
supporting the use-cases we need?

----8<-----
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dc05cb339761..7ad1751bf2b4 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -327,8 +327,7 @@ static struct clk_core *clk_core_lookup(const char *name)
 /**
  * clk_core_get - Find the clk_core parent of a clk
  * @core: clk to find parent of
- * @name: name to search for (if string based)
- * @index: index to use for search (if DT index based)
+ * @p_index: parent index to search for
  *
  * This is the preferred method for clk providers to find the parent of a
  * clk when that parent is external to the clk controller. The parent_names
@@ -360,9 +359,10 @@ static struct clk_core *clk_core_lookup(const char *name)
  * provider knows about the clk but it isn't provided on this system.
  * A valid clk_core pointer when the clk can be found in the provider.
  */
-static struct clk_core *clk_core_get(struct clk_core *core, const char *name,
-				     int index)
+static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
 {
+	const char *name = core->parents[p_index].fw_name;
+	u8 index = core->parents[p_index].index;
 	struct clk_hw *hw = ERR_PTR(-ENOENT);
 	struct device *dev = core->dev;
 	const char *dev_id = dev ? dev_name(dev) : NULL;
@@ -400,7 +400,7 @@ static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
 		if (!parent)
 			parent = ERR_PTR(-EPROBE_DEFER);
 	} else {
-		parent = clk_core_get(core, entry->fw_name, entry->index);
+		parent = clk_core_get(core, index);
 		if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT)
 			parent = clk_core_lookup(entry->name);
 	}
@@ -1612,12 +1612,29 @@ static int clk_fetch_parent_index(struct clk_core *core,
 		return -EINVAL;
 
 	for (i = 0; i < core->num_parents; i++) {
+		/* Found it first try! */
 		if (core->parents[i].core == parent)
 			return i;
 
+		/* Something else is here, so keep looking */
 		if (core->parents[i].core)
 			continue;
 
+		/* Maybe core hasn't been cached but the hw is all we know? */
+		if (core->parents[i].hw) {
+			if (core->parents[i].hw == parent->hw) {
+				core->parents[i].core = parent;
+				return i;
+			}
+
+			/* Didn't match, but we're expecting a clk_hw */
+			continue;
+		}
+
+		/* Maybe it hasn't been cached (clk_set_parent() path) */
+		if (parent == clk_core_get(core, i))
+			return i;
+
 		/* Fallback to comparing globally unique names */
 		if (!strcmp(parent->name, core->parents[i].name)) {
 			core->parents[i].core = parent;
Charles Keepax May 3, 2019, 4:33 p.m. UTC | #4
On Wed, May 01, 2019 at 12:59:48PM -0700, Stephen Boyd wrote:
> Quoting Charles Keepax (2019-05-01 01:33:17)
> > On Tue, Apr 30, 2019 at 09:44:49AM -0700, Stephen Boyd wrote:
> > > Quoting Charles Keepax (2019-04-30 07:44:11)

Thank you for the explanation think I am starting to get there.

> I don't expect parents[i].core to be populated until we do the fallback
> string match in this function when the globally unique names match or if
> it's already populated by some other path calling
> clk_core_fill_parent_index(). The problem is we just fixed a long
> standing regression in this function with commit ede77858473a ("clk:
> Remove global clk traversal on fetch parent index"). Calling
> clk_core_fill_parent_index() will bring that performance problem back,
> so we need to figure out how to find the index for a clk without doing
> the global search.
> 

There is a slight error in the commit message there I think, the
change doesn't affect clk_mux_determine_rate_flags which still
calls clk_core_get_parent_by_index so will still do the full
lookup. It looks like it affects clk_calc_new_rates instead.

Which I guess does raise the question would an optimisation on
the determine_rate path help these power issues as well?

> It seems that some clk providers specify parents that may never exist,
> so we'll possibly spend time looping through the parents each time doing
> a global string comparison on hundreds of clks. It would be best to
> avoid that, so we shouldn't really do any sort of caching here except
> for the one clk_core pointer we already have passed in.
> 
> So you're saying this happens in the clk_set_parent() path, where the
> parent passed into this function has never been cached before?

Yeah that seems to be what is happening in my case. As best I
can figure out so far, this relates to our clocks not having
any rate setting capabilities, as it looks like most of the
cache population comes from those paths.

> +
> +		/* Maybe it hasn't been cached (clk_set_parent() path) */
> +		if (parent == clk_core_get(core, i))
> +			return i;

This part does fix my issue. Is there a reason not to update
core->parents[i].core on this path?

Thanks,
Charles
Stephen Boyd May 3, 2019, 5:44 p.m. UTC | #5
Quoting Charles Keepax (2019-05-03 09:33:22)
> On Wed, May 01, 2019 at 12:59:48PM -0700, Stephen Boyd wrote:
> > Quoting Charles Keepax (2019-05-01 01:33:17)
> > > On Tue, Apr 30, 2019 at 09:44:49AM -0700, Stephen Boyd wrote:
> > > > Quoting Charles Keepax (2019-04-30 07:44:11)
> 
> Thank you for the explanation think I am starting to get there.
> 
> > I don't expect parents[i].core to be populated until we do the fallback
> > string match in this function when the globally unique names match or if
> > it's already populated by some other path calling
> > clk_core_fill_parent_index(). The problem is we just fixed a long
> > standing regression in this function with commit ede77858473a ("clk:
> > Remove global clk traversal on fetch parent index"). Calling
> > clk_core_fill_parent_index() will bring that performance problem back,
> > so we need to figure out how to find the index for a clk without doing
> > the global search.
> > 
> 
> There is a slight error in the commit message there I think, the
> change doesn't affect clk_mux_determine_rate_flags which still
> calls clk_core_get_parent_by_index so will still do the full
> lookup. It looks like it affects clk_calc_new_rates instead.
> 
> Which I guess does raise the question would an optimisation on
> the determine_rate path help these power issues as well?

Yes I would think that determine_rate() is iterating over parents and
trying to find them again and again even when there are holes and that's
probably wasting time. I hope we can fill the parent cache of a clk with
the error pointer indicating the clk will never appear (-ENOENT?) and
then skip over it in this case.

> 
> > It seems that some clk providers specify parents that may never exist,
> > so we'll possibly spend time looping through the parents each time doing
> > a global string comparison on hundreds of clks. It would be best to
> > avoid that, so we shouldn't really do any sort of caching here except
> > for the one clk_core pointer we already have passed in.
> > 
> > So you're saying this happens in the clk_set_parent() path, where the
> > parent passed into this function has never been cached before?
> 
> Yeah that seems to be what is happening in my case. As best I
> can figure out so far, this relates to our clocks not having
> any rate setting capabilities, as it looks like most of the
> cache population comes from those paths.

Makes sense!

> 
> > +
> > +             /* Maybe it hasn't been cached (clk_set_parent() path) */
> > +             if (parent == clk_core_get(core, i))
> > +                     return i;
> 
> This part does fix my issue. Is there a reason not to update
> core->parents[i].core on this path?
> 

Nope. I though clk_core_get() did it already, but it doesn't. Thanks for
catching it. Find the new patch attached:

---8<---
Subject: [PATCH] clk: Cache core in clk_fetch_parent_index() without names

If a clk has specified parents via clk_hw pointers it won't specify the
globally unique names for the parents. Without the unique names, we
can't fallback to comparing them against the name of the 'parent'
pointer here. Therefore, do a pointer comparison against the clk_hw
pointers too and cache the clk_core structure if they match. This fixes
parent lookup code for clks that only specify clk_hw pointers and
nothing else, like muxes that are purely inside a clk controller.

Similarly, if the parent pointer isn't cached after trying to match
clk_core or clk_hw pointers, lookup the pointer from DT or via clkdev
lookups instead of relying purely on the globally unique clk name match.
This should allow us to move away from having to specify global names
for clk parents entirely.

While we're in the area, add some comments so it's clearer what's going
on. The if statements don't lend themselves to much clarity in their raw
form.

Fixes: fc0c209c147f ("clk: Allow parents to be specified without string names")
Reported-by: Charles Keepax <ckeepax@opensource.cirrus.com>
Signed-off-by: Stephen Boyd <sboyd@kernel.org>
---
 drivers/clk/clk.c | 37 +++++++++++++++++++++++++++----------
 1 file changed, 27 insertions(+), 10 deletions(-)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index dc05cb339761..dcb7e1cddd2d 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -327,8 +327,7 @@ static struct clk_core *clk_core_lookup(const char *name)
 /**
  * clk_core_get - Find the clk_core parent of a clk
  * @core: clk to find parent of
- * @name: name to search for (if string based)
- * @index: index to use for search (if DT index based)
+ * @p_index: parent index to search for
  *
  * This is the preferred method for clk providers to find the parent of a
  * clk when that parent is external to the clk controller. The parent_names
@@ -360,9 +359,10 @@ static struct clk_core *clk_core_lookup(const char *name)
  * provider knows about the clk but it isn't provided on this system.
  * A valid clk_core pointer when the clk can be found in the provider.
  */
-static struct clk_core *clk_core_get(struct clk_core *core, const char *name,
-				     int index)
+static struct clk_core *clk_core_get(struct clk_core *core, u8 p_index)
 {
+	const char *name = core->parents[p_index].fw_name;
+	int index = core->parents[p_index].index;
 	struct clk_hw *hw = ERR_PTR(-ENOENT);
 	struct device *dev = core->dev;
 	const char *dev_id = dev ? dev_name(dev) : NULL;
@@ -400,7 +400,7 @@ static void clk_core_fill_parent_index(struct clk_core *core, u8 index)
 		if (!parent)
 			parent = ERR_PTR(-EPROBE_DEFER);
 	} else {
-		parent = clk_core_get(core, entry->fw_name, entry->index);
+		parent = clk_core_get(core, index);
 		if (IS_ERR(parent) && PTR_ERR(parent) == -ENOENT)
 			parent = clk_core_lookup(entry->name);
 	}
@@ -1612,20 +1612,37 @@ static int clk_fetch_parent_index(struct clk_core *core,
 		return -EINVAL;
 
 	for (i = 0; i < core->num_parents; i++) {
+		/* Found it first try! */
 		if (core->parents[i].core == parent)
 			return i;
 
+		/* Something else is here, so keep looking */
 		if (core->parents[i].core)
 			continue;
 
-		/* Fallback to comparing globally unique names */
-		if (!strcmp(parent->name, core->parents[i].name)) {
-			core->parents[i].core = parent;
-			return i;
+		/* Maybe core hasn't been cached but the hw is all we know? */
+		if (core->parents[i].hw) {
+			if (core->parents[i].hw == parent->hw)
+				break;
+
+			/* Didn't match, but we're expecting a clk_hw */
+			continue;
 		}
+
+		/* Maybe it hasn't been cached (clk_set_parent() path) */
+		if (parent == clk_core_get(core, i))
+			break;
+
+		/* Fallback to comparing globally unique names */
+		if (!strcmp(parent->name, core->parents[i].name))
+			break;
 	}
 
-	return -EINVAL;
+	if (i == core->num_parents)
+		return -EINVAL;
+
+	core->parents[i].core = parent;
+	return i;
 }
 
 /*
diff mbox series

Patch

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index ffd33b63c37eb..5aa180180ee95 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -1601,6 +1601,9 @@  static int clk_fetch_parent_index(struct clk_core *core,
 		return -EINVAL;
 
 	for (i = 0; i < core->num_parents; i++) {
+		if (!core->parents[i].core)
+			clk_core_fill_parent_index(core, i);
+
 		if (core->parents[i].core == parent)
 			return i;