Skip to content

Commit 10c46f2

Browse files
mripardbebarino
authored andcommitted
clk: Enforce that disjoints limits are invalid
If we were to have two users of the same clock, doing something like: clk_set_rate_range(user1, 1000, 2000); clk_set_rate_range(user2, 3000, 4000); The second call would fail with -EINVAL, preventing from getting in a situation where we end up with impossible limits. However, this is never explicitly checked against and enforced, and works by relying on an undocumented behaviour of clk_set_rate(). Indeed, on the first clk_set_rate_range will make sure the current clock rate is within the new range, so it will be between 1000 and 2000Hz. On the second clk_set_rate_range(), it will consider (rightfully), that our current clock is outside of the 3000-4000Hz range, and will call clk_core_set_rate_nolock() to set it to 3000Hz. clk_core_set_rate_nolock() will then call clk_calc_new_rates() that will eventually check that our rate 3000Hz rate is outside the min 3000Hz max 2000Hz range, will bail out, the error will propagate and we'll eventually return -EINVAL. This solely relies on the fact that clk_calc_new_rates(), and in particular clk_core_determine_round_nolock(), won't modify the new rate allowing the error to be reported. That assumption won't be true for all drivers, and most importantly we'll break that assumption in a later patch. It can also be argued that we shouldn't even reach the point where we're calling clk_core_set_rate_nolock(). Let's make an explicit check for disjoints range before we're doing anything. Signed-off-by: Maxime Ripard <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Stephen Boyd <[email protected]>
1 parent 723d053 commit 10c46f2

File tree

1 file changed

+24
-0
lines changed

1 file changed

+24
-0
lines changed

drivers/clk/clk.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,24 @@ static void clk_core_get_boundaries(struct clk_core *core,
632632
*max_rate = min(*max_rate, clk_user->max_rate);
633633
}
634634

635+
static bool clk_core_check_boundaries(struct clk_core *core,
636+
unsigned long min_rate,
637+
unsigned long max_rate)
638+
{
639+
struct clk *user;
640+
641+
lockdep_assert_held(&prepare_lock);
642+
643+
if (min_rate > core->max_rate || max_rate < core->min_rate)
644+
return false;
645+
646+
hlist_for_each_entry(user, &core->clks, clks_node)
647+
if (min_rate > user->max_rate || max_rate < user->min_rate)
648+
return false;
649+
650+
return true;
651+
}
652+
635653
void clk_hw_set_rate_range(struct clk_hw *hw, unsigned long min_rate,
636654
unsigned long max_rate)
637655
{
@@ -2348,6 +2366,11 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
23482366
clk->min_rate = min;
23492367
clk->max_rate = max;
23502368

2369+
if (!clk_core_check_boundaries(clk->core, min, max)) {
2370+
ret = -EINVAL;
2371+
goto out;
2372+
}
2373+
23512374
rate = clk_core_get_rate_nolock(clk->core);
23522375
if (rate < min || rate > max) {
23532376
/*
@@ -2376,6 +2399,7 @@ int clk_set_rate_range(struct clk *clk, unsigned long min, unsigned long max)
23762399
}
23772400
}
23782401

2402+
out:
23792403
if (clk->exclusive_count)
23802404
clk_core_rate_protect(clk->core);
23812405

0 commit comments

Comments
 (0)