summaryrefslogtreecommitdiff
path: root/kernel/timer.c
diff options
context:
space:
mode:
authorjohn stultz <johnstul@us.ibm.com>2006-07-14 00:24:17 -0700
committerLinus Torvalds <torvalds@g5.osdl.org>2006-07-14 21:53:54 -0700
commit3e143475c22036847f898d7e76ba337c1d7dbf6f (patch)
tree4bbd8f322f6a1cd43be409393742967234d23151 /kernel/timer.c
parent635adb6cd25c8f816c9017a0a0349cd389eafcd3 (diff)
downloadlwn-3e143475c22036847f898d7e76ba337c1d7dbf6f.tar.gz
lwn-3e143475c22036847f898d7e76ba337c1d7dbf6f.zip
[PATCH] improve timekeeping resume robustness
Resolve problems seen w/ APM suspend. Due to resume initialization ordering, its possible we could get a timer interrupt before the timekeeping resume() function is called. This patch ensures we don't do any timekeeping accounting before we're fully resumed. (akpm: fixes the machine-freezes-on-APM-resume bug) Signed-off-by: John Stultz <johnstul@us.ibm.com> Cc: Roman Zippel <zippel@linux-m68k.org> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel/timer.c')
-rw-r--r--kernel/timer.c19
1 files changed, 18 insertions, 1 deletions
diff --git a/kernel/timer.c b/kernel/timer.c
index acfa557e685b..05809c2e2fd6 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -969,6 +969,7 @@ void __init timekeeping_init(void)
}
+static int timekeeping_suspended;
/*
* timekeeping_resume - Resumes the generic timekeeping subsystem.
* @dev: unused
@@ -984,6 +985,18 @@ static int timekeeping_resume(struct sys_device *dev)
write_seqlock_irqsave(&xtime_lock, flags);
/* restart the last cycle value */
clock->cycle_last = clocksource_read(clock);
+ clock->error = 0;
+ timekeeping_suspended = 0;
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+ return 0;
+}
+
+static int timekeeping_suspend(struct sys_device *dev, pm_message_t state)
+{
+ unsigned long flags;
+
+ write_seqlock_irqsave(&xtime_lock, flags);
+ timekeeping_suspended = 1;
write_sequnlock_irqrestore(&xtime_lock, flags);
return 0;
}
@@ -991,6 +1004,7 @@ static int timekeeping_resume(struct sys_device *dev)
/* sysfs resume/suspend bits for timekeeping */
static struct sysdev_class timekeeping_sysclass = {
.resume = timekeeping_resume,
+ .suspend = timekeeping_suspend,
set_kset_name("timekeeping"),
};
@@ -1101,13 +1115,16 @@ static void update_wall_time(void)
{
cycle_t offset;
- clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
+ /* Make sure we're fully resumed: */
+ if (unlikely(timekeeping_suspended))
+ return;
#ifdef CONFIG_GENERIC_TIME
offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask;
#else
offset = clock->cycle_interval;
#endif
+ clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
/* normally this loop will run just once, however in the
* case of lost or late ticks, it will accumulate correctly.