Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=6a669ee8a790487b7ec1edda762d39615a78264b
Commit:     6a669ee8a790487b7ec1edda762d39615a78264b
Parent:     3be9095063885d482b87d3875ea7f28e635882d0
Author:     Thomas Gleixner <[EMAIL PROTECTED]>
AuthorDate: Sun Sep 16 15:36:43 2007 +0200
Committer:  Thomas Gleixner <[EMAIL PROTECTED](none)>
CommitDate: Sun Sep 16 15:36:43 2007 +0200

    timekeeping: Prevent time going backwards on resume
    
    Timekeeping resume adjusts xtime by adding the slept time in seconds and
    resets the reference value of the clock source (clock->cycle_last).
    clock->cycle last is used to calculate the delta between the last xtime
    update and the readout of the clock source in __get_nsec_offset(). xtime
    plus the offset is the current time. The resume code ignores the delta
    which had already elapsed between the last xtime update and the actual
    time of suspend. If the suspend time is short, then we can see time
    going backwards on resume.
    
    Suspend:
    offs_s = clock->read() - clock->cycle_last;
    now = xtime + offs_s;
    timekeeping_suspend_time = read_rtc();
    
    Resume:
    sleep_time = read_rtc() - timekeeping_suspend_time;
    xtime.tv_sec += sleep_time;
    clock->cycle_last = clock->read();
    offs_r = clock->read() - clock->cycle_last;
    now = xtime + offs_r;
    
    if sleep_time_seconds == 0 and offs_r < offs_s, then time goes
    backwards.
    
    Fix this by storing the offset from the last xtime update and add it to
    xtime during resume, when we reset clock->cycle_last:
    
    sleep_time = read_rtc() - timekeeping_suspend_time;
    xtime.tv_sec += sleep_time;
    xtime += offs_s;    /* Fixup xtime offset at suspend time */
    clock->cycle_last = clock->read();
    offs_r = clock->read() - clock->cycle_last;
    now = xtime + offs_r;
    
    Thanks to Marcelo for tracking this down on the OLPC and providing the
    necessary details to analyze the root cause.
    
    Signed-off-by: Thomas Gleixner <[EMAIL PROTECTED]>
    Cc: John Stultz <[EMAIL PROTECTED]>
    Cc: Tosatti <[EMAIL PROTECTED]>
---
 kernel/time/timekeeping.c |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/kernel/time/timekeeping.c b/kernel/time/timekeeping.c
index f682091..4ad79f6 100644
--- a/kernel/time/timekeeping.c
+++ b/kernel/time/timekeeping.c
@@ -217,6 +217,7 @@ static void change_clocksource(void)
 }
 #else
 static inline void change_clocksource(void) { }
+static inline s64 __get_nsec_offset(void) { return 0; }
 #endif
 
 /**
@@ -280,6 +281,8 @@ void __init timekeeping_init(void)
 static int timekeeping_suspended;
 /* time in seconds when suspend began */
 static unsigned long timekeeping_suspend_time;
+/* xtime offset when we went into suspend */
+static s64 timekeeping_suspend_nsecs;
 
 /**
  * timekeeping_resume - Resumes the generic timekeeping subsystem.
@@ -305,6 +308,8 @@ static int timekeeping_resume(struct sys_device *dev)
                wall_to_monotonic.tv_sec -= sleep_length;
                total_sleep_time += sleep_length;
        }
+       /* Make sure that we have the correct xtime reference */
+       timespec_add_ns(&xtime, timekeeping_suspend_nsecs);
        /* re-base the last cycle value */
        clock->cycle_last = clocksource_read(clock);
        clock->error = 0;
@@ -328,6 +333,8 @@ static int timekeeping_suspend(struct sys_device *dev, 
pm_message_t state)
        timekeeping_suspend_time = read_persistent_clock();
 
        write_seqlock_irqsave(&xtime_lock, flags);
+       /* Get the current xtime offset */
+       timekeeping_suspend_nsecs = __get_nsec_offset();
        timekeeping_suspended = 1;
        write_sequnlock_irqrestore(&xtime_lock, flags);
 
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to