ソースを参照

[PATCH] GTOD: persistent clock support

Persistent clock support: do proper timekeeping across suspend/resume.

[bunk@stusta.de: cleanup]
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Cc: Roman Zippel <zippel@linux-m68k.org>
Cc: Adrian Bunk <bunk@stusta.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
John Stultz 18 年 前
コミット
411187fb05
4 ファイル変更50 行追加1 行削除
  1. 3 0
      include/linux/hrtimer.h
  2. 1 0
      include/linux/time.h
  3. 8 0
      kernel/hrtimer.c
  4. 38 1
      kernel/timer.c

+ 3 - 0
include/linux/hrtimer.h

@@ -146,6 +146,9 @@ extern void hrtimer_init_sleeper(struct hrtimer_sleeper *sl,
 /* Soft interrupt function to run the hrtimer queues: */
 /* Soft interrupt function to run the hrtimer queues: */
 extern void hrtimer_run_queues(void);
 extern void hrtimer_run_queues(void);
 
 
+/* Resume notification */
+void hrtimer_notify_resume(void);
+
 /* Bootup initialization: */
 /* Bootup initialization: */
 extern void __init hrtimers_init(void);
 extern void __init hrtimers_init(void);
 
 

+ 1 - 0
include/linux/time.h

@@ -92,6 +92,7 @@ extern struct timespec xtime;
 extern struct timespec wall_to_monotonic;
 extern struct timespec wall_to_monotonic;
 extern seqlock_t xtime_lock __attribute__((weak));
 extern seqlock_t xtime_lock __attribute__((weak));
 
 
+extern unsigned long read_persistent_clock(void);
 void timekeeping_init(void);
 void timekeeping_init(void);
 
 
 static inline unsigned long get_seconds(void)
 static inline unsigned long get_seconds(void)

+ 8 - 0
kernel/hrtimer.c

@@ -291,6 +291,14 @@ static unsigned long ktime_divns(const ktime_t kt, s64 div)
 # define ktime_divns(kt, div)		(unsigned long)((kt).tv64 / (div))
 # define ktime_divns(kt, div)		(unsigned long)((kt).tv64 / (div))
 #endif /* BITS_PER_LONG >= 64 */
 #endif /* BITS_PER_LONG >= 64 */
 
 
+/*
+ * Timekeeping resumed notification
+ */
+void hrtimer_notify_resume(void)
+{
+	clock_was_set();
+}
+
 /*
 /*
  * Counterpart to lock_timer_base above:
  * Counterpart to lock_timer_base above:
  */
  */

+ 38 - 1
kernel/timer.c

@@ -878,12 +878,27 @@ int timekeeping_is_continuous(void)
 	return ret;
 	return ret;
 }
 }
 
 
+/**
+ * read_persistent_clock -  Return time in seconds from the persistent clock.
+ *
+ * Weak dummy function for arches that do not yet support it.
+ * Returns seconds from epoch using the battery backed persistent clock.
+ * Returns zero if unsupported.
+ *
+ *  XXX - Do be sure to remove it once all arches implement it.
+ */
+unsigned long __attribute__((weak)) read_persistent_clock(void)
+{
+	return 0;
+}
+
 /*
 /*
  * timekeeping_init - Initializes the clocksource and common timekeeping values
  * timekeeping_init - Initializes the clocksource and common timekeeping values
  */
  */
 void __init timekeeping_init(void)
 void __init timekeeping_init(void)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
+	unsigned long sec = read_persistent_clock();
 
 
 	write_seqlock_irqsave(&xtime_lock, flags);
 	write_seqlock_irqsave(&xtime_lock, flags);
 
 
@@ -893,11 +908,20 @@ void __init timekeeping_init(void)
 	clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
 	clocksource_calculate_interval(clock, NTP_INTERVAL_LENGTH);
 	clock->cycle_last = clocksource_read(clock);
 	clock->cycle_last = clocksource_read(clock);
 
 
+	xtime.tv_sec = sec;
+	xtime.tv_nsec = 0;
+	set_normalized_timespec(&wall_to_monotonic,
+		-xtime.tv_sec, -xtime.tv_nsec);
+
 	write_sequnlock_irqrestore(&xtime_lock, flags);
 	write_sequnlock_irqrestore(&xtime_lock, flags);
 }
 }
 
 
 
 
+/* flag for if timekeeping is suspended */
 static int timekeeping_suspended;
 static int timekeeping_suspended;
+/* time in seconds when suspend began */
+static unsigned long timekeeping_suspend_time;
+
 /**
 /**
  * timekeeping_resume - Resumes the generic timekeeping subsystem.
  * timekeeping_resume - Resumes the generic timekeeping subsystem.
  * @dev:	unused
  * @dev:	unused
@@ -909,13 +933,25 @@ static int timekeeping_suspended;
 static int timekeeping_resume(struct sys_device *dev)
 static int timekeeping_resume(struct sys_device *dev)
 {
 {
 	unsigned long flags;
 	unsigned long flags;
+	unsigned long now = read_persistent_clock();
 
 
 	write_seqlock_irqsave(&xtime_lock, flags);
 	write_seqlock_irqsave(&xtime_lock, flags);
-	/* restart the last cycle value */
+
+	if (now && (now > timekeeping_suspend_time)) {
+		unsigned long sleep_length = now - timekeeping_suspend_time;
+
+		xtime.tv_sec += sleep_length;
+		wall_to_monotonic.tv_sec -= sleep_length;
+	}
+	/* re-base the last cycle value */
 	clock->cycle_last = clocksource_read(clock);
 	clock->cycle_last = clocksource_read(clock);
 	clock->error = 0;
 	clock->error = 0;
 	timekeeping_suspended = 0;
 	timekeeping_suspended = 0;
 	write_sequnlock_irqrestore(&xtime_lock, flags);
 	write_sequnlock_irqrestore(&xtime_lock, flags);
+
+	touch_softlockup_watchdog();
+	hrtimer_notify_resume();
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -925,6 +961,7 @@ static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
 
 
 	write_seqlock_irqsave(&xtime_lock, flags);
 	write_seqlock_irqsave(&xtime_lock, flags);
 	timekeeping_suspended = 1;
 	timekeeping_suspended = 1;
+	timekeeping_suspend_time = read_persistent_clock();
 	write_sequnlock_irqrestore(&xtime_lock, flags);
 	write_sequnlock_irqrestore(&xtime_lock, flags);
 	return 0;
 	return 0;
 }
 }