Introduce CLOCK_BOOTTIME
Subsystems (2)
cover Introduce CLOCK_BOOTTIME
This patch set (applies against tip/timers/core) extends the hrtimer, timekeeping, and posix-timer code to support a new
clockid: CLOCK_BOOTTIME.
CLOCK_BOOTTIME is identical to CLOCK_MONOTONIC, except it also includes any time spent in suspend (as currently measured by read_persistent_clock()). This allows applications to get a suspend aware monotonic clock.
Thomas, I've included your suggestions from the last round. Does this look like something you could queue for 2.6.39?
thanks
-john
John Stultz (5):
hrtimers: extend hrtimer base code to handle more then 2 clockids
time: Introduce get_monotonic_boottime and ktime_get_boottime
time: Extend get_xtime_and_monotonic_offset() to also return sleep
timers: Add CLOCK_BOOTTIME hrtimer base
timers: Export CLOCK_BOOTTIME via the posix timers interface
include/linux/hrtimer.h | 8 +++++- include/linux/time.h | 5 +++- kernel/hrtimer.c | 63 ++++++++++++++++++++++++++++++-------------- kernel/posix-timers.c | 21 ++++++++++++++- kernel/time/timekeeping.c | 56 ++++++++++++++++++++++++++++++++++++++- 5 files changed, 128 insertions(+), 25 deletions(-)
Signature · 1 line
1.7.3.2.146.gca209
PATCH 1/5 hrtimers: extend hrtimer base code to handle more then 2 clockids
The hrtimer code is written mainly with CLOCK_REALTIME and CLOCK_MONOTONIC in mind. These are clockids 0 and 1 resepctively. However, if we are to introduce any new hrtimer bases, using new clockids, we have to skip the cputimers (clockids 2,3) as well as other clockids that may not impelement timers.
This patch adds a little bit of indirection between the clockid and the base, so that we can extend the base by one when we add a new clockid at number 7 or so.
--- include/linux/hrtimer.h | 6 +++++- kernel/hrtimer.c | 40 +++++++++++++++++++++++++++------------- 2 files changed, 32 insertions(+), 14 deletions(-)
include/linux/hrtimer.h +5 -1
@@ -148,7 +148,11 @@ struct hrtimer_clock_base {#endif }; -#define HRTIMER_MAX_CLOCK_BASES 2 +enum hrtimer_base_type { +HRTIMER_BASE_REALTIME , +HRTIMER_BASE_MONOTONIC , +HRTIMER_MAX_CLOCK_BASES , +};/* * struct hrtimer_cpu_base - the per cpu clock bases
kernel/hrtimer.c +27 -13
@@ -53,11 +53,10 @@/* * The timer bases: * -* Note: If we want to add new timer bases, we have to skip the two -* clock ids captured by the cpu-timers. We do this by holding empty -* entries rather than doing math adjustment of the clock ids. -* This ensures that we capture erroneous accesses to these clock ids -* rather than moving them into the range of valid clock id's. +* There are more clockids then hrtimer bases. Thus, we index +* into the timer bases by the hrtimer_base_type enum. When trying +* to reach a base using a clockid, hrtimer_clockid_to_base() +* is used to convert from clockid to the proper hrtimer_base_type. */ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = {
@@ -77,6 +76,14 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = } }; +static int hrtimer_clock_to_base_table[MAX_CLOCKS ]; + +static inline int hrtimer_clockid_to_base (clockid_t clock_id) +{ +return hrtimer_clock_to_base_table[clock_id]; +} + +/* * Get the coarse grained time at the softirq based on xtime and * wall_to_monotonic.
@@ -90,8 +97,8 @@ static void hrtimer_get_softirq_time(struct hrtimer_cpu_base *base) xtim =timespec_to_ktime (xts); tomono =timespec_to_ktime (tom); - base->clock_base[CLOCK_REALTIME ].softirq_time = xtim; - base->clock_base[CLOCK_MONOTONIC ].softirq_time = + base->clock_base[HRTIMER_BASE_REALTIME ].softirq_time = xtim; + base->clock_base[HRTIMER_BASE_MONOTONIC ].softirq_time =ktime_add (xtim, tomono); }
@@ -179,10 +186,11 @@ switch_hrtimer_base(struct hrtimer *timer, struct hrtimer_clock_base *base,struct hrtimer_cpu_base *new_cpu_base;int this_cpu =smp_processor_id ();int cpu =hrtimer_get_target (this_cpu, pinned); +int basenum =hrtimer_clockid_to_base (base->index); again: new_cpu_base = &per_cpu (hrtimer_bases, cpu); - new_base = &new_cpu_base->clock_base[base->index]; + new_base = &new_cpu_base->clock_base[basenum];if (base != new_base) {/*
@@ -618,7 +626,7 @@ static void retrigger_next_event(void *arg)/* Adjust CLOCK_REALTIME offset */ raw_spin_lock (&base->lock); - base->clock_base[CLOCK_REALTIME ].offset = + base->clock_base[HRTIMER_BASE_REALTIME ].offset =timespec_to_ktime (realtime_offset);hrtimer_force_reprogram (base,0 );
@@ -716,8 +724,8 @@ static int hrtimer_switch_to_hres(void)return 0 ; } base->hres_active =1 ; - base->clock_base[CLOCK_REALTIME ].resolution =KTIME_HIGH_RES ; - base->clock_base[CLOCK_MONOTONIC ].resolution =KTIME_HIGH_RES ; + base->clock_base[HRTIMER_BASE_REALTIME ].resolution =KTIME_HIGH_RES ; + base->clock_base[HRTIMER_BASE_MONOTONIC ].resolution =KTIME_HIGH_RES ;tick_setup_sched_timer ();
@@ -1112,6 +1120,7 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id, enum hrtimer_mode mode) {struct hrtimer_cpu_base *cpu_base; +int base;memset (timer,0 ,sizeof (struct hrtimer ));
@@ -1120,7 +1129,8 @@ static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,if (clock_id ==CLOCK_REALTIME && mode !=HRTIMER_MODE_ABS ) clock_id =CLOCK_MONOTONIC ; - timer->base = &cpu_base->clock_base[clock_id]; + base =hrtimer_clockid_to_base (clock_id); + timer->base = &cpu_base->clock_base[base];hrtimer_init_timer_hres (timer);timerqueue_init (&timer->node);
@@ -1156,9 +1166,10 @@ EXPORT_SYMBOL_GPL(hrtimer_init);int hrtimer_get_res (const clockid_t which_clock,struct timespec *tp) {struct hrtimer_cpu_base *cpu_base; +int base =hrtimer_clockid_to_base (which_clock); cpu_base = &__raw_get_cpu_var (hrtimer_bases); - *tp =ktime_to_timespec (cpu_base->clock_base[which_clock].resolution); + *tp =ktime_to_timespec (cpu_base->clock_base[base].resolution);return 0 ; }
@@ -1705,6 +1716,9 @@ static struct notifier_block __cpuinitdata hrtimers_nb = {void __init hrtimers_init (void ) { + hrtimer_clock_to_base_table[CLOCK_REALTIME ] =HRTIMER_BASE_REALTIME ; + hrtimer_clock_to_base_table[CLOCK_MONOTONIC ] =HRTIMER_BASE_MONOTONIC ; +hrtimer_cpu_notify (&hrtimers_nb, (unsigned long )CPU_UP_PREPARE , (void *)(long )smp_processor_id ());register_cpu_notifier (&hrtimers_nb);
Signature · 1 line
1.7.3.2.146.gca209
PATCH 2/5 time: Introduce get_monotonic_boottime and ktime_get_boottime
In reply to John StultzThis adds new functions that return the monotonic time since boot (in other words, CLOCK_MONOTONIC + suspend time).
--- include/linux/hrtimer.h | 1 + include/linux/time.h | 1 + kernel/time/timekeeping.c | 51 ++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 52 insertions(+), 1 deletions(-)
include/linux/hrtimer.h +1 -0
@@ -312,6 +312,7 @@ static inline int hrtimer_is_hres_active(struct hrtimer *timer)extern ktime_t ktime_get (void );extern ktime_t ktime_get_real (void ); +extern ktime_t ktime_get_boottime (void ); DECLARE_PER_CPU(struct tick_device, tick_cpu_device);
include/linux/time.h +1 -0
@@ -161,6 +161,7 @@ extern void getnstime_raw_and_real(struct timespec *ts_raw, struct timespec *ts_real);extern void getboottime (struct timespec *ts);extern void monotonic_to_bootbased (struct timespec *ts); +extern void get_monotonic_boottime (struct timespec *ts);extern struct timespec timespec_trunc (struct timespec t,unsigned gran);extern int timekeeping_valid_for_hres (void );
kernel/time/timekeeping.c +50 -1
63 lines not on this page · raw message
Signature · 1 line
1.7.3.2.146.gca209
PATCH 3/5 time: Extend get_xtime_and_monotonic_offset() to also return sleep
In reply to John StultzExtend get_xtime_and_monotonic_offset to get_xtime_and_monotonic_and_sleep_offset().
--- include/linux/time.h | 3 ++- kernel/hrtimer.c | 9 +++++---- kernel/time/timekeeping.c | 5 ++++- 3 files changed, 11 insertions(+), 6 deletions(-)
include/linux/time.h +2 -1
@@ -124,7 +124,8 @@ unsigned long get_seconds(void);struct timespec current_kernel_time (void );struct timespec __current_kernel_time (void );/* does not take xtime_lock */ struct timespec get_monotonic_coarse (void ); -void get_xtime_and_monotonic_offset (struct timespec *xtim,struct timespec *wtom); +void get_xtime_and_monotonic_and_sleep_offset (struct timespec *xtim, +struct timespec *wtom,struct timespec *sleep);#define CURRENT_TIME (current_kernel_time())#define CURRENT_TIME_SEC ((struct timespec) { get_seconds(), 0 })
kernel/hrtimer.c +5 -4
@@ -91,9 +91,9 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id)static void hrtimer_get_softirq_time (struct hrtimer_cpu_base *base) {ktime_t xtim, tomono; -struct timespec xts, tom; +struct timespec xts, tom, slp; -get_xtime_and_monotonic_offset (&xts, &tom); +get_xtime_and_monotonic_and_sleep_offset (&xts, &tom, &slp); xtim =timespec_to_ktime (xts); tomono =timespec_to_ktime (tom);
@@ -614,12 +614,13 @@ static int hrtimer_reprogram(struct hrtimer *timer,static void retrigger_next_event (void *arg) {struct hrtimer_cpu_base *base; -struct timespec realtime_offset, wtm; +struct timespec realtime_offset, wtm, sleep;if (!hrtimer_hres_active ())return ; -get_xtime_and_monotonic_offset (&realtime_offset, &wtm); +get_xtime_and_monotonic_and_sleep_offset (&realtime_offset, &wtm, + &sleep);set_normalized_timespec (&realtime_offset, -wtm.tv_sec, -wtm.tv_nsec); base = &__get_cpu_var (hrtimer_bases);
kernel/time/timekeeping.c +4 -1
@@ -1043,8 +1043,10 @@ void do_timer(unsigned long ticks)* get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic * @xtim: pointer to timespec to be set with xtime * @wtom: pointer to timespec to be set with wall_to_monotonic +* @sleep: pointer to timespec to be set with time in suspend */ -void get_xtime_and_monotonic_offset (struct timespec *xtim,struct timespec *wtom) +void get_xtime_and_monotonic_and_sleep_offset (struct timespec *xtim, +struct timespec *wtom,struct timespec *sleep) {unsigned long seq;
@@ -1052,6 +1054,7 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom seq =read_seqbegin (&xtime_lock); *xtim = xtime; *wtom = wall_to_monotonic; + *sleep = total_sleep_time; }while (read_seqretry (&xtime_lock, seq)); }
Signature · 1 line
1.7.3.2.146.gca209
Re: [PATCH 3/5] time: Extend get_xtime_and_monotonic_offset() to also return sleep
kernel/time/timekeeping.c void do_timer(unsigned long ticks) · line 1043
Quoted 6 lines · John Stultz
kernel/time/timekeeping.c +0
-0@@ -1043,8 +1043,10 @@ void do_timer(unsigned long ticks)* get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic
Can you update the function name in the comment as well.
Quoted 11 lines
kernel/time/timekeeping.c +3
-1* @xtim: pointer to timespec to be set with xtime * @wtom: pointer to timespec to be set with wall_to_monotonic +* @sleep: pointer to timespec to be set with time in suspend */ -void get_xtime_and_monotonic_offset (struct timespec *xtim,struct timespec *wtom) +void get_xtime_and_monotonic_and_sleep_offset (struct timespec *xtim, +struct timespec *wtom,struct timespec *sleep) {unsigned long seq;
kernel/time/timekeeping.c void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom · line 1060
Quoted 7 lines
kernel/time/timekeeping.c +1
-0@@ -1052,6 +1054,7 @@ void get_xtime_and_monotonic_offset(struct timespec *xtim, struct timespec *wtom seq =read_seqbegin (&xtime_lock); *xtim = xtime; *wtom = wall_to_monotonic; + *sleep = total_sleep_time; }while (read_seqretry (&xtime_lock, seq)); }
Thanks,
Jack
Re: [PATCH 3/5] time: Extend get_xtime_and_monotonic_offset() to also return sleep
Quoted 9 lines · Jack Stone
kernel/time/timekeeping.c void do_timer(unsigned long ticks) · line 1043
Quoted 6 lines · John Stultz
kernel/time/timekeeping.c +0
-0@@ -1043,8 +1043,10 @@ void do_timer(unsigned long ticks)* get_xtime_and_monotonic_offset() - get xtime and wall_to_monotonic Can you update the function name in the comment as well.
Thanks for catching that! Will do!
-john
PATCH 4/5 timers: Add CLOCK_BOOTTIME hrtimer base
In reply to John StultzCLOCK_MONOTONIC stops while the system is in suspend. This is because to applications system suspend is invisible. However, there is a growing set of applications that are wanting to be suspend-aware, but do not want to deal with the complications of CLOCK_REALTIME (which might jump around if settimeofday is called).
For these applications, I propose a new clockid: CLOCK_BOOTTIME. CLOCK_BOOTTIME is idential to CLOCK_MONOTONIC, except it also includes any time spent in suspend.
This patch add hrtimer base for CLOCK_BOOTTIME, using get_monotonic_boottime/ktime_get_boottime, to allow in kernel users to set timers against.
--- include/linux/hrtimer.h | 1 + include/linux/time.h | 1 + kernel/hrtimer.c | 16 ++++++++++++---- 3 files changed, 14 insertions(+), 4 deletions(-)
include/linux/hrtimer.h +1 -0
@@ -151,6 +151,7 @@ struct hrtimer_clock_base {enum hrtimer_base_type {HRTIMER_BASE_REALTIME ,HRTIMER_BASE_MONOTONIC , +HRTIMER_BASE_BOOTTIME ,HRTIMER_MAX_CLOCK_BASES , };
include/linux/time.h +1 -0
@@ -293,6 +293,7 @@ struct itimerval {#define CLOCK_MONOTONIC_RAW 4#define CLOCK_REALTIME_COARSE 5#define CLOCK_MONOTONIC_COARSE 6 +#define CLOCK_BOOTTIME 7/* * The IDs of various hardware clocks:
kernel/hrtimer.c +12 -4
@@ -73,6 +73,11 @@ DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = .get_time = &ktime_get, .resolution = KTIME_LOW_RES, }, + { + .index = CLOCK_BOOTTIME, + .get_time = &ktime_get_boottime, + .resolution = KTIME_LOW_RES, + }, } };
@@ -90,16 +95,17 @@ static inline int hrtimer_clockid_to_base(clockid_t clock_id)*/ static void hrtimer_get_softirq_time (struct hrtimer_cpu_base *base) { -ktime_t xtim, tomono; +ktime_t xtim, mono, boot;struct timespec xts, tom, slp;get_xtime_and_monotonic_and_sleep_offset (&xts, &tom, &slp); xtim =timespec_to_ktime (xts); - tomono =timespec_to_ktime (tom); + mono =ktime_add (xtim,timespec_to_ktime (tom)); + boot =ktime_add (mono,timespec_to_ktime (slp)); base->clock_base[HRTIMER_BASE_REALTIME ].softirq_time = xtim; - base->clock_base[HRTIMER_BASE_MONOTONIC ].softirq_time = -ktime_add (xtim, tomono); + base->clock_base[HRTIMER_BASE_MONOTONIC ].softirq_time = mono; + base->clock_base[HRTIMER_BASE_BOOTTIME ].softirq_time = boot; }/*
@@ -727,6 +733,7 @@ static int hrtimer_switch_to_hres(void) base->hres_active =1 ; base->clock_base[HRTIMER_BASE_REALTIME ].resolution =KTIME_HIGH_RES ; base->clock_base[HRTIMER_BASE_MONOTONIC ].resolution =KTIME_HIGH_RES ; + base->clock_base[HRTIMER_BASE_BOOTTIME ].resolution =KTIME_HIGH_RES ;tick_setup_sched_timer ();
@@ -1719,6 +1726,7 @@ void __init hrtimers_init(void) { hrtimer_clock_to_base_table[CLOCK_REALTIME ] =HRTIMER_BASE_REALTIME ; hrtimer_clock_to_base_table[CLOCK_MONOTONIC ] =HRTIMER_BASE_MONOTONIC ; + hrtimer_clock_to_base_table[CLOCK_BOOTTIME ] =HRTIMER_BASE_BOOTTIME ;hrtimer_cpu_notify (&hrtimers_nb, (unsigned long )CPU_UP_PREPARE , (void *)(long )smp_processor_id ());
Signature · 1 line
1.7.3.2.146.gca209
PATCH 5/5 timers: Export CLOCK_BOOTTIME via the posix timers interface
In reply to John StultzThis patch exports CLOCK_BOOTTIME through the posix timers interface
--- kernel/posix-timers.c | 21 ++++++++++++++++++++- 1 files changed, 20 insertions(+), 1 deletions(-)
kernel/posix-timers.c +20 -1
@@ -187,7 +187,7 @@ static int posix_ktime_get_ts(clockid_t which_clock, struct timespec *tp) }/* -* Get monotonic time for posix timers +* Get monotonic-raw time for posix timers */ static int posix_get_monotonic_raw (clockid_t which_clock,struct timespec *tp) {
@@ -214,6 +214,14 @@ static int posix_get_coarse_res(const clockid_t which_clock, struct timespec *tp *tp =ktime_to_timespec (KTIME_LOW_RES );return 0 ; } + +static int posix_get_boottime (const clockid_t which_clock,struct timespec *tp) +{ +get_monotonic_boottime (tp); +return 0 ; +} + +/* * Initialize everything, well, just everything in Posix clocks/timers ;) */
@@ -253,12 +261,23 @@ static __init int init_posix_timers(void) .clock_getres = posix_get_coarse_res, .clock_get = posix_get_monotonic_coarse, }; +struct k_clock clock_boottime = { + .clock_getres = hrtimer_get_res, + .clock_get = posix_get_boottime, + .nsleep = common_nsleep, + .nsleep_restart = hrtimer_nanosleep_restart, + .timer_create = common_timer_create, + .timer_set = common_timer_set, + .timer_get = common_timer_get, + .timer_del = common_timer_del, + };posix_timers_register_clock (CLOCK_REALTIME , &clock_realtime);posix_timers_register_clock (CLOCK_MONOTONIC , &clock_monotonic);posix_timers_register_clock (CLOCK_MONOTONIC_RAW , &clock_monotonic_raw);posix_timers_register_clock (CLOCK_REALTIME_COARSE , &clock_realtime_coarse);posix_timers_register_clock (CLOCK_MONOTONIC_COARSE , &clock_monotonic_coarse); +posix_timers_register_clock (CLOCK_BOOTTIME , &clock_boottime); posix_timers_cache = kmem_cache_create("posix_timers_cache", sizeof (struct k_itimer), 0, SLAB_PANIC,
Signature · 1 line
1.7.3.2.146.gca209