time: Introduce CLOCK_REALTIME_COARSE
authorjohn stultz <johnstul@us.ibm.com>
Thu, 20 Aug 2009 02:13:34 +0000 (19:13 -0700)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 21 Aug 2009 19:43:46 +0000 (21:43 +0200)
After talking with some application writers who want very fast, but not
fine-grained timestamps, I decided to try to implement new clock_ids
to clock_gettime(): CLOCK_REALTIME_COARSE and CLOCK_MONOTONIC_COARSE
which returns the time at the last tick. This is very fast as we don't
have to access any hardware (which can be very painful if you're using
something like the acpi_pm clocksource), and we can even use the vdso
clock_gettime() method to avoid the syscall. The only trade off is you
only get low-res tick grained time resolution.

This isn't a new idea, I know Ingo has a patch in the -rt tree that made
the vsyscall gettimeofday() return coarse grained time when the
vsyscall64 sysctrl was set to 2. However this affects all applications
on a system.

With this method, applications can choose the proper speed/granularity
trade-off for themselves.

Signed-off-by: John Stultz <johnstul@us.ibm.com>
Cc: Andi Kleen <andi@firstfloor.org>
Cc: nikolag@ca.ibm.com
Cc: Darren Hart <dvhltc@us.ibm.com>
Cc: arjan@infradead.org
Cc: jonathan@jonmasters.org
LKML-Reference: <1250734414.6897.5.camel@localhost.localdomain>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
arch/x86/include/asm/vgtod.h
arch/x86/kernel/vsyscall_64.c
arch/x86/vdso/vclock_gettime.c
include/linux/time.h
kernel/posix-timers.c
kernel/time/timekeeping.c

index dc27a69e5d2ab1754153afb9281d6023caa18cd4..3d61e204826f1da5b6cb747957818a0e7b2d3913 100644 (file)
@@ -21,6 +21,7 @@ struct vsyscall_gtod_data {
                u32     shift;
        } clock;
        struct timespec wall_to_monotonic;
+       struct timespec wall_time_coarse;
 };
 extern struct vsyscall_gtod_data __vsyscall_gtod_data
 __section_vsyscall_gtod_data;
index 25ee06a80aad3cd116292227826f9a32fa4b3f7a..cf53a78e2dcf1b6639dd569b3810da809e437e1b 100644 (file)
@@ -87,6 +87,7 @@ void update_vsyscall(struct timespec *wall_time, struct clocksource *clock)
        vsyscall_gtod_data.wall_time_sec = wall_time->tv_sec;
        vsyscall_gtod_data.wall_time_nsec = wall_time->tv_nsec;
        vsyscall_gtod_data.wall_to_monotonic = wall_to_monotonic;
+       vsyscall_gtod_data.wall_time_coarse = __current_kernel_time();
        write_sequnlock_irqrestore(&vsyscall_gtod_data.lock, flags);
 }
 
index 6a40b78b46aafae8273ad7ed12ebbfd44187733a..ee55754cc3c5ff378b76f2065a610b72e757f088 100644 (file)
@@ -86,14 +86,47 @@ notrace static noinline int do_monotonic(struct timespec *ts)
        return 0;
 }
 
+notrace static noinline int do_realtime_coarse(struct timespec *ts)
+{
+       unsigned long seq;
+       do {
+               seq = read_seqbegin(&gtod->lock);
+               ts->tv_sec = gtod->wall_time_coarse.tv_sec;
+               ts->tv_nsec = gtod->wall_time_coarse.tv_nsec;
+       } while (unlikely(read_seqretry(&gtod->lock, seq)));
+       return 0;
+}
+
+notrace static noinline int do_monotonic_coarse(struct timespec *ts)
+{
+       unsigned long seq, ns, secs;
+       do {
+               seq = read_seqbegin(&gtod->lock);
+               secs = gtod->wall_time_coarse.tv_sec;
+               ns = gtod->wall_time_coarse.tv_nsec;
+               secs += gtod->wall_to_monotonic.tv_sec;
+               ns += gtod->wall_to_monotonic.tv_nsec;
+       } while (unlikely(read_seqretry(&gtod->lock, seq)));
+       vset_normalized_timespec(ts, secs, ns);
+       return 0;
+}
+
 notrace int __vdso_clock_gettime(clockid_t clock, struct timespec *ts)
 {
-       if (likely(gtod->sysctl_enabled && gtod->clock.vread))
+       if (likely(gtod->sysctl_enabled))
                switch (clock) {
                case CLOCK_REALTIME:
-                       return do_realtime(ts);
+                       if (likely(gtod->clock.vread))
+                               return do_realtime(ts);
+                       break;
                case CLOCK_MONOTONIC:
-                       return do_monotonic(ts);
+                       if (likely(gtod->clock.vread))
+                               return do_monotonic(ts);
+                       break;
+               case CLOCK_REALTIME_COARSE:
+                       return do_realtime_coarse(ts);
+               case CLOCK_MONOTONIC_COARSE:
+                       return do_monotonic_coarse(ts);
                }
        return vdso_fallback_gettime(clock, ts);
 }
index f505988398e612d0b1d887c5871bce08782e7fcf..256232f7e5e6e3022f9d66b3e228b6aa6d9e8910 100644 (file)
@@ -110,6 +110,8 @@ extern int timekeeping_suspended;
 
 unsigned long get_seconds(void);
 struct timespec current_kernel_time(void);
+struct timespec __current_kernel_time(void); /* does not hold xtime_lock */
+struct timespec get_monotonic_coarse(void);
 
 #define CURRENT_TIME           (current_kernel_time())
 #define CURRENT_TIME_SEC       ((struct timespec) { get_seconds(), 0 })
@@ -243,6 +245,8 @@ struct itimerval {
 #define CLOCK_PROCESS_CPUTIME_ID       2
 #define CLOCK_THREAD_CPUTIME_ID                3
 #define CLOCK_MONOTONIC_RAW            4
+#define CLOCK_REALTIME_COARSE          5
+#define CLOCK_MONOTONIC_COARSE         6
 
 /*
  * The IDs of various hardware clocks:
index d089d052c4a90f18bb908724e93806f86d0e11aa..495440779ce3b91c9927b98c61a83922b8578a31 100644 (file)
@@ -242,6 +242,25 @@ static int posix_get_monotonic_raw(clockid_t which_clock, struct timespec *tp)
        return 0;
 }
 
+
+static int posix_get_realtime_coarse(clockid_t which_clock, struct timespec *tp)
+{
+       *tp = current_kernel_time();
+       return 0;
+}
+
+static int posix_get_monotonic_coarse(clockid_t which_clock,
+                                               struct timespec *tp)
+{
+       *tp = get_monotonic_coarse();
+       return 0;
+}
+
+int posix_get_coarse_res(const clockid_t which_clock, struct timespec *tp)
+{
+       *tp = ktime_to_timespec(KTIME_LOW_RES);
+       return 0;
+}
 /*
  * Initialize everything, well, just everything in Posix clocks/timers ;)
  */
@@ -262,10 +281,26 @@ static __init int init_posix_timers(void)
                .timer_create = no_timer_create,
                .nsleep = no_nsleep,
        };
+       struct k_clock clock_realtime_coarse = {
+               .clock_getres = posix_get_coarse_res,
+               .clock_get = posix_get_realtime_coarse,
+               .clock_set = do_posix_clock_nosettime,
+               .timer_create = no_timer_create,
+               .nsleep = no_nsleep,
+       };
+       struct k_clock clock_monotonic_coarse = {
+               .clock_getres = posix_get_coarse_res,
+               .clock_get = posix_get_monotonic_coarse,
+               .clock_set = do_posix_clock_nosettime,
+               .timer_create = no_timer_create,
+               .nsleep = no_nsleep,
+       };
 
        register_posix_clock(CLOCK_REALTIME, &clock_realtime);
        register_posix_clock(CLOCK_MONOTONIC, &clock_monotonic);
        register_posix_clock(CLOCK_MONOTONIC_RAW, &clock_monotonic_raw);
+       register_posix_clock(CLOCK_REALTIME_COARSE, &clock_realtime_coarse);
+       register_posix_clock(CLOCK_MONOTONIC_COARSE, &clock_monotonic_coarse);
 
        posix_timers_cache = kmem_cache_create("posix_timers_cache",
                                        sizeof (struct k_itimer), 0, SLAB_PANIC,
index 15e06defca5518ca1378c797bbcaa28472e66c72..03cbeb34d141e6ef35b8b7acd3cbbfee6eb4fdba 100644 (file)
@@ -847,6 +847,10 @@ unsigned long get_seconds(void)
 }
 EXPORT_SYMBOL(get_seconds);
 
+struct timespec __current_kernel_time(void)
+{
+       return xtime_cache;
+}
 
 struct timespec current_kernel_time(void)
 {
@@ -862,3 +866,20 @@ struct timespec current_kernel_time(void)
        return now;
 }
 EXPORT_SYMBOL(current_kernel_time);
+
+struct timespec get_monotonic_coarse(void)
+{
+       struct timespec now, mono;
+       unsigned long seq;
+
+       do {
+               seq = read_seqbegin(&xtime_lock);
+
+               now = xtime_cache;
+               mono = wall_to_monotonic;
+       } while (read_seqretry(&xtime_lock, seq));
+
+       set_normalized_timespec(&now, now.tv_sec + mono.tv_sec,
+                               now.tv_nsec + mono.tv_nsec);
+       return now;
+}