Commit 3e143475 authored by john stultz's avatar john stultz Committed by Linus Torvalds

[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: default avatarJohn Stultz <johnstul@us.ibm.com>
Cc: Roman Zippel <zippel@linux-m68k.org>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 635adb6c
...@@ -969,6 +969,7 @@ void __init timekeeping_init(void) ...@@ -969,6 +969,7 @@ void __init timekeeping_init(void)
} }
static int timekeeping_suspended;
/* /*
* timekeeping_resume - Resumes the generic timekeeping subsystem. * timekeeping_resume - Resumes the generic timekeeping subsystem.
* @dev: unused * @dev: unused
...@@ -984,6 +985,18 @@ static int timekeeping_resume(struct sys_device *dev) ...@@ -984,6 +985,18 @@ static int timekeeping_resume(struct sys_device *dev)
write_seqlock_irqsave(&xtime_lock, flags); write_seqlock_irqsave(&xtime_lock, flags);
/* restart the last cycle value */ /* restart the last cycle value */
clock->cycle_last = clocksource_read(clock); 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); write_sequnlock_irqrestore(&xtime_lock, flags);
return 0; return 0;
} }
...@@ -991,6 +1004,7 @@ static int timekeeping_resume(struct sys_device *dev) ...@@ -991,6 +1004,7 @@ static int timekeeping_resume(struct sys_device *dev)
/* sysfs resume/suspend bits for timekeeping */ /* sysfs resume/suspend bits for timekeeping */
static struct sysdev_class timekeeping_sysclass = { static struct sysdev_class timekeeping_sysclass = {
.resume = timekeeping_resume, .resume = timekeeping_resume,
.suspend = timekeeping_suspend,
set_kset_name("timekeeping"), set_kset_name("timekeeping"),
}; };
...@@ -1101,13 +1115,16 @@ static void update_wall_time(void) ...@@ -1101,13 +1115,16 @@ static void update_wall_time(void)
{ {
cycle_t offset; 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 #ifdef CONFIG_GENERIC_TIME
offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask; offset = (clocksource_read(clock) - clock->cycle_last) & clock->mask;
#else #else
offset = clock->cycle_interval; offset = clock->cycle_interval;
#endif #endif
clock->xtime_nsec += (s64)xtime.tv_nsec << clock->shift;
/* normally this loop will run just once, however in the /* normally this loop will run just once, however in the
* case of lost or late ticks, it will accumulate correctly. * case of lost or late ticks, it will accumulate correctly.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment