[PATCH v2] timekeeping: get_jiffies_boot_64() for jiffies that include sleep time

From: Jason A. Donenfeld
Date: Wed Jun 19 2019 - 10:29:21 EST

This enables using the usual get_jiffies_64() but taking into account
time spent sleeping, giving the high performance characteristics of
querying jiffies without the drawback.

We accomplish this by precomputing the boottime jiffies offset whenever
it is updated, rather than doing the expensive-ish div_u64 on each

Since the resolution of this is in terms of jiffies, this allows
determining limits for comparison in terms of jiffies too, which makes
the comparisons more exact, despite jiffies being a fairly coarse stamp.

Adding the suspend offset to jiffies as such doesn't actually race in a
way different from the usual races associated with the suspend offset:
either boot offset has been updated before the call to
get_jiffies_boot_64(), in which case we're fine, or it hasn't in which
case, this is no different than any of the existing suspend querying
functions, which may be invoked early in system resumption before the
offset is updated.

Signed-off-by: Jason A. Donenfeld <Jason@xxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Cc: Arnd Bergmann <arnd@xxxxxxxx>
Cc: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx>
Cc; John Stultz <john.stultz@xxxxxxxxxx>
include/linux/jiffies.h | 1 +
include/linux/timekeeper_internal.h | 2 ++
kernel/time/timekeeping.c | 11 +++++++++++
3 files changed, 14 insertions(+)

diff --git a/include/linux/jiffies.h b/include/linux/jiffies.h
index 1b6d31da7cbc..e4a9776d8b2a 100644
--- a/include/linux/jiffies.h
+++ b/include/linux/jiffies.h
@@ -80,6 +80,7 @@ extern int register_refined_jiffies(long clock_tick_rate);
extern u64 __cacheline_aligned_in_smp jiffies_64;
extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;

+u64 get_jiffies_boot_64(void);
#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void);
diff --git a/include/linux/timekeeper_internal.h b/include/linux/timekeeper_internal.h
index 7acb953298a7..2e4c52fe0250 100644
--- a/include/linux/timekeeper_internal.h
+++ b/include/linux/timekeeper_internal.h
@@ -51,6 +51,7 @@ struct tk_read_base {
* @wall_to_monotonic: CLOCK_REALTIME to CLOCK_MONOTONIC offset
* @offs_real: Offset clock monotonic -> clock realtime
* @offs_boot: Offset clock monotonic -> clock boottime
+ * @offs_boot_jiffies64 Offset clock monotonic -> clock boottime in jiffies64
* @offs_tai: Offset clock monotonic -> clock tai
* @tai_offset: The current UTC to TAI offset in seconds
* @clock_was_set_seq: The sequence number of clock was set events
@@ -93,6 +94,7 @@ struct timekeeper {
struct timespec64 wall_to_monotonic;
ktime_t offs_real;
ktime_t offs_boot;
+ u64 offs_boot_jiffies64;
ktime_t offs_tai;
s32 tai_offset;
unsigned int clock_was_set_seq;
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 85f5912d8f70..a3707b454446 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -146,6 +146,7 @@ static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec64 wtm)
static inline void tk_update_sleep_time(struct timekeeper *tk, ktime_t delta)
tk->offs_boot = ktime_add(tk->offs_boot, delta);
+ tk->offs_boot_jiffies64 = nsecs_to_jiffies64(ktime_to_ns(tk->offs_boot));

@@ -539,6 +540,16 @@ u64 ktime_get_real_fast_ns(void)

+ * get_jiffies_boot_64 - The normal get_jiffies_64(), but taking into
+ * account the time spent sleeping.
+ */
+u64 get_jiffies_boot_64(void)
+ return get_jiffies_64() + tk_core.timekeeper.offs_boot_jiffies64;
* halt_fast_timekeeper - Prevent fast timekeeper from accessing clocksource.
* @tk: Timekeeper to snapshot.