From patchwork Mon Oct 31 21:40:28 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: John Stultz X-Patchwork-Id: 4881 Return-Path: X-Original-To: patchwork@peony.canonical.com Delivered-To: patchwork@peony.canonical.com Received: from fiordland.canonical.com (fiordland.canonical.com [91.189.94.145]) by peony.canonical.com (Postfix) with ESMTP id 06FD523E05 for ; Mon, 31 Oct 2011 21:41:53 +0000 (UTC) Received: from mail-fx0-f52.google.com (mail-fx0-f52.google.com [209.85.161.52]) by fiordland.canonical.com (Postfix) with ESMTP id E4A04A18362 for ; Mon, 31 Oct 2011 21:41:52 +0000 (UTC) Received: by faan26 with SMTP id n26so9129878faa.11 for ; Mon, 31 Oct 2011 14:41:52 -0700 (PDT) Received: by 10.223.16.82 with SMTP id n18mr32000720faa.2.1320097312689; Mon, 31 Oct 2011 14:41:52 -0700 (PDT) X-Forwarded-To: linaro-patchwork@canonical.com X-Forwarded-For: patch@linaro.org linaro-patchwork@canonical.com Delivered-To: patches@linaro.org Received: by 10.152.1.71 with SMTP id 7cs48045lak; Mon, 31 Oct 2011 14:41:52 -0700 (PDT) Received: by 10.224.18.210 with SMTP id x18mr13120235qaa.80.1320097310293; Mon, 31 Oct 2011 14:41:50 -0700 (PDT) Received: from e2.ny.us.ibm.com (e2.ny.us.ibm.com. [32.97.182.142]) by mx.google.com with ESMTPS id q17si10791259vcv.82.2011.10.31.14.41.49 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 31 Oct 2011 14:41:50 -0700 (PDT) Received-SPF: pass (google.com: domain of jstultz@us.ibm.com designates 32.97.182.142 as permitted sender) client-ip=32.97.182.142; Authentication-Results: mx.google.com; spf=pass (google.com: domain of jstultz@us.ibm.com designates 32.97.182.142 as permitted sender) smtp.mail=jstultz@us.ibm.com Received: from /spool/local by e2.ny.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Mon, 31 Oct 2011 17:41:15 -0400 Received: from d01relay04.pok.ibm.com ([9.56.227.236]) by e2.ny.us.ibm.com ([192.168.1.102]) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Mon, 31 Oct 2011 17:40:43 -0400 Received: from d03av01.boulder.ibm.com (d03av01.boulder.ibm.com [9.17.195.167]) by d01relay04.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p9VLehvV206900; Mon, 31 Oct 2011 17:40:43 -0400 Received: from d03av01.boulder.ibm.com (loopback [127.0.0.1]) by d03av01.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p9VLegGQ005702; Mon, 31 Oct 2011 15:40:42 -0600 Received: from kernel.beaverton.ibm.com ([9.47.67.96]) by d03av01.boulder.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p9VLefsq005203; Mon, 31 Oct 2011 15:40:42 -0600 Received: by kernel.beaverton.ibm.com (Postfix, from userid 1056) id 60F7E1E74FB; Mon, 31 Oct 2011 14:40:33 -0700 (PDT) From: John Stultz To: LKML Cc: John Stultz , Yong Zhang , David Daney , Thomas Gleixner Subject: [RFC][PATCH] clocksource: Avoid selecting mult values that might overflow when adjusted Date: Mon, 31 Oct 2011 14:40:28 -0700 Message-Id: <1320097228-3612-1-git-send-email-john.stultz@linaro.org> X-Mailer: git-send-email 1.7.3.2.146.gca209 x-cbid: 11103121-5112-0000-0000-00000177EE6A Here's another rev of my earlier patch. Let me know if it helps. -john For some frequqencies, the clocks_calc_mult_shift() function will unfortunately select mult values very close to 0xffffffff. This has the potential to overflow when NTP adjusts the clock, adding to the mult value. This patch adds a clocksource.maxadj value, which provides an approximation of an 11% adjustment(NTP limits adjustments to 500ppm and the tick adjustment is limited to 10%), which could be made to the clocksource.mult value. This is then used to both check that the current mult value won't overflow/underflow, as well as warning us if the timekeeping_adjust() code pushes over that 11% boundary. UNTESTED! NOT FOR INCLUSION (YET)! CC: Yong Zhang CC: David Daney CC: Thomas Gleixner Reported-by: Chen Jie Reported-by: zhangfx Signed-off-by: John Stultz --- include/linux/clocksource.h | 3 ++- kernel/time/clocksource.c | 37 +++++++++++++++++++++++++++---------- kernel/time/timekeeping.c | 3 +++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h index 139c4db..c86c940 100644 --- a/include/linux/clocksource.h +++ b/include/linux/clocksource.h @@ -156,6 +156,7 @@ extern u64 timecounter_cyc2time(struct timecounter *tc, * @mult: cycle to nanosecond multiplier * @shift: cycle to nanosecond divisor (power of two) * @max_idle_ns: max idle time permitted by the clocksource (nsecs) + * @maxadj maximum adjustment value to mult (~11%) * @flags: flags describing special properties * @archdata: arch-specific data * @suspend: suspend function for the clocksource, if necessary @@ -172,7 +173,7 @@ struct clocksource { u32 mult; u32 shift; u64 max_idle_ns; - + u32 maxadj; #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA struct arch_clocksource_data archdata; #endif diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c index cf52fda..2982996 100644 --- a/kernel/time/clocksource.c +++ b/kernel/time/clocksource.c @@ -503,25 +503,26 @@ static u64 clocksource_max_deferment(struct clocksource *cs) /* * Calculate the maximum number of cycles that we can pass to the * cyc2ns function without overflowing a 64-bit signed result. The - * maximum number of cycles is equal to ULLONG_MAX/cs->mult which - * is equivalent to the below. - * max_cycles < (2^63)/cs->mult - * max_cycles < 2^(log2((2^63)/cs->mult)) - * max_cycles < 2^(log2(2^63) - log2(cs->mult)) - * max_cycles < 2^(63 - log2(cs->mult)) - * max_cycles < 1 << (63 - log2(cs->mult)) + * maximum number of cycles is equal to ULLONG_MAX/(cs->mult+cs->maxadj) + * which is equivalent to the below. + * max_cycles < (2^63)/(cs->mult + cs->maxadj) + * max_cycles < 2^(log2((2^63)/(cs->mult + cs->maxadj))) + * max_cycles < 2^(log2(2^63) - log2(cs->mult + cs->maxadj)) + * max_cycles < 2^(63 - log2(cs->mult + cs->maxadj)) + * max_cycles < 1 << (63 - log2(cs->mult + cs->maxadj)) * Please note that we add 1 to the result of the log2 to account for * any rounding errors, ensure the above inequality is satisfied and * no overflow will occur. */ - max_cycles = 1ULL << (63 - (ilog2(cs->mult) + 1)); + max_cycles = 1ULL << (63 - (ilog2(cs->mult + cs->maxadj) + 1)); /* * The actual maximum number of cycles we can defer the clocksource is * determined by the minimum of max_cycles and cs->mask. */ max_cycles = min_t(u64, max_cycles, (u64) cs->mask); - max_nsecs = clocksource_cyc2ns(max_cycles, cs->mult, cs->shift); + max_nsecs = clocksource_cyc2ns(max_cycles, cs->mult+cs->maxadj, + cs->shift); /* * To ensure that the clocksource does not wrap whilst we are idle, @@ -640,7 +641,6 @@ static void clocksource_enqueue(struct clocksource *cs) void __clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq) { u64 sec; - /* * Calc the maximum number of seconds which we can run before * wrapping around. For clocksources which have a mask > 32bit @@ -661,6 +661,23 @@ void __clocksource_updatefreq_scale(struct clocksource *cs, u32 scale, u32 freq) clocks_calc_mult_shift(&cs->mult, &cs->shift, freq, NSEC_PER_SEC / scale, sec * scale); + + /* + * for clocksources that have large mults, to avoid overflow. + * Since mult may be adjusted by ntp, add an safety extra margin + * + * We won't try to correct for more then 11% adjustments (110,000 ppm), + * which approximates to 1/8 or 1/2^3. Thus 1 << (shift - 3) is the + * largest mult adjustment we'll support. + */ + cs->maxadj = 1 << (cs->shift-3); + while ((cs->mult + cs->maxadj < cs->mult) + || (cs->mult - cs->maxadj > cs->mult)) { + cs->mult >>= 1; + cs->shift--; + cs->maxadj = 1 << (cs->shift-3); + } + cs->max_idle_ns = clocksource_max_deferment(cs); } EXPORT_SYMBOL_GPL(__clocksource_updatefreq_scale); diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 2b021b0e..d37c9e3 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -820,6 +820,9 @@ static void timekeeping_adjust(s64 offset) } else return; + WARN_ONCE(timekeeper.mult+adj > + timekeeper.clock->mult + timekeeper.clock->maxadj, + "Adjusting more then 11%%"); timekeeper.mult += adj; timekeeper.xtime_interval += interval; timekeeper.xtime_nsec -= offset;