| From ed4f20943cd4c7b55105c04daedf8d63ab6d499c Mon Sep 17 00:00:00 2001 |
| From: Heiko Carstens <heiko.carstens@de.ibm.com> |
| Date: Mon, 14 Jan 2013 16:55:55 +0100 |
| Subject: s390/time: fix sched_clock() overflow |
| |
| From: Heiko Carstens <heiko.carstens@de.ibm.com> |
| |
| commit ed4f20943cd4c7b55105c04daedf8d63ab6d499c upstream. |
| |
| Converting a 64 Bit TOD format value to nanoseconds means that the value |
| must be divided by 4.096. In order to achieve that we multiply with 125 |
| and divide by 512. |
| When used within sched_clock() this triggers an overflow after appr. |
| 417 days. Resulting in a sched_clock() return value that is much smaller |
| than previously and therefore may cause all sort of weird things in |
| subsystems that rely on a monotonic sched_clock() behaviour. |
| |
| To fix this implement a tod_to_ns() helper function which converts TOD |
| values without overflow and call this function from both places that |
| open coded the conversion: sched_clock() and kvm_s390_handle_wait(). |
| |
| Reviewed-by: Martin Schwidefsky <schwidefsky@de.ibm.com> |
| Signed-off-by: Heiko Carstens <heiko.carstens@de.ibm.com> |
| Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| arch/s390/include/asm/timex.h | 28 ++++++++++++++++++++++++++++ |
| arch/s390/kernel/time.c | 2 +- |
| arch/s390/kvm/interrupt.c | 2 +- |
| 3 files changed, 30 insertions(+), 2 deletions(-) |
| |
| --- a/arch/s390/include/asm/timex.h |
| +++ b/arch/s390/include/asm/timex.h |
| @@ -126,4 +126,32 @@ static inline unsigned long long get_clo |
| return get_clock_xt() - sched_clock_base_cc; |
| } |
| |
| +/** |
| + * tod_to_ns - convert a TOD format value to nanoseconds |
| + * @todval: to be converted TOD format value |
| + * Returns: number of nanoseconds that correspond to the TOD format value |
| + * |
| + * Converting a 64 Bit TOD format value to nanoseconds means that the value |
| + * must be divided by 4.096. In order to achieve that we multiply with 125 |
| + * and divide by 512: |
| + * |
| + * ns = (todval * 125) >> 9; |
| + * |
| + * In order to avoid an overflow with the multiplication we can rewrite this. |
| + * With a split todval == 2^32 * th + tl (th upper 32 bits, tl lower 32 bits) |
| + * we end up with |
| + * |
| + * ns = ((2^32 * th + tl) * 125 ) >> 9; |
| + * -> ns = (2^23 * th * 125) + ((tl * 125) >> 9); |
| + * |
| + */ |
| +static inline unsigned long long tod_to_ns(unsigned long long todval) |
| +{ |
| + unsigned long long ns; |
| + |
| + ns = ((todval >> 32) << 23) * 125; |
| + ns += ((todval & 0xffffffff) * 125) >> 9; |
| + return ns; |
| +} |
| + |
| #endif |
| --- a/arch/s390/kernel/time.c |
| +++ b/arch/s390/kernel/time.c |
| @@ -63,7 +63,7 @@ static DEFINE_PER_CPU(struct clock_event |
| */ |
| unsigned long long notrace __kprobes sched_clock(void) |
| { |
| - return (get_clock_monotonic() * 125) >> 9; |
| + return tod_to_ns(get_clock_monotonic()); |
| } |
| |
| /* |
| --- a/arch/s390/kvm/interrupt.c |
| +++ b/arch/s390/kvm/interrupt.c |
| @@ -358,7 +358,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu |
| return 0; |
| } |
| |
| - sltime = ((vcpu->arch.sie_block->ckc - now)*125)>>9; |
| + sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); |
| |
| hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL); |
| VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime); |