]> www.infradead.org Git - users/jedix/linux-maple.git/commitdiff
3.0.x: hrtimer: Update hrtimer base offsets each hrtimer_interrupt
authorJohn Stultz <johnstul@us.ibm.com>
Mon, 2 Jul 2012 23:28:54 +0000 (19:28 -0400)
committerJoe Jin <joe.jin@oracle.com>
Fri, 6 Jul 2012 11:01:29 +0000 (19:01 +0800)
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 <prarit@redhat.com>
CC: stable@vger.kernel.org
CC: Thomas Gleixner <tglx@linutronix.de>
Acked-by: Prarit Bhargava <prarit@redhat.com>
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Backported-by: Joe Jin <joe.jin@oracle.com>
include/linux/hrtimer.h
kernel/hrtimer.c
kernel/time/timekeeping.c

index fd0dc30c9f154af94155b8c8c47e0a228fbd2573..f6b2a74bfc9f14bcb7ae490490cd6380078a40fc 100644 (file)
@@ -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);
 
index 250b2b5205a3e05c53d31abe9f1a8ef9a663bbff..70e9951627c5db5a7f92d8d7e6bef8b8e395b189 100644 (file)
@@ -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;
index 0c433a0b331c60b582d14bf800d04b4031b30ba0..8933d4901df6ab518b9097f395e19ba2d0015a3d 100644 (file)
@@ -1103,6 +1103,40 @@ void get_xtime_and_monotonic_and_sleep_offset(struct timespec *xtim,
        } while (read_seqretry(&xtime_lock, seq));
 }
 
+/**
+ * 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(&xtime_lock);
+
+               secs = xtime.tv_sec +
+                               wall_to_monotonic.tv_sec;
+               nsecs = xtime.tv_nsec +
+                               wall_to_monotonic.tv_nsec;
+               nsecs += timekeeping_get_ns();
+               /* If arch requires, add in gettimeoffset() */
+               nsecs += arch_gettimeoffset();
+
+               wtom = wall_to_monotonic;
+               sleep = total_sleep_time;
+       } while (read_seqretry(&xtime_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
  */