diff mbox series

[v2] clk: Add clk_min/max_rate entries in debugfs

Message ID 0c12208398cadb7450b6b7745e99c55770c0ccf8.1561709827.git.leonard.crestez@nxp.com (mailing list archive)
State Superseded
Headers show
Series [v2] clk: Add clk_min/max_rate entries in debugfs | expand

Commit Message

Leonard Crestez June 28, 2019, 8:19 a.m. UTC
Add two files to expose min/max clk rates as determined by
clk_core_get_boundaries, taking all consumer requests into account.

This information does not appear to be otherwise exposed to userspace.

Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>

---
 drivers/clk/clk.c | 39 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 39 insertions(+)

Changes since v1:
* Call clk_prepare_lock/clk_prepare_unlock (Geert)
* Also include in clk_dump, but only with non-default values
Link to v1: https://patchwork.kernel.org/patch/11019873/

Didn't add to clk_summary because min/max rates are rarely used and
clk_summary already has too many columns.

Comments

Geert Uytterhoeven June 28, 2019, 11:58 a.m. UTC | #1
Hi Leonard,

On Fri, Jun 28, 2019 at 10:19 AM Leonard Crestez
<leonard.crestez@nxp.com> wrote:
> Add two files to expose min/max clk rates as determined by
> clk_core_get_boundaries, taking all consumer requests into account.
>
> This information does not appear to be otherwise exposed to userspace.
>
> Signed-off-by: Leonard Crestez <leonard.crestez@nxp.com>

> Changes since v1:
> * Call clk_prepare_lock/clk_prepare_unlock (Geert)
> * Also include in clk_dump, but only with non-default values
> Link to v1: https://patchwork.kernel.org/patch/11019873/

Thanks for the update!

> Didn't add to clk_summary because min/max rates are rarely used and
> clk_summary already has too many columns.

Agreed.

> --- a/drivers/clk/clk.c
> +++ b/drivers/clk/clk.c
> @@ -591,10 +591,12 @@ static void clk_core_get_boundaries(struct clk_core *core,
>                                     unsigned long *min_rate,
>                                     unsigned long *max_rate)
>  {
>         struct clk *clk_user;
>
> +       lockdep_assert_held(&prepare_lock);
> +

I guess the clock maintainers want to see the addition of this check
spun off into a separate patch....

> @@ -3062,10 +3071,38 @@ static int clk_duty_cycle_show(struct seq_file *s, void *data)
>
>         return 0;
>  }
>  DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle);
>
> +static int clk_min_rate_show(struct seq_file *s, void *data)
> +{
> +       struct clk_core *core = s->private;
> +       unsigned long min_rate, max_rate;
> +
> +       clk_prepare_lock();
> +       clk_core_get_boundaries(core, &min_rate, &max_rate);
> +       seq_printf(s, "%lu\n", min_rate);
> +       clk_prepare_unlock();

You can move the release of the lock one line up.

> +
> +       return 0;
> +}
> +DEFINE_SHOW_ATTRIBUTE(clk_min_rate);
> +
> +static int clk_max_rate_show(struct seq_file *s, void *data)
> +{
> +       struct clk_core *core = s->private;
> +       unsigned long min_rate, max_rate;
> +
> +       clk_prepare_lock();
> +       clk_core_get_boundaries(core, &min_rate, &max_rate);
> +       seq_printf(s, "%lu\n", max_rate);
> +       clk_prepare_unlock();

You can move the release of the lock one line up.

> +
> +       return 0;
> +}

With the above fixed:
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>

Gr{oetje,eeting}s,

                        Geert
Stephen Boyd June 28, 2019, 7:35 p.m. UTC | #2
Quoting Geert Uytterhoeven (2019-06-28 04:58:27)
> On Fri, Jun 28, 2019 at 10:19 AM Leonard Crestez
> 
> > --- a/drivers/clk/clk.c
> > +++ b/drivers/clk/clk.c
> > @@ -591,10 +591,12 @@ static void clk_core_get_boundaries(struct clk_core *core,
> >                                     unsigned long *min_rate,
> >                                     unsigned long *max_rate)
> >  {
> >         struct clk *clk_user;
> >
> > +       lockdep_assert_held(&prepare_lock);
> > +
> 
> I guess the clock maintainers want to see the addition of this check
> spun off into a separate patch....

Yes. I'm not sure we should have the assertion in there. I seem to
recall that we thought it might not always be necessary to have the
lock, but maybe that was wrong.
Leonard Crestez July 1, 2019, 1:13 p.m. UTC | #3
On 6/28/2019 10:36 PM, Stephen Boyd wrote:
> Quoting Geert Uytterhoeven (2019-06-28 04:58:27)
>> On Fri, Jun 28, 2019 at 10:19 AM Leonard Crestez
>>
>>> --- a/drivers/clk/clk.c
>>> +++ b/drivers/clk/clk.c
>>> @@ -591,10 +591,12 @@ static void clk_core_get_boundaries(struct clk_core *core,
>>>                                      unsigned long *min_rate,
>>>                                      unsigned long *max_rate)
>>>   {
>>>          struct clk *clk_user;
>>>
>>> +       lockdep_assert_held(&prepare_lock);
>>> +
>>
>> I guess the clock maintainers want to see the addition of this check
>> spun off into a separate patch....
> 
> Yes. I'm not sure we should have the assertion in there. I seem to
> recall that we thought it might not always be necessary to have the
> lock, but maybe that was wrong.

The clk_core_get_boundaries function iterates consumers so locking is 
required. Looking at other functions manipulating clks_node (such as 
__clk_put, clk_core_link/unlink_consumer) this is always done under 
prepare_lock.

In theory without locking debugfs could read freed memory if it happens 
at the same time as device removal.

--
Regards,
Leonard
diff mbox series

Patch

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index efa593ecbfa9..8cec1954580b 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -591,10 +591,12 @@  static void clk_core_get_boundaries(struct clk_core *core,
 				    unsigned long *min_rate,
 				    unsigned long *max_rate)
 {
 	struct clk *clk_user;
 
+	lockdep_assert_held(&prepare_lock);
+
 	*min_rate = core->min_rate;
 	*max_rate = core->max_rate;
 
 	hlist_for_each_entry(clk_user, &core->clks, clks_node)
 		*min_rate = max(*min_rate, clk_user->min_rate);
@@ -2894,19 +2896,26 @@  static int clk_summary_show(struct seq_file *s, void *data)
 }
 DEFINE_SHOW_ATTRIBUTE(clk_summary);
 
 static void clk_dump_one(struct seq_file *s, struct clk_core *c, int level)
 {
+	unsigned long min_rate, max_rate;
+
 	if (!c)
 		return;
 
 	/* This should be JSON format, i.e. elements separated with a comma */
 	seq_printf(s, "\"%s\": { ", c->name);
 	seq_printf(s, "\"enable_count\": %d,", c->enable_count);
 	seq_printf(s, "\"prepare_count\": %d,", c->prepare_count);
 	seq_printf(s, "\"protect_count\": %d,", c->protect_count);
 	seq_printf(s, "\"rate\": %lu,", clk_core_get_rate(c));
+	clk_core_get_boundaries(c, &min_rate, &max_rate);
+	if (min_rate != 0)
+		seq_printf(s, "\"min_rate\": %lu,", min_rate);
+	if (max_rate != ULONG_MAX)
+		seq_printf(s, "\"max_rate\": %lu,", max_rate);
 	seq_printf(s, "\"accuracy\": %lu,", clk_core_get_accuracy(c));
 	seq_printf(s, "\"phase\": %d,", clk_core_get_phase(c));
 	seq_printf(s, "\"duty_cycle\": %u",
 		   clk_core_get_scaled_duty_cycle(c, 100000));
 }
@@ -3062,10 +3071,38 @@  static int clk_duty_cycle_show(struct seq_file *s, void *data)
 
 	return 0;
 }
 DEFINE_SHOW_ATTRIBUTE(clk_duty_cycle);
 
+static int clk_min_rate_show(struct seq_file *s, void *data)
+{
+	struct clk_core *core = s->private;
+	unsigned long min_rate, max_rate;
+
+	clk_prepare_lock();
+	clk_core_get_boundaries(core, &min_rate, &max_rate);
+	seq_printf(s, "%lu\n", min_rate);
+	clk_prepare_unlock();
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(clk_min_rate);
+
+static int clk_max_rate_show(struct seq_file *s, void *data)
+{
+	struct clk_core *core = s->private;
+	unsigned long min_rate, max_rate;
+
+	clk_prepare_lock();
+	clk_core_get_boundaries(core, &min_rate, &max_rate);
+	seq_printf(s, "%lu\n", max_rate);
+	clk_prepare_unlock();
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(clk_max_rate);
+
 static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
 {
 	struct dentry *root;
 
 	if (!core || !pdentry)
@@ -3073,10 +3110,12 @@  static void clk_debug_create_one(struct clk_core *core, struct dentry *pdentry)
 
 	root = debugfs_create_dir(core->name, pdentry);
 	core->dentry = root;
 
 	debugfs_create_ulong("clk_rate", 0444, root, &core->rate);
+	debugfs_create_file("clk_min_rate", 0444, root, core, &clk_min_rate_fops);
+	debugfs_create_file("clk_max_rate", 0444, root, core, &clk_max_rate_fops);
 	debugfs_create_ulong("clk_accuracy", 0444, root, &core->accuracy);
 	debugfs_create_u32("clk_phase", 0444, root, &core->phase);
 	debugfs_create_file("clk_flags", 0444, root, core, &clk_flags_fops);
 	debugfs_create_u32("clk_prepare_count", 0444, root, &core->prepare_count);
 	debugfs_create_u32("clk_enable_count", 0444, root, &core->enable_count);