Messages in this thread | | | Subject | Re: [PATCH v3 1/1] clk: divider: Fix divisor masking on 64 bit platforms | From | Stephen Boyd <> | Date | Mon, 23 Oct 2023 20:34:12 -0700 |
| |
Quoting Sebastian Reichel (2023-06-30 11:38:35) > The clock framework handles clock rates as "unsigned long", so u32 on > 32-bit architectures and u64 on 64-bit architectures. > > The current code casts the dividend to u64 on 32-bit to avoid a > potential overflow. For example DIV_ROUND_UP(3000000000, 1500000000) > = (3.0G + 1.5G - 1) / 1.5G = = OVERFLOW / 1.5G, which has been > introduced in commit 9556f9dad8f5 ("clk: divider: handle integer overflow > when dividing large clock rates"). > > On 64 bit platforms this masks the divisor, so that only the lower > 32 bit are used. Thus requesting a frequency >= 4.3GHz results > in incorrect values. For example requesting 4300000000 (4.3 GHz) will > effectively request ca. 5 MHz. Requesting clk_round_rate(clk, ULONG_MAX) > is a bit of a special case, since that still returns correct values as > long as the parent clock is below 8.5 GHz. > > Fix this by introducing a new helper, which avoids the overflow > by using a modulo operation instead of math tricks. This avoids > any requirements on the arguments (except that divisor should not > be 0 obviously). > > Signed-off-by: Sebastian Reichel <sebastian.reichel@collabora.com> > ---
Sorry this one fell off my review list :(
> Changes since PATCHv2: > * https://lore.kernel.org/all/20230526171057.66876-1-sebastian.reichel@collabora.com/ > * Drop first patch (applied) > * Update second patch to use newly introduced DIV_ROUND_UP_NO_OVERFLOW > > Changes since PATCHv1: > * https://lore.kernel.org/linux-clk/20230519190522.194729-1-sebastian.reichel@collabora.com/ > * Add Christopher Obbard's Reviewed-by to the first patch > * Update the second patch to use DIV_ROUND_UP instead of DIV64_U64_ROUND_UP > --- > drivers/clk/clk-divider.c | 6 +++--- > include/linux/math.h | 11 +++++++++++ > 2 files changed, 14 insertions(+), 3 deletions(-) > > diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c > index a2c2b5203b0a..94b4fb66a60f 100644 > --- a/drivers/clk/clk-divider.c > +++ b/drivers/clk/clk-divider.c > @@ -220,7 +220,7 @@ static int _div_round_up(const struct clk_div_table *table, > unsigned long parent_rate, unsigned long rate, > unsigned long flags) > { > - int div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + int div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); > > if (flags & CLK_DIVIDER_POWER_OF_TWO) > div = __roundup_pow_of_two(div); > @@ -237,7 +237,7 @@ static int _div_round_closest(const struct clk_div_table *table, > int up, down; > unsigned long up_rate, down_rate; > > - up = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + up = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); > down = parent_rate / rate; > > if (flags & CLK_DIVIDER_POWER_OF_TWO) { > @@ -473,7 +473,7 @@ int divider_get_val(unsigned long rate, unsigned long parent_rate, > { > unsigned int div, value; > > - div = DIV_ROUND_UP_ULL((u64)parent_rate, rate); > + div = DIV_ROUND_UP_NO_OVERFLOW(parent_rate, rate); > > if (!_is_valid_div(table, div, flags)) > return -EINVAL; > diff --git a/include/linux/math.h b/include/linux/math.h > index 2d388650c556..cf14d436fc2e 100644 > --- a/include/linux/math.h > +++ b/include/linux/math.h > @@ -36,6 +36,17 @@ > > #define DIV_ROUND_UP __KERNEL_DIV_ROUND_UP > > +/** > + * DIV_ROUND_UP_NO_OVERFLOW - divide two numbers and always round up > + * @n: numerator / dividend > + * @d: denominator / divisor > + * > + * This functions does the same as DIV_ROUND_UP, but internally uses a > + * division and a modulo operation instead of math tricks. This way it > + * avoids overflowing when handling big numbers. > + */ > +#define DIV_ROUND_UP_NO_OVERFLOW(n, d) (((n) / (d)) + !!((n) % (d)))
Can you get someone to review/ack this macro? Maybe Andy?
> + > #define DIV_ROUND_DOWN_ULL(ll, d) \ > ({ unsigned long long _tmp = (ll); do_div(_tmp, d); _tmp; }) >
| |