diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
-rw-r--r-- | kernel/time/timekeeping.c | 39 |
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 | */ |
605 | static void __timekeeping_inject_sleeptime(struct timespec *delta) | 609 | static 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) | |||
686 | static int timekeeping_suspend(void) | 696 | static 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; |