diff options
Diffstat (limited to 'kernel/time/timekeeping.c')
| -rw-r--r-- | kernel/time/timekeeping.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c index 342408cf68dd..2b021b0e8507 100644 --- a/kernel/time/timekeeping.c +++ b/kernel/time/timekeeping.c | |||
| @@ -604,6 +604,12 @@ static struct timespec timekeeping_suspend_time; | |||
| 604 | */ | 604 | */ |
| 605 | static void __timekeeping_inject_sleeptime(struct timespec *delta) | 605 | static void __timekeeping_inject_sleeptime(struct timespec *delta) |
| 606 | { | 606 | { |
| 607 | if (!timespec_valid(delta)) { | ||
| 608 | printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid " | ||
| 609 | "sleep delta value!\n"); | ||
| 610 | return; | ||
| 611 | } | ||
| 612 | |||
| 607 | xtime = timespec_add(xtime, *delta); | 613 | xtime = timespec_add(xtime, *delta); |
| 608 | wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta); | 614 | wall_to_monotonic = timespec_sub(wall_to_monotonic, *delta); |
| 609 | total_sleep_time = timespec_add(total_sleep_time, *delta); | 615 | total_sleep_time = timespec_add(total_sleep_time, *delta); |
| @@ -686,12 +692,34 @@ static void timekeeping_resume(void) | |||
| 686 | static int timekeeping_suspend(void) | 692 | static int timekeeping_suspend(void) |
| 687 | { | 693 | { |
| 688 | unsigned long flags; | 694 | unsigned long flags; |
| 695 | struct timespec delta, delta_delta; | ||
| 696 | static struct timespec old_delta; | ||
| 689 | 697 | ||
| 690 | read_persistent_clock(&timekeeping_suspend_time); | 698 | read_persistent_clock(&timekeeping_suspend_time); |
| 691 | 699 | ||
| 692 | write_seqlock_irqsave(&xtime_lock, flags); | 700 | write_seqlock_irqsave(&xtime_lock, flags); |
| 693 | timekeeping_forward_now(); | 701 | timekeeping_forward_now(); |
| 694 | 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 | } | ||
| 695 | write_sequnlock_irqrestore(&xtime_lock, flags); | 723 | write_sequnlock_irqrestore(&xtime_lock, flags); |
| 696 | 724 | ||
| 697 | clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); | 725 | clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL); |
