• Thomas Gleixner's avatar
    clockevents: Sanitize ticks to nsec conversion · 97b94106
    Thomas Gleixner authored
    Marc Kleine-Budde pointed out, that commit 77cc982f "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.
    
    [ukl: move assignment to rnd after eventually changing mult, fix build
     issue and correct comment with the right math]
    Signed-off-by: default avatarThomas 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>
    Cc: stable@vger.kernel.org
    Link: http://lkml.kernel.org/r/1380052223-24139-1-git-send-email-u.kleine-koenig@pengutronix.deSigned-off-by: default avatarUwe Kleine-König <u.kleine-koenig@pengutronix.de>
    97b94106
clockevents.c 17.7 KB