timekeeping: Add the new CLOCK_MONOTONIC_ACTIVE clock
authorThomas Gleixner <tglx@linutronix.de>
Thu, 1 Mar 2018 16:33:32 +0000 (17:33 +0100)
committerIngo Molnar <mingo@kernel.org>
Tue, 13 Mar 2018 06:34:21 +0000 (07:34 +0100)
The planned change to unify the behaviour of the MONOTONIC and BOOTTIME
clocks vs. suspend removes the ability to retrieve the active
non-suspended time of a system.

Provide a new CLOCK_MONOTONIC_ACTIVE clock which returns the active
non-suspended time of the system via clock_gettime().

This preserves the old behaviour of CLOCK_MONOTONIC before the
BOOTTIME/MONOTONIC unification.

This new clock also allows applications to detect programmatically that
the MONOTONIC and BOOTTIME clocks are identical.

Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: John Stultz <john.stultz@linaro.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: Kevin Easton <kevin@guarana.org>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Mark Salyzyn <salyzyn@android.com>
Cc: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Petr Mladek <pmladek@suse.com>
Cc: Prarit Bhargava <prarit@redhat.com>
Cc: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Cc: Steven Rostedt <rostedt@goodmis.org>
Link: http://lkml.kernel.org/r/20180301165149.965235774@linutronix.de
Signed-off-by: Ingo Molnar <mingo@kernel.org>
include/linux/timekeeper_internal.h
include/linux/timekeeping.h
include/uapi/linux/time.h
kernel/time/posix-stubs.c
kernel/time/posix-timers.c
kernel/time/timekeeping.c

index 7acb953..4b3dca1 100644 (file)
@@ -52,6 +52,7 @@ struct tk_read_base {
  * @offs_real:         Offset clock monotonic -> clock realtime
  * @offs_boot:         Offset clock monotonic -> clock boottime
  * @offs_tai:          Offset clock monotonic -> clock tai
+ * @time_suspended:    Accumulated suspend time
  * @tai_offset:                The current UTC to TAI offset in seconds
  * @clock_was_set_seq: The sequence number of clock was set events
  * @cs_was_changed_seq:        The sequence number of clocksource change events
@@ -94,6 +95,7 @@ struct timekeeper {
        ktime_t                 offs_real;
        ktime_t                 offs_boot;
        ktime_t                 offs_tai;
+       ktime_t                 time_suspended;
        s32                     tai_offset;
        unsigned int            clock_was_set_seq;
        u8                      cs_was_changed_seq;
index b17bcce..440b193 100644 (file)
@@ -32,6 +32,7 @@ extern void getrawmonotonic64(struct timespec64 *ts);
 extern void ktime_get_ts64(struct timespec64 *ts);
 extern time64_t ktime_get_seconds(void);
 extern time64_t ktime_get_real_seconds(void);
+extern void ktime_get_active_ts64(struct timespec64 *ts);
 
 extern int __getnstimeofday64(struct timespec64 *tv);
 extern void getnstimeofday64(struct timespec64 *tv);
index 53f8dd8..61a187d 100644 (file)
@@ -61,6 +61,7 @@ struct itimerval {
  */
 #define CLOCK_SGI_CYCLE                        10
 #define CLOCK_TAI                      11
+#define CLOCK_MONOTONIC_ACTIVE         12
 
 #define MAX_CLOCKS                     16
 #define CLOCKS_MASK                    (CLOCK_REALTIME | CLOCK_MONOTONIC)
index b258bee..6259dbc 100644 (file)
@@ -73,6 +73,8 @@ int do_clock_gettime(clockid_t which_clock, struct timespec64 *tp)
        case CLOCK_BOOTTIME:
                get_monotonic_boottime64(tp);
                break;
+       case CLOCK_MONOTONIC_ACTIVE:
+               ktime_get_active_ts64(tp);
        default:
                return -EINVAL;
        }
index 7504304..556fe02 100644 (file)
@@ -263,6 +263,13 @@ static int posix_get_tai(clockid_t which_clock, struct timespec64 *tp)
        return 0;
 }
 
+static int posix_get_monotonic_active(clockid_t which_clock,
+                                     struct timespec64 *tp)
+{
+       ktime_get_active_ts64(tp);
+       return 0;
+}
+
 static int posix_get_hrtimer_res(clockid_t which_clock, struct timespec64 *tp)
 {
        tp->tv_sec = 0;
@@ -1330,6 +1337,11 @@ static const struct k_clock clock_boottime = {
        .timer_arm              = common_hrtimer_arm,
 };
 
+static const struct k_clock clock_monotonic_active = {
+       .clock_getres           = posix_get_hrtimer_res,
+       .clock_get              = posix_get_monotonic_active,
+};
+
 static const struct k_clock * const posix_clocks[] = {
        [CLOCK_REALTIME]                = &clock_realtime,
        [CLOCK_MONOTONIC]               = &clock_monotonic,
@@ -1342,6 +1354,7 @@ static const struct k_clock * const posix_clocks[] = {
        [CLOCK_REALTIME_ALARM]          = &alarm_clock,
        [CLOCK_BOOTTIME_ALARM]          = &alarm_clock,
        [CLOCK_TAI]                     = &clock_tai,
+       [CLOCK_MONOTONIC_ACTIVE]        = &clock_monotonic_active,
 };
 
 static const struct k_clock *clockid_to_kclock(const clockid_t id)
index e117601..a2b7f58 100644 (file)
@@ -139,6 +139,9 @@ 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);
+
+       /* Accumulate time spent in suspend */
+       tk->time_suspended += delta;
 }
 
 /*
@@ -886,6 +889,39 @@ void ktime_get_ts64(struct timespec64 *ts)
 }
 EXPORT_SYMBOL_GPL(ktime_get_ts64);
 
+/**
+ * ktime_get_active_ts64 - Get the active non-suspended monotonic clock
+ * @ts:                pointer to timespec variable
+ *
+ * The function calculates the monotonic clock from the realtime clock and
+ * the wall_to_monotonic offset, subtracts the accumulated suspend time and
+ * stores the result in normalized timespec64 format in the variable
+ * pointed to by @ts.
+ */
+void ktime_get_active_ts64(struct timespec64 *ts)
+{
+       struct timekeeper *tk = &tk_core.timekeeper;
+       struct timespec64 tomono, tsusp;
+       u64 nsec, nssusp;
+       unsigned int seq;
+
+       WARN_ON(timekeeping_suspended);
+
+       do {
+               seq = read_seqcount_begin(&tk_core.seq);
+               ts->tv_sec = tk->xtime_sec;
+               nsec = timekeeping_get_ns(&tk->tkr_mono);
+               tomono = tk->wall_to_monotonic;
+               nssusp = tk->time_suspended;
+       } while (read_seqcount_retry(&tk_core.seq, seq));
+
+       ts->tv_sec += tomono.tv_sec;
+       ts->tv_nsec = 0;
+       timespec64_add_ns(ts, nsec + tomono.tv_nsec);
+       tsusp = ns_to_timespec64(nssusp);
+       *ts = timespec64_sub(*ts, tsusp);
+}
+
 /**
  * ktime_get_seconds - Get the seconds portion of CLOCK_MONOTONIC
  *