Skip to contentSkip to conversation

Introduce CLOCK_BOOTTIME

PATCH In review John Stultz 2 participants 8 messages Feb 16, 2011

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

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

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve HjønnevÃ¥g <[email protected]>

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
#1 lore raw

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.

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve Hjønnevåg <[email protected]>
---
 include/linux/hrtimer.h |    6 +++++-
 kernel/hrtimer.c        |   40 +++++++++++++++++++++++++++-------------
 2 files changed, 32 insertions(+), 14 deletions(-)
include/linux/hrtimer.h +5 -1
index f376ddc..20b8e66 100644
@@ -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
index 57c4d33..ca99e24 100644
@@ -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
#2 lore raw

PATCH 2/5 time: Introduce get_monotonic_boottime and ktime_get_boottime

In reply to John Stultz

This adds new functions that return the monotonic time since boot (in other words, CLOCK_MONOTONIC + suspend time).

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve HjønnevÃ¥g <[email protected]>
---
 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
index 20b8e66..7a9e7ee 100644
@@ -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
index 379b903..fa39150 100644
@@ -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
#3 lore raw

PATCH 3/5 time: Extend get_xtime_and_monotonic_offset() to also return sleep

In reply to John Stultz

Extend get_xtime_and_monotonic_offset to get_xtime_and_monotonic_and_sleep_offset().

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve HjønnevÃ¥g <[email protected]>
---
 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
index fa39150..02d48fb 100644
@@ -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
index ca99e24..e8bf3ad 100644
@@ -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
index 5fbd9aa..29f75aa 100644
@@ -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
#4 lore raw

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
index 5fbd9aa..29f75aa 100644
@@ -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

#5 lore raw

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
index 5fbd9aa..29f75aa 100644
@@ -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

#6 lore raw

PATCH 4/5 timers: Add CLOCK_BOOTTIME hrtimer base

In reply to John Stultz

CLOCK_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.

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve HjønnevÃ¥g <[email protected]>
---
 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
index 7a9e7ee..6bc1804 100644
@@ -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
index 02d48fb..454a262 100644
@@ -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
index e8bf3ad..4c53af1 100644
@@ -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
#7 lore raw

PATCH 5/5 timers: Export CLOCK_BOOTTIME via the posix timers interface

In reply to John Stultz

This patch exports CLOCK_BOOTTIME through the posix timers interface

CC Jamie Lokier <[email protected]>
CC Thomas Gleixner <[email protected]>
CC Alexander Shishkin <[email protected]>
CC Arve HjønnevÃ¥g <[email protected]>
---
 kernel/posix-timers.c |   21 ++++++++++++++++++++-
 1 files changed, 20 insertions(+), 1 deletions(-)
kernel/posix-timers.c +20 -1
index 44fcff1..4c01249 100644
@@ -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