aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/timekeeping.c
diff options
context:
space:
mode:
authorJohn Stultz <john.stultz@linaro.org>2012-07-27 14:48:12 -0400
committerIngo Molnar <mingo@kernel.org>2012-07-31 11:09:14 -0400
commit6d0ef903e2bda70da124c10d8ad89f2382c87991 (patch)
treefae52a865dbe0757e4e5ff49ce4a7f65447e0343 /kernel/time/timekeeping.c
parentd4e3ab384b2343c7074f713ac330f839c38c52ee (diff)
time: Clean up offs_real/wall_to_mono and offs_boot/total_sleep_time updates
For performance reasons, we maintain ktime_t based duplicates of wall_to_monotonic (offs_real) and total_sleep_time (offs_boot). Since large problems could occur (such as the resume regression on 3.5-rc7, or the leapsecond hrtimer issue) if these value pairs were to be inconsistently updated, this patch this cleans up how we modify these value pairs to ensure we are always consistent. As a side-effect this is also more efficient as we only caulculate the duplicate values when they are changed, rather then every update_wall_time call. This also provides WARN_ONs to detect if future changes break the invariants. Signed-off-by: John Stultz <john.stultz@linaro.org> Cc: Peter Zijlstra <a.p.zijlstra@chello.nl> Cc: Richard Cochran <richardcochran@gmail.com> Cc: Prarit Bhargava <prarit@redhat.com> Link: http://lkml.kernel.org/r/1343414893-45779-5-git-send-email-john.stultz@linaro.org [ Cleaned up minor style issues. ] Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r--kernel/time/timekeeping.c90
1 files changed, 54 insertions, 36 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 05b37a5ec7fb..4da65592b4d9 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -65,14 +65,14 @@ struct timekeeper {
65 * used instead. 65 * used instead.
66 */ 66 */
67 struct timespec wall_to_monotonic; 67 struct timespec wall_to_monotonic;
68 /* time spent in suspend */
69 struct timespec total_sleep_time;
70 /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
71 struct timespec raw_time;
72 /* Offset clock monotonic -> clock realtime */ 68 /* Offset clock monotonic -> clock realtime */
73 ktime_t offs_real; 69 ktime_t offs_real;
70 /* time spent in suspend */
71 struct timespec total_sleep_time;
74 /* Offset clock monotonic -> clock boottime */ 72 /* Offset clock monotonic -> clock boottime */
75 ktime_t offs_boot; 73 ktime_t offs_boot;
74 /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
75 struct timespec raw_time;
76 /* Seqlock for all timekeeper values */ 76 /* Seqlock for all timekeeper values */
77 seqlock_t lock; 77 seqlock_t lock;
78}; 78};
@@ -117,6 +117,31 @@ static void tk_xtime_add(struct timekeeper *tk, const struct timespec *ts)
117 tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift; 117 tk->xtime_nsec += (u64)ts->tv_nsec << tk->shift;
118} 118}
119 119
120static void tk_set_wall_to_mono(struct timekeeper *tk, struct timespec wtm)
121{
122 struct timespec tmp;
123
124 /*
125 * Verify consistency of: offset_real = -wall_to_monotonic
126 * before modifying anything
127 */
128 set_normalized_timespec(&tmp, -tk->wall_to_monotonic.tv_sec,
129 -tk->wall_to_monotonic.tv_nsec);
130 WARN_ON_ONCE(tk->offs_real.tv64 != timespec_to_ktime(tmp).tv64);
131 tk->wall_to_monotonic = wtm;
132 set_normalized_timespec(&tmp, -wtm.tv_sec, -wtm.tv_nsec);
133 tk->offs_real = timespec_to_ktime(tmp);
134}
135
136static void tk_set_sleep_time(struct timekeeper *tk, struct timespec t)
137{
138 /* Verify consistency before modifying */
139 WARN_ON_ONCE(tk->offs_boot.tv64 != timespec_to_ktime(tk->total_sleep_time).tv64);
140
141 tk->total_sleep_time = t;
142 tk->offs_boot = timespec_to_ktime(t);
143}
144
120/** 145/**
121 * timekeeper_setup_internals - Set up internals to use clocksource clock. 146 * timekeeper_setup_internals - Set up internals to use clocksource clock.
122 * 147 *
@@ -217,14 +242,6 @@ static inline s64 timekeeping_get_ns_raw(struct timekeeper *tk)
217 return nsec + arch_gettimeoffset(); 242 return nsec + arch_gettimeoffset();
218} 243}
219 244
220static void update_rt_offset(struct timekeeper *tk)
221{
222 struct timespec tmp, *wtm = &tk->wall_to_monotonic;
223
224 set_normalized_timespec(&tmp, -wtm->tv_sec, -wtm->tv_nsec);
225 tk->offs_real = timespec_to_ktime(tmp);
226}
227
228/* must hold write on timekeeper.lock */ 245/* must hold write on timekeeper.lock */
229static void timekeeping_update(struct timekeeper *tk, bool clearntp) 246static void timekeeping_update(struct timekeeper *tk, bool clearntp)
230{ 247{
@@ -234,7 +251,6 @@ static void timekeeping_update(struct timekeeper *tk, bool clearntp)
234 tk->ntp_error = 0; 251 tk->ntp_error = 0;
235 ntp_clear(); 252 ntp_clear();
236 } 253 }
237 update_rt_offset(tk);
238 xt = tk_xtime(tk); 254 xt = tk_xtime(tk);
239 update_vsyscall(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult); 255 update_vsyscall(&xt, &tk->wall_to_monotonic, tk->clock, tk->mult);
240} 256}
@@ -419,8 +435,8 @@ int do_settimeofday(const struct timespec *tv)
419 ts_delta.tv_sec = tv->tv_sec - xt.tv_sec; 435 ts_delta.tv_sec = tv->tv_sec - xt.tv_sec;
420 ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec; 436 ts_delta.tv_nsec = tv->tv_nsec - xt.tv_nsec;
421 437
422 timekeeper.wall_to_monotonic = 438 tk_set_wall_to_mono(&timekeeper,
423 timespec_sub(timekeeper.wall_to_monotonic, ts_delta); 439 timespec_sub(timekeeper.wall_to_monotonic, ts_delta));
424 440
425 tk_set_xtime(&timekeeper, tv); 441 tk_set_xtime(&timekeeper, tv);
426 442
@@ -454,8 +470,8 @@ int timekeeping_inject_offset(struct timespec *ts)
454 470
455 471
456 tk_xtime_add(&timekeeper, ts); 472 tk_xtime_add(&timekeeper, ts);
457 timekeeper.wall_to_monotonic = 473 tk_set_wall_to_mono(&timekeeper,
458 timespec_sub(timekeeper.wall_to_monotonic, *ts); 474 timespec_sub(timekeeper.wall_to_monotonic, *ts));
459 475
460 timekeeping_update(&timekeeper, true); 476 timekeeping_update(&timekeeper, true);
461 477
@@ -621,7 +637,7 @@ void __init timekeeping_init(void)
621{ 637{
622 struct clocksource *clock; 638 struct clocksource *clock;
623 unsigned long flags; 639 unsigned long flags;
624 struct timespec now, boot; 640 struct timespec now, boot, tmp;
625 641
626 read_persistent_clock(&now); 642 read_persistent_clock(&now);
627 read_boot_clock(&boot); 643 read_boot_clock(&boot);
@@ -642,23 +658,19 @@ void __init timekeeping_init(void)
642 if (boot.tv_sec == 0 && boot.tv_nsec == 0) 658 if (boot.tv_sec == 0 && boot.tv_nsec == 0)
643 boot = tk_xtime(&timekeeper); 659 boot = tk_xtime(&timekeeper);
644 660
645 set_normalized_timespec(&timekeeper.wall_to_monotonic, 661 set_normalized_timespec(&tmp, -boot.tv_sec, -boot.tv_nsec);
646 -boot.tv_sec, -boot.tv_nsec); 662 tk_set_wall_to_mono(&timekeeper, tmp);
647 update_rt_offset(&timekeeper); 663
648 timekeeper.total_sleep_time.tv_sec = 0; 664 tmp.tv_sec = 0;
649 timekeeper.total_sleep_time.tv_nsec = 0; 665 tmp.tv_nsec = 0;
666 tk_set_sleep_time(&timekeeper, tmp);
667
650 write_sequnlock_irqrestore(&timekeeper.lock, flags); 668 write_sequnlock_irqrestore(&timekeeper.lock, flags);
651} 669}
652 670
653/* time in seconds when suspend began */ 671/* time in seconds when suspend began */
654static struct timespec timekeeping_suspend_time; 672static struct timespec timekeeping_suspend_time;
655 673
656static void update_sleep_time(struct timespec t)
657{
658 timekeeper.total_sleep_time = t;
659 timekeeper.offs_boot = timespec_to_ktime(t);
660}
661
662/** 674/**
663 * __timekeeping_inject_sleeptime - Internal function to add sleep interval 675 * __timekeeping_inject_sleeptime - Internal function to add sleep interval
664 * @delta: pointer to a timespec delta value 676 * @delta: pointer to a timespec delta value
@@ -674,10 +686,9 @@ static void __timekeeping_inject_sleeptime(struct timekeeper *tk,
674 "sleep delta value!\n"); 686 "sleep delta value!\n");
675 return; 687 return;
676 } 688 }
677
678 tk_xtime_add(tk, delta); 689 tk_xtime_add(tk, delta);
679 tk->wall_to_monotonic = timespec_sub(tk->wall_to_monotonic, *delta); 690 tk_set_wall_to_mono(tk, timespec_sub(tk->wall_to_monotonic, *delta));
680 update_sleep_time(timespec_add(tk->total_sleep_time, *delta)); 691 tk_set_sleep_time(tk, timespec_add(tk->total_sleep_time, *delta));
681} 692}
682 693
683/** 694/**
@@ -1018,11 +1029,18 @@ static inline void accumulate_nsecs_to_secs(struct timekeeper *tk)
1018 1029
1019 /* Figure out if its a leap sec and apply if needed */ 1030 /* Figure out if its a leap sec and apply if needed */
1020 leap = second_overflow(tk->xtime_sec); 1031 leap = second_overflow(tk->xtime_sec);
1021 tk->xtime_sec += leap; 1032 if (unlikely(leap)) {
1022 tk->wall_to_monotonic.tv_sec -= leap; 1033 struct timespec ts;
1023 if (leap) 1034
1024 clock_was_set_delayed(); 1035 tk->xtime_sec += leap;
1025 1036
1037 ts.tv_sec = leap;
1038 ts.tv_nsec = 0;
1039 tk_set_wall_to_mono(tk,
1040 timespec_sub(tk->wall_to_monotonic, ts));
1041
1042 clock_was_set_delayed();
1043 }
1026 } 1044 }
1027} 1045}
1028 1046