diff mbox

[v2] clockevents: Sanitize ticks to nsec conversion

Message ID 1380052223-24139-1-git-send-email-u.kleine-koenig@pengutronix.de (mailing list archive)
State New, archived
Headers show

Commit Message

Uwe Kleine-König Sept. 24, 2013, 7:50 p.m. UTC
From: Thomas Gleixner <tglx@linutronix.de>

Marc Kleine-Budde pointed out, that commit 77cc982 "clocksource: use
clockevents_config_and_register() where possible" caused a regression
for some of the converted subarchs.

The reason is, that the clockevents core code converts the minimal
hardware tick delta to a nanosecond value for core internal
usage. This conversion is affected by integer math rounding loss, so
the backwards conversion to hardware ticks will likely result in a
value which is less than the configured hardware limitation. The
affected subarchs used their own workaround (SIGH!) which got lost in
the conversion.

The solution for the issue at hand is simple: adding evt->mult - 1 to
the shifted value before the integer divison in the core conversion
function takes care of it. But this only works for the case where for
the scaled math mult/shift pair "mult <= 1 << shift" is true. For the
case where "mult > 1 << shift" we can apply the rounding add only for
the minimum delta value to make sure that the backward conversion is
not less than the given hardware limit. For the upper bound we need to
omit the rounding add, because the backwards conversion is always
larger than the original latch value. That would violate the upper
bound of the hardware device.

Though looking closer at the details of that function reveals another
bogosity: The upper bounds check is broken as well. Checking for a
resulting "clc" value greater than KTIME_MAX after the conversion is
pointless. The conversion does:

      u64 clc = (latch << evt->shift) / evt->mult;

So there is no sanity check for (latch << evt->shift) exceeding the
64bit boundary. The latch argument is "unsigned long", so on a 64bit
arch the handed in argument could easily lead to an unnoticed shift
overflow. With the above rounding fix applied the calculation before
the divison is:

       u64 clc = (latch << evt->shift) + evt->mult - 1;

So we need to make sure, that neither the shift nor the rounding add
is overflowing the u64 boundary.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
Cc: Marc Kleine-Budde <mkl@pengutronix.de>
Cc: nicolas.ferre@atmel.com
Cc: Marc Pignat <marc.pignat@hevs.ch>
Cc: john.stultz@linaro.org
Cc: kernel@pengutronix.de
Cc: Ronald Wahl <ronald.wahl@raritan.com>
Cc: LAK <linux-arm-kernel@lists.infradead.org>
Cc: Ludovic Desroches <ludovic.desroches@atmel.com>
[ukl: move assignment to rnd after eventually changing mult, fix build
 issue and correct comment with the right math]
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
---
 kernel/time/clockevents.c | 65 ++++++++++++++++++++++++++++++++++++-----------
 1 file changed, 50 insertions(+), 15 deletions(-)

Comments

Uwe Kleine-König Sept. 24, 2013, 9:11 p.m. UTC | #1
Hello Nicolas, hello Jean-Christophe,

I expect you should be able to revert 

	b7a8ca5 (ARM: at91: rm9200 fix time support)

which reverted 

	838a2ae (ARM: use clockevents_config_and_register() where possible)

for at91rm9200 on top of the patch this mail is a reply to.

Can you please test that?

Best regards
Uwe
Uwe Kleine-König Sept. 24, 2013, 9:16 p.m. UTC | #2
Hello Thomas,

just found another little issue ...

On Tue, Sep 24, 2013 at 09:50:23PM +0200, Uwe Kleine-König wrote:
> +/**
> + * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
> + * @latch:	value to convert
> + * @evt:	pointer to clock event device descriptor
> + *
> + * Math helper, returns latch value converted to nanoseconds (bound checked)
> + */
> +u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
... the kernel-doc comment has an 's' too much in the function name.

I'm not sending a v3 for just that. Can you fix that up when/if
applying?

Best regards
Uwe
Nicolas Ferre Oct. 4, 2013, 10 a.m. UTC | #3
On 24/09/2013 23:11, Uwe Kleine-König :
> Hello Nicolas, hello Jean-Christophe,
>
> I expect you should be able to revert
>
> 	b7a8ca5 (ARM: at91: rm9200 fix time support)
>
> which reverted
>
> 	838a2ae (ARM: use clockevents_config_and_register() where possible)
>
> for at91rm9200 on top of the patch this mail is a reply to.
>
> Can you please test that?

I have just tested that on at91rm9200-ek:
- revert the revert (!)
- experience the crash
- then apply the v2 patch
- and finally see it working nicely!

Thanks a lot for having corrected this. Now, do you want that I stack 
this revert-of-revert on the AT91 git tree or you are able to apply it 
through the clock git tree together with the patch that is fixing the 
issue (it may prevent synchronization difficulties)? (BTW, I do not see 
it yet in "next").

Anyway, if it is required, you can add my:

Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>

Thanks, bye,
Marc Kleine-Budde Oct. 8, 2013, 10:08 a.m. UTC | #4
On 09/24/2013 09:50 PM, Uwe Kleine-König wrote:
> From: Thomas Gleixner <tglx@linutronix.de>
> 
> Marc Kleine-Budde pointed out, that commit 77cc982 "clocksource: use
> clockevents_config_and_register() where possible" caused a regression
> for some of the converted subarchs.
> 
> The reason is, that the clockevents core code converts the minimal
> hardware tick delta to a nanosecond value for core internal
> usage. This conversion is affected by integer math rounding loss, so
> the backwards conversion to hardware ticks will likely result in a
> value which is less than the configured hardware limitation. The
> affected subarchs used their own workaround (SIGH!) which got lost in
> the conversion.
> 
> The solution for the issue at hand is simple: adding evt->mult - 1 to
> the shifted value before the integer divison in the core conversion
> function takes care of it. But this only works for the case where for
> the scaled math mult/shift pair "mult <= 1 << shift" is true. For the
> case where "mult > 1 << shift" we can apply the rounding add only for
> the minimum delta value to make sure that the backward conversion is
> not less than the given hardware limit. For the upper bound we need to
> omit the rounding add, because the backwards conversion is always
> larger than the original latch value. That would violate the upper
> bound of the hardware device.
> 
> Though looking closer at the details of that function reveals another
> bogosity: The upper bounds check is broken as well. Checking for a
> resulting "clc" value greater than KTIME_MAX after the conversion is
> pointless. The conversion does:
> 
>       u64 clc = (latch << evt->shift) / evt->mult;
> 
> So there is no sanity check for (latch << evt->shift) exceeding the
> 64bit boundary. The latch argument is "unsigned long", so on a 64bit
> arch the handed in argument could easily lead to an unnoticed shift
> overflow. With the above rounding fix applied the calculation before
> the divison is:
> 
>        u64 clc = (latch << evt->shift) + evt->mult - 1;
> 
> So we need to make sure, that neither the shift nor the rounding add
> is overflowing the u64 boundary.
> 
> Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
> Cc: Russell King - ARM Linux <linux@arm.linux.org.uk>
> Cc: Marc Kleine-Budde <mkl@pengutronix.de>
> Cc: nicolas.ferre@atmel.com
> Cc: Marc Pignat <marc.pignat@hevs.ch>
> Cc: john.stultz@linaro.org
> Cc: kernel@pengutronix.de
> Cc: Ronald Wahl <ronald.wahl@raritan.com>
> Cc: LAK <linux-arm-kernel@lists.infradead.org>
> Cc: Ludovic Desroches <ludovic.desroches@atmel.com>
> [ukl: move assignment to rnd after eventually changing mult, fix build
>  issue and correct comment with the right math]
> Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>

What's the status of this patch?

Marc
Uwe Kleine-König Oct. 14, 2013, 7:34 a.m. UTC | #5
Hello Linus, hello Ingo,

On Tue, Oct 08, 2013 at 05:31:47PM +0200, Uwe Kleine-König wrote:
> > What's the status of this patch?
> I pinged Thomas about it already via irc, but I didn't get a response up
> to now. It would be great to get that into 3.12. To make it easy for
> Thomas, here is a pull request that includes the fix and converts
> at91rm9200 back to clockevents_config_and_register dropping one of the
> workarounds for the fixed bug.
> 
> The following changes since commit 4a10c2ac2f368583138b774ca41fac4207911983:
> 
>   Linux 3.12-rc2 (2013-09-23 15:41:09 -0700)
> 
> are available in the git repository at:
> 
>   git://git.pengutronix.de/git/ukl/linux.git tags/timer-for-tip
> 
> for you to fetch changes up to b58c4b70121070f45eefa739ecbb58d57da1c4d9:
> 
>   ARM: at91: rm9200: switch back to clockevents_config_and_register (2013-10-08 16:58:22 +0200)
> 
> ----------------------------------------------------------------
> The first patch in this pull request fixes an old integer rounding bug in the
> timer core that was (and still is) worked around in a few arch's timer code.
> The second patch drops such a work around now that the core is fixed.
It would be nice to get that fix into 3.12, but Thomas stopped
responding nearly a month ago---his last mail in this thread[1] dates back
to September 20.

Can one of you please take it directly?

Thanks
Uwe
 
[1] http://thread.gmane.org/gmane.linux.kernel/1563370/
> ----------------------------------------------------------------
> Thomas Gleixner (1):
>       clockevents: Sanitize ticks to nsec conversion
> 
> Uwe Kleine-König (1):
>       ARM: at91: rm9200: switch back to clockevents_config_and_register
> 
>  arch/arm/mach-at91/at91rm9200_time.c |  7 ++--
>  kernel/time/clockevents.c            | 65 +++++++++++++++++++++++++++---------
>  2 files changed, 52 insertions(+), 20 deletions(-)
> 
> -- 
> Pengutronix e.K.                           | Uwe Kleine-König            |
> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
Nicolas Ferre Oct. 16, 2013, 2:19 p.m. UTC | #6
On 14/10/2013 09:34, Uwe Kleine-König :
> Hello Linus, hello Ingo,
>
> On Tue, Oct 08, 2013 at 05:31:47PM +0200, Uwe Kleine-König wrote:
>>> What's the status of this patch?
>> I pinged Thomas about it already via irc, but I didn't get a response up
>> to now. It would be great to get that into 3.12. To make it easy for
>> Thomas, here is a pull request that includes the fix and converts
>> at91rm9200 back to clockevents_config_and_register dropping one of the
>> workarounds for the fixed bug.
>>
>> The following changes since commit 4a10c2ac2f368583138b774ca41fac4207911983:
>>
>>    Linux 3.12-rc2 (2013-09-23 15:41:09 -0700)
>>
>> are available in the git repository at:
>>
>>    git://git.pengutronix.de/git/ukl/linux.git tags/timer-for-tip
>>
>> for you to fetch changes up to b58c4b70121070f45eefa739ecbb58d57da1c4d9:
>>
>>    ARM: at91: rm9200: switch back to clockevents_config_and_register (2013-10-08 16:58:22 +0200)
>>
>> ----------------------------------------------------------------
>> The first patch in this pull request fixes an old integer rounding bug in the
>> timer core that was (and still is) worked around in a few arch's timer code.
>> The second patch drops such a work around now that the core is fixed.
> It would be nice to get that fix into 3.12, but Thomas stopped
> responding nearly a month ago---his last mail in this thread[1] dates back
> to September 20.
>
> Can one of you please take it directly?

I second Uwe in his request (aka +1).

Thanks, bye,


> [1] http://thread.gmane.org/gmane.linux.kernel/1563370/
>> ----------------------------------------------------------------
>> Thomas Gleixner (1):
>>        clockevents: Sanitize ticks to nsec conversion
>>
>> Uwe Kleine-König (1):
>>        ARM: at91: rm9200: switch back to clockevents_config_and_register
>>
>>   arch/arm/mach-at91/at91rm9200_time.c |  7 ++--
>>   kernel/time/clockevents.c            | 65 +++++++++++++++++++++++++++---------
>>   2 files changed, 52 insertions(+), 20 deletions(-)
>>
>> --
>> Pengutronix e.K.                           | Uwe Kleine-König            |
>> Industrial Linux Solutions                 | http://www.pengutronix.de/  |
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
>
Uwe Kleine-König Oct. 21, 2013, 7:12 a.m. UTC | #7
Hello,

[added Daniel Lezcano to To:]

On Mon, Oct 14, 2013 at 09:34:37AM +0200, Uwe Kleine-König wrote:
> On Tue, Oct 08, 2013 at 05:31:47PM +0200, Uwe Kleine-König wrote:
> > > What's the status of this patch?
> > I pinged Thomas about it already via irc, but I didn't get a response up
> > to now. It would be great to get that into 3.12. To make it easy for
> > Thomas, here is a pull request that includes the fix and converts
> > at91rm9200 back to clockevents_config_and_register dropping one of the
> > workarounds for the fixed bug.
> > 
> > The following changes since commit 4a10c2ac2f368583138b774ca41fac4207911983:
> > 
> >   Linux 3.12-rc2 (2013-09-23 15:41:09 -0700)
> > 
> > are available in the git repository at:
> > 
> >   git://git.pengutronix.de/git/ukl/linux.git tags/timer-for-tip
> > 
> > for you to fetch changes up to b58c4b70121070f45eefa739ecbb58d57da1c4d9:
> > 
> >   ARM: at91: rm9200: switch back to clockevents_config_and_register (2013-10-08 16:58:22 +0200)
> > 
> > ----------------------------------------------------------------
> > The first patch in this pull request fixes an old integer rounding bug in the
> > timer core that was (and still is) worked around in a few arch's timer code.
> > The second patch drops such a work around now that the core is fixed.
> It would be nice to get that fix into 3.12, but Thomas stopped
> responding nearly a month ago---his last mail in this thread[1] dates back
> to September 20.
Still no sign of life from Thomas.
 
> Can one of you please take it directly?
Daniel, would you care to take it?

Best regards
Uwe
Daniel Lezcano Oct. 21, 2013, 8:53 p.m. UTC | #8
On 10/21/2013 09:12 AM, Uwe Kleine-König wrote:
> Hello,
>
> [added Daniel Lezcano to To:]
>
> On Mon, Oct 14, 2013 at 09:34:37AM +0200, Uwe Kleine-König wrote:
>> On Tue, Oct 08, 2013 at 05:31:47PM +0200, Uwe Kleine-König wrote:
>>>> What's the status of this patch?
>>> I pinged Thomas about it already via irc, but I didn't get a response up
>>> to now. It would be great to get that into 3.12. To make it easy for
>>> Thomas, here is a pull request that includes the fix and converts
>>> at91rm9200 back to clockevents_config_and_register dropping one of the
>>> workarounds for the fixed bug.

[ ... ]

>>> ----------------------------------------------------------------
>>> The first patch in this pull request fixes an old integer rounding bug in the
>>> timer core that was (and still is) worked around in a few arch's timer code.
>>> The second patch drops such a work around now that the core is fixed.
>> It would be nice to get that fix into 3.12, but Thomas stopped
>> responding nearly a month ago---his last mail in this thread[1] dates back
>> to September 20.
> Still no sign of life from Thomas.
>
>> Can one of you please take it directly?
> Daniel, would you care to take it?

Yes, sure.

   -- Daniel
diff mbox

Patch

diff --git a/kernel/time/clockevents.c b/kernel/time/clockevents.c
index 38959c8..662c579 100644
--- a/kernel/time/clockevents.c
+++ b/kernel/time/clockevents.c
@@ -33,29 +33,64 @@  struct ce_unbind {
 	int res;
 };
 
-/**
- * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
- * @latch:	value to convert
- * @evt:	pointer to clock event device descriptor
- *
- * Math helper, returns latch value converted to nanoseconds (bound checked)
- */
-u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+static u64 cev_delta2ns(unsigned long latch, struct clock_event_device *evt,
+			bool ismax)
 {
 	u64 clc = (u64) latch << evt->shift;
+	u64 rnd;
 
 	if (unlikely(!evt->mult)) {
 		evt->mult = 1;
 		WARN_ON(1);
 	}
+	rnd = (u64) evt->mult - 1;
+
+	/*
+	 * Upper bound sanity check. If the backwards conversion is
+	 * not equal latch, we know that the above shift overflowed.
+	 */
+	if ((clc >> evt->shift) != (u64)latch)
+		clc = ~0ULL;
+
+	/*
+	 * Scaled math oddities:
+	 *
+	 * For mult <= (1 << shift) we can safely add mult - 1 to
+	 * prevent integer rounding loss. So the backwards conversion
+	 * from nsec to device ticks will be correct.
+	 *
+	 * For mult > (1 << shift), i.e. device frequency is > 1GHz we
+	 * need to be careful. Adding mult - 1 will result in a value
+	 * which when converted back to device ticks can be larger
+	 * than latch by up to (mult - 1) >> shift. For the min_delta
+	 * calculation we still want to apply this in order to stay
+	 * above the minimum device ticks limit. For the upper limit
+	 * we would end up with a latch value larger than the upper
+	 * limit of the device, so we omit the add to stay below the
+	 * device upper boundary.
+	 *
+	 * Also omit the add if it would overflow the u64 boundary.
+	 */
+	if ((~0ULL - clc > rnd) &&
+	    (!ismax || evt->mult <= (1U << evt->shift)))
+		clc += rnd;
 
 	do_div(clc, evt->mult);
-	if (clc < 1000)
-		clc = 1000;
-	if (clc > KTIME_MAX)
-		clc = KTIME_MAX;
 
-	return clc;
+	/* Deltas less than 1usec are pointless noise */
+	return clc > 1000 ? clc : 1000;
+}
+
+/**
+ * clockevents_delta2ns - Convert a latch value (device ticks) to nanoseconds
+ * @latch:	value to convert
+ * @evt:	pointer to clock event device descriptor
+ *
+ * Math helper, returns latch value converted to nanoseconds (bound checked)
+ */
+u64 clockevent_delta2ns(unsigned long latch, struct clock_event_device *evt)
+{
+	return cev_delta2ns(latch, evt, false);
 }
 EXPORT_SYMBOL_GPL(clockevent_delta2ns);
 
@@ -380,8 +415,8 @@  void clockevents_config(struct clock_event_device *dev, u32 freq)
 		sec = 600;
 
 	clockevents_calc_mult_shift(dev, freq, sec);
-	dev->min_delta_ns = clockevent_delta2ns(dev->min_delta_ticks, dev);
-	dev->max_delta_ns = clockevent_delta2ns(dev->max_delta_ticks, dev);
+	dev->min_delta_ns = cev_delta2ns(dev->min_delta_ticks, dev, false);
+	dev->max_delta_ns = cev_delta2ns(dev->max_delta_ticks, dev, true);
 }
 
 /**