[PATCH 10/12] [RFC] hrtimers: Add CLOCK_BOOTTIME clockid, hrtimerbase and posix interface
From: John Stultz
Date: Wed Jan 05 2011 - 21:16:51 EST
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 complicatoins 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 adds the new CLOCK_BOOTTIME clockid, as well as the
infrastructure needed to support hrtimers against it, and the
wiring to expose it out via the posix interface.
CC: Jamie Lokier <jamie@xxxxxxxxxxxxx>
CC: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
CC: Alexander Shishkin <virtuoso@xxxxxxxxx>
CC: Arve HjÃnnevÃg <arve@xxxxxxxxxxx>
Signed-off-by: John Stultz <john.stultz@xxxxxxxxxx>
---
include/linux/hrtimer.h | 2 +
include/linux/time.h | 4 ++
kernel/hrtimer.c | 15 +++++++-
kernel/posix-timers.c | 16 ++++++++-
kernel/time/timekeeping.c | 79 ++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 112 insertions(+), 4 deletions(-)
diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h
index f96c43d..7f22dbb 100644
--- a/include/linux/hrtimer.h
+++ b/include/linux/hrtimer.h
@@ -152,6 +152,7 @@ struct hrtimer_clock_base {
enum hrtimer_base_type {
HRTIMER_BASE_REALTIME,
HRTIMER_BASE_MONOTONIC,
+ HRTIMER_BASE_BOOTTIME,
HRTIMER_MAX_CLOCK_BASES,
};
@@ -314,6 +315,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);
diff --git a/include/linux/time.h b/include/linux/time.h
index 9f15ac7..4ed031a 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -126,6 +126,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_wall_to_monotonic(void); /* does not take xtime_lock */
+extern struct timespec __get_sleep_time(void); /* does not take xtime_lock */
+
struct timespec get_monotonic_coarse(void);
#define CURRENT_TIME (current_kernel_time())
@@ -160,6 +162,7 @@ extern void getnstimeofday(struct timespec *tv);
extern void getrawmonotonic(struct timespec *ts);
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);
@@ -290,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:
diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c
index fb20e08..f507610 100644
--- a/kernel/hrtimer.c
+++ b/kernel/hrtimer.c
@@ -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,
+ },
}
};
@@ -93,21 +98,26 @@ 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;
+ ktime_t xtim, tomono, sleep;
+ struct timespec xts, tom, slp;
unsigned long seq;
do {
seq = read_seqbegin(&xtime_lock);
xts = __current_kernel_time();
tom = __get_wall_to_monotonic();
+ slp = __get_sleep_time();
} while (read_seqretry(&xtime_lock, seq));
xtim = timespec_to_ktime(xts);
tomono = timespec_to_ktime(tom);
+ sleep = 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_BOOTTIME].softirq_time =
+ ktime_add(base->clock_base[HRTIMER_BASE_MONOTONIC].softirq_time,
+ sleep);
}
/*
@@ -1735,6 +1745,7 @@ void __init hrtimers_init(void)
hrtimer_clock_to_base_table[i] = -1;
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());
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 9ca4973..1bc2572 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -234,7 +234,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)
{
@@ -261,6 +261,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 ;)
*/
@@ -295,12 +303,18 @@ static __init int init_posix_timers(void)
.timer_create = no_timer_create,
.nsleep = no_nsleep,
};
+ struct k_clock clock_boottime = {
+ .clock_getres = hrtimer_get_res,
+ .clock_get = posix_get_boottime,
+ .clock_set = do_posix_clock_nosettime,
+ };
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);
+ register_posix_clock(CLOCK_BOOTTIME, &clock_boottime);
posix_timers_cache = kmem_cache_create("posix_timers_cache",
sizeof (struct k_itimer), 0, SLAB_PANIC,
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 49010d8..e3db3c9 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -823,7 +823,7 @@ void update_wall_time(void)
* getboottime - Return the real time of system boot.
* @ts: pointer to the timespec to be set
*
- * Returns the time of day in a timespec.
+ * Returns the wall-time of boot in a timespec.
*
* This is based on the wall_to_monotonic offset and the total suspend
* time. Calls to settimeofday will affect the value returned (which
@@ -841,6 +841,83 @@ void getboottime(struct timespec *ts)
}
EXPORT_SYMBOL_GPL(getboottime);
+
+/**
+ * get_monotonic_boottime - Returns monotonic time since boot
+ * @ts: pointer to the timespec to be set
+ *
+ * Returns the monotonic time since boot in a timespec.
+ *
+ * This is similar to CLOCK_MONTONIC/ktime_get_ts, but also
+ * includes the time spent in suspend.
+ */
+void get_monotonic_boottime(struct timespec *ts)
+{
+ struct timespec tomono, sleep;
+ unsigned int seq;
+ s64 nsecs;
+
+ WARN_ON(timekeeping_suspended);
+
+ do {
+ seq = read_seqbegin(&xtime_lock);
+ *ts = xtime;
+ tomono = wall_to_monotonic;
+ sleep = total_sleep_time;
+ nsecs = timekeeping_get_ns();
+
+ } while (read_seqretry(&xtime_lock, seq));
+
+ set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
+ ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs);
+}
+EXPORT_SYMBOL_GPL(get_monotonic_boottime);
+
+/**
+ * ktime_get_boottime - Returns monotonic time since boot in a ktime
+ *
+ * Returns the monotonic time since boot in a ktime
+ *
+ * This is similar to CLOCK_MONTONIC/ktime_get, but also
+ * includes the time spent in suspend.
+ */
+ktime_t ktime_get_boottime(void)
+{
+ unsigned int seq;
+ s64 secs, nsecs;
+
+ WARN_ON(timekeeping_suspended);
+
+ do {
+ seq = read_seqbegin(&xtime_lock);
+ secs = xtime.tv_sec;
+ secs += wall_to_monotonic.tv_sec;
+ secs += total_sleep_time.tv_sec;
+ nsecs = xtime.tv_nsec;
+ nsecs += wall_to_monotonic.tv_nsec;
+ nsecs += total_sleep_time.tv_nsec;
+ nsecs += timekeeping_get_ns();
+
+ } while (read_seqretry(&xtime_lock, seq));
+ /*
+ * Use ktime_set/ktime_add_ns to create a proper ktime on
+ * 32-bit architectures without CONFIG_KTIME_SCALAR.
+ */
+ return ktime_add_ns(ktime_set(secs, 0), nsecs);
+}
+EXPORT_SYMBOL_GPL(ktime_get_boottime);
+
+/**
+ * __get_sleep_time - returns total_sleep_time
+ *
+ * Returns total time spent in suspend.
+ * Requires the xtime lock be held
+ */
+struct timespec __get_sleep_time(void)
+{
+ return total_sleep_time;
+}
+
/**
* monotonic_to_bootbased - Convert the monotonic time to boot based.
* @ts: pointer to the timespec to be converted
--
1.7.3.2.146.gca209
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/