aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/time/timekeeping.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r--kernel/time/timekeeping.c39
1 files changed, 39 insertions, 0 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index 342408cf68d..6f9798bf240 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -249,6 +249,8 @@ ktime_t ktime_get(void)
249 secs = xtime.tv_sec + wall_to_monotonic.tv_sec; 249 secs = xtime.tv_sec + wall_to_monotonic.tv_sec;
250 nsecs = xtime.tv_nsec + wall_to_monotonic.tv_nsec; 250 nsecs = xtime.tv_nsec + wall_to_monotonic.tv_nsec;
251 nsecs += timekeeping_get_ns(); 251 nsecs += timekeeping_get_ns();
252 /* If arch requires, add in gettimeoffset() */
253 nsecs += arch_gettimeoffset();
252 254
253 } while (read_seqretry(&xtime_lock, seq)); 255 } while (read_seqretry(&xtime_lock, seq));
254 /* 256 /*
@@ -280,6 +282,8 @@ void ktime_get_ts(struct timespec *ts)
280 *ts = xtime; 282 *ts = xtime;
281 tomono = wall_to_monotonic; 283 tomono = wall_to_monotonic;
282 nsecs = timekeeping_get_ns(); 284 nsecs = timekeeping_get_ns();
285 /* If arch requires, add in gettimeoffset() */
286 nsecs += arch_gettimeoffset();
283 287
284 } while (read_seqretry(&xtime_lock, seq)); 288 } while (read_seqretry(&xtime_lock, seq));
285 289
@@ -604,6 +608,12 @@ static struct timespec timekeeping_suspend_time;
604 */ 608 */
605static void __timekeeping_inject_sleeptime(struct timespec *delta) 609static void __timekeeping_inject_sleeptime(struct timespec *delta)
606{ 610{
611 if (!timespec_valid(delta)) {
612 printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid "
613 "sleep delta value!\n");
614 return;
615 }
616
607 xtime = timespec_add(xtime, *delta); 617 xtime = timespec_add(xtime, *delta);
608 wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta); 618 wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta);
609 total_sleep_time = timespec_add(total_sleep_time, *delta); 619 total_sleep_time = timespec_add(total_sleep_time, *delta);
@@ -686,12 +696,34 @@ static void timekeeping_resume(void)
686static int timekeeping_suspend(void) 696static int timekeeping_suspend(void)
687{ 697{
688 unsigned long flags; 698 unsigned long flags;
699 struct timespec delta, delta_delta;
700 static struct timespec old_delta;
689 701
690 read_persistent_clock(&timekeeping_suspend_time); 702 read_persistent_clock(&timekeeping_suspend_time);
691 703
692 write_seqlock_irqsave(&xtime_lock, flags); 704 write_seqlock_irqsave(&xtime_lock, flags);
693 timekeeping_forward_now(); 705 timekeeping_forward_now();
694 timekeeping_suspended = 1; 706 timekeeping_suspended = 1;
707
708 /*
709 * To avoid drift caused by repeated suspend/resumes,
710 * which each can add ~1 second drift error,
711 * try to compensate so the difference in system time
712 * and persistent_clock time stays close to constant.
713 */
714 delta = timespec_sub(xtime, timekeeping_suspend_time);
715 delta_delta = timespec_sub(delta, old_delta);
716 if (abs(delta_delta.tv_sec) >= 2) {
717 /*
718 * if delta_delta is too large, assume time correction
719 * has occured and set old_delta to the current delta.
720 */
721 old_delta = delta;
722 } else {
723 /* Otherwise try to adjust old_system to compensate */
724 timekeeping_suspend_time =
725 timespec_add(timekeeping_suspend_time, delta_delta);
726 }
695 write_sequnlock_irqrestore(&xtime_lock, flags); 727 write_sequnlock_irqrestore(&xtime_lock, flags);
696 728
697 clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); 729 clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
@@ -792,6 +824,13 @@ static void timekeeping_adjust(s64 offset)
792 } else 824 } else
793 return; 825 return;
794 826
827 WARN_ONCE(timekeeper.clock->maxadj &&
828 (timekeeper.mult + adj > timekeeper.clock->mult +
829 timekeeper.clock->maxadj),
830 "Adjusting %s more then 11%% (%ld vs %ld)\n",
831 timekeeper.clock->name, (long)timekeeper.mult + adj,
832 (long)timekeeper.clock->mult +
833 timekeeper.clock->maxadj);
795 timekeeper.mult += adj; 834 timekeeper.mult += adj;
796 timekeeper.xtime_interval += interval; 835 timekeeper.xtime_interval += interval;
797 timekeeper.xtime_nsec -= offset; 836 timekeeper.xtime_nsec -= offset;