From patchwork Thu Jul 5 16:08:05 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Brad Figg X-Patchwork-Id: 169216 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from chlorine.canonical.com (chlorine.canonical.com [91.189.94.204]) by ozlabs.org (Postfix) with ESMTP id 255522C0316 for ; Fri, 6 Jul 2012 02:08:27 +1000 (EST) Received: from localhost ([127.0.0.1] helo=chlorine.canonical.com) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1SmobK-0008MI-NK; Thu, 05 Jul 2012 16:08:18 +0000 Received: from youngberry.canonical.com ([91.189.89.112]) by chlorine.canonical.com with esmtp (Exim 4.71) (envelope-from ) id 1SmobI-0008LY-Mm for kernel-team@lists.ubuntu.com; Thu, 05 Jul 2012 16:08:16 +0000 Received: from static-50-53-107-235.bvtn.or.frontiernet.net ([50.53.107.235] helo=localhost) by youngberry.canonical.com with esmtpsa (TLS1.0:DHE_RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1SmobI-000514-9k for kernel-team@lists.ubuntu.com; Thu, 05 Jul 2012 16:08:16 +0000 From: Brad Figg To: kernel-team@lists.ubuntu.com Subject: [PATCH 3/3] [QUANTAL] (pre-upstream) hrtimer: Update hrtimer base offsets each hrtimer_interrupt Date: Thu, 5 Jul 2012 09:08:05 -0700 Message-Id: <1341504485-25761-4-git-send-email-brad.figg@canonical.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1341504485-25761-1-git-send-email-brad.figg@canonical.com> References: <1341504485-25761-1-git-send-email-brad.figg@canonical.com> X-BeenThere: kernel-team@lists.ubuntu.com X-Mailman-Version: 2.1.13 Precedence: list List-Id: Kernel team discussions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: kernel-team-bounces@lists.ubuntu.com Errors-To: kernel-team-bounces@lists.ubuntu.com From: John Stultz BugLink: http://bugs.launchpad.net/bugs/1020285 This patch introduces a new funciton which captures the CLOCK_MONOTONIC time, along with the CLOCK_REALTIME and CLOCK_BOOTTIME offsets at the same moment. This new function is then used in place of ktime_get() when hrtimer_interrupt() is expiring timers. This ensures that any changes to realtime or boottime offsets are noticed and stored into the per-cpu hrtimer base structures, prior to doing any hrtimer expiration. This should ensure that timers are not expired early if the offsets changes under us. This is useful in the case where clock_was_set() is called from atomic context and have to schedule the hrtimer base offset update via a timer, as it provides extra robustness in the face of any possible timer delay. CC: Prarit Bhargava CC: stable@vger.kernel.org CC: Thomas Gleixner CC: linux@openhuawei.org Signed-off-by: John Stultz Signed-off-by: Brad Figg --- include/linux/hrtimer.h | 3 +++ kernel/hrtimer.c | 14 +++++++++++--- kernel/time/timekeeping.c | 34 ++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 3 deletions(-) diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index fd0dc30..f6b2a74 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -320,6 +320,9 @@ extern ktime_t ktime_get(void); extern ktime_t ktime_get_real(void); extern ktime_t ktime_get_boottime(void); extern ktime_t ktime_get_monotonic_offset(void); +extern void ktime_get_and_real_and_sleep_offset(ktime_t *monotonic, + ktime_t *real_offset, + ktime_t *sleep_offset); DECLARE_PER_CPU(struct tick_device, tick_cpu_device); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index d730678..56600c4 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -1258,18 +1258,26 @@ static void __run_hrtimer(struct hrtimer *timer, ktime_t *now) void hrtimer_interrupt(struct clock_event_device *dev) { struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases); - ktime_t expires_next, now, entry_time, delta; + ktime_t expires_next, now, entry_time, delta, real_offset, sleep_offset; int i, retries = 0; BUG_ON(!cpu_base->hres_active); cpu_base->nr_events++; dev->next_event.tv64 = KTIME_MAX; - entry_time = now = ktime_get(); + + ktime_get_and_real_and_sleep_offset(&now, &real_offset, &sleep_offset); + + entry_time = now; retry: expires_next.tv64 = KTIME_MAX; raw_spin_lock(&cpu_base->lock); + + /* Update base offsets, to avoid early wakeups */ + cpu_base->clock_base[HRTIMER_BASE_REALTIME].offset = real_offset; + cpu_base->clock_base[HRTIMER_BASE_BOOTTIME].offset = sleep_offset; + /* * We set expires_next to KTIME_MAX here with cpu_base->lock * held to prevent that a timer is enqueued in our queue via @@ -1346,7 +1354,7 @@ retry: * interrupt routine. We give it 3 attempts to avoid * overreacting on some spurious event. */ - now = ktime_get(); + ktime_get_and_real_and_sleep_offset(&now, &real_offset, &sleep_offset); cpu_base->nr_retries++; if (++retries < 3) goto retry; diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index cc2991d..b3404cf 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c @@ -1251,6 +1251,40 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim, } /** + * ktime_get_and_real_and_sleep_offset() - hrtimer helper, gets monotonic ktime, + * realtime offset, and sleep offsets. + */ +void ktime_get_and_real_and_sleep_offset(ktime_t *monotonic, + ktime_t *real_offset, + ktime_t *sleep_offset) +{ + unsigned long seq; + struct timespec wtom, sleep; + u64 secs, nsecs; + + do { + seq = read_seqbegin(&timekeeper.lock); + + secs = timekeeper.xtime.tv_sec + + timekeeper.wall_to_monotonic.tv_sec; + nsecs = timekeeper.xtime.tv_nsec + + timekeeper.wall_to_monotonic.tv_nsec; + nsecs += timekeeping_get_ns(); + /* If arch requires, add in gettimeoffset() */ + nsecs += arch_gettimeoffset(); + + wtom = timekeeper.wall_to_monotonic; + sleep = timekeeper.total_sleep_time; + } while (read_seqretry(&timekeeper.lock, seq)); + + *monotonic = ktime_add_ns(ktime_set(secs, 0), nsecs); + set_normalized_timespec(&wtom, -wtom.tv_sec, -wtom.tv_nsec); + *real_offset = timespec_to_ktime(wtom); + *sleep_offset = timespec_to_ktime(sleep); +} + + +/** * ktime_get_monotonic_offset() - get wall_to_monotonic in ktime_t format */ ktime_t ktime_get_monotonic_offset(void)