diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r-- | kernel/time/timekeeping.c | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 9d09777a213f..fdc6b887b208 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
@@ -692,12 +692,34 @@ static void timekeeping_resume(void) | |||
692 | static int timekeeping_suspend(void) | 692 | static int timekeeping_suspend(void) |
693 | { | 693 | { |
694 | unsigned long flags; | 694 | unsigned long flags; |
695 | struct timespec delta, delta_delta; | ||
696 | static struct timespec old_delta; | ||
695 | 697 | ||
696 | read_persistent_clock(&timekeeping_suspend_time); | 698 | read_persistent_clock(&timekeeping_suspend_time); |
697 | 699 | ||
698 | write_seqlock_irqsave(&xtime_lock, flags); | 700 | write_seqlock_irqsave(&xtime_lock, flags); |
699 | timekeeping_forward_now(); | 701 | timekeeping_forward_now(); |
700 | timekeeping_suspended = 1; | 702 | timekeeping_suspended = 1; |
703 | |||
704 | /* | ||
705 | * To avoid drift caused by repeated suspend/resumes, | ||
706 | * which each can add ~1 second drift error, | ||
707 | * try to compensate so the difference in system time | ||
708 | * and persistent_clock time stays close to constant. | ||
709 | */ | ||
710 | delta = timespec_sub(xtime, timekeeping_suspend_time); | ||
711 | delta_delta = timespec_sub(delta, old_delta); | ||
712 | if (abs(delta_delta.tv_sec) >= 2) { | ||
713 | /* | ||
714 | * if delta_delta is too large, assume time correction | ||
715 | * has occured and set old_delta to the current delta. | ||
716 | */ | ||
717 | old_delta = delta; | ||
718 | } else { | ||
719 | /* Otherwise try to adjust old_system to compensate */ | ||
720 | timekeeping_suspend_time = | ||
721 | timespec_add(timekeeping_suspend_time, delta_delta); | ||
722 | } | ||
701 | write_sequnlock_irqrestore(&xtime_lock, flags); | 723 | write_sequnlock_irqrestore(&xtime_lock, flags); |
702 | 724 | ||
703 | clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); | 725 | clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); |