From: Shaohua Li The timers lack .suspend/.resume methods. Because of this, jiffies got a big compensation after a S3 resume. And then softlockup watchdog reports an oops. This occured with HPET enabled, but it's also possible for other timers. Signed-off-by: Shaohua Li Signed-off-by: Andrew Morton --- arch/i386/kernel/time.c | 10 ++++++++++ arch/i386/kernel/timers/timer_hpet.c | 14 ++++++++++++++ arch/i386/kernel/timers/timer_pit.c | 27 --------------------------- arch/i386/kernel/timers/timer_pm.c | 9 +++++++++ arch/i386/kernel/timers/timer_tsc.c | 14 ++++++++++++++ include/asm-i386/timer.h | 3 +++ 6 files changed, 50 insertions(+), 27 deletions(-) diff -puN arch/i386/kernel/time.c~add-suspend-resume-for-timer arch/i386/kernel/time.c --- 25/arch/i386/kernel/time.c~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/arch/i386/kernel/time.c Fri Jun 24 16:59:36 2005 @@ -383,6 +383,7 @@ void notify_arch_cmos_timer(void) static long clock_cmos_diff, sleep_start; +static struct timer_opts *last_timer; static int timer_suspend(struct sys_device *dev, pm_message_t state) { /* @@ -391,6 +392,10 @@ static int timer_suspend(struct sys_devi clock_cmos_diff = -get_cmos_time(); clock_cmos_diff += get_seconds(); sleep_start = get_cmos_time(); + last_timer = cur_timer; + cur_timer = &timer_none; + if (last_timer->suspend) + last_timer->suspend(state); return 0; } @@ -404,6 +409,7 @@ static int timer_resume(struct sys_devic if (is_hpet_enabled()) hpet_reenable(); #endif + setup_pit_timer(); sec = get_cmos_time() + clock_cmos_diff; sleep_length = (get_cmos_time() - sleep_start) * HZ; write_seqlock_irqsave(&xtime_lock, flags); @@ -412,6 +418,10 @@ static int timer_resume(struct sys_devic write_sequnlock_irqrestore(&xtime_lock, flags); jiffies += sleep_length; wall_jiffies += sleep_length; + if (last_timer->resume) + last_timer->resume(); + cur_timer = last_timer; + last_timer = NULL; return 0; } diff -puN arch/i386/kernel/timers/timer_hpet.c~add-suspend-resume-for-timer arch/i386/kernel/timers/timer_hpet.c --- 25/arch/i386/kernel/timers/timer_hpet.c~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/arch/i386/kernel/timers/timer_hpet.c Fri Jun 24 16:59:04 2005 @@ -177,6 +177,19 @@ static int __init init_hpet(char* overri return 0; } +static int hpet_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + rdtsc(last_tsc_low, last_tsc_high); + + if (hpet_use_timer) + hpet_last = hpet_readl(HPET_T0_CMP) - hpet_tick; + else + hpet_last = hpet_readl(HPET_COUNTER); + write_sequnlock(&monotonic_lock); + return 0; +} /************************************************************/ /* tsc timer_opts struct */ @@ -187,6 +200,7 @@ static struct timer_opts timer_hpet = { .monotonic_clock = monotonic_clock_hpet, .delay = delay_hpet, .read_timer = read_timer_tsc, + .resume = hpet_resume, }; struct init_timer_opts __initdata timer_hpet_init = { diff -puN arch/i386/kernel/timers/timer_pit.c~add-suspend-resume-for-timer arch/i386/kernel/timers/timer_pit.c --- 25/arch/i386/kernel/timers/timer_pit.c~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/arch/i386/kernel/timers/timer_pit.c Fri Jun 24 16:59:04 2005 @@ -175,30 +175,3 @@ void setup_pit_timer(void) outb(LATCH >> 8 , PIT_CH0); /* MSB */ spin_unlock_irqrestore(&i8253_lock, flags); } - -static int timer_resume(struct sys_device *dev) -{ - setup_pit_timer(); - return 0; -} - -static struct sysdev_class timer_sysclass = { - set_kset_name("timer_pit"), - .resume = timer_resume, -}; - -static struct sys_device device_timer = { - .id = 0, - .cls = &timer_sysclass, -}; - -static int __init init_timer_sysfs(void) -{ - int error = sysdev_class_register(&timer_sysclass); - if (!error) - error = sysdev_register(&device_timer); - return error; -} - -device_initcall(init_timer_sysfs); - diff -puN arch/i386/kernel/timers/timer_pm.c~add-suspend-resume-for-timer arch/i386/kernel/timers/timer_pm.c --- 25/arch/i386/kernel/timers/timer_pm.c~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/arch/i386/kernel/timers/timer_pm.c Fri Jun 24 16:59:04 2005 @@ -186,6 +186,14 @@ static void mark_offset_pmtmr(void) } } +static int pmtmr_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + offset_tick = read_pmtmr(); + write_sequnlock(&monotonic_lock); + return 0; +} static unsigned long long monotonic_clock_pmtmr(void) { @@ -247,6 +255,7 @@ static struct timer_opts timer_pmtmr = { .monotonic_clock = monotonic_clock_pmtmr, .delay = delay_pmtmr, .read_timer = read_timer_tsc, + .resume = pmtmr_resume, }; struct init_timer_opts __initdata timer_pmtmr_init = { diff -puN arch/i386/kernel/timers/timer_tsc.c~add-suspend-resume-for-timer arch/i386/kernel/timers/timer_tsc.c --- 25/arch/i386/kernel/timers/timer_tsc.c~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/arch/i386/kernel/timers/timer_tsc.c Fri Jun 24 16:59:04 2005 @@ -543,6 +543,19 @@ static int __init init_tsc(char* overrid return -ENODEV; } +static int tsc_resume(void) +{ + write_seqlock(&monotonic_lock); + /* Assume this is the last mark offset time */ + rdtsc(last_tsc_low, last_tsc_high); +#ifdef CONFIG_HPET_TIMER + if (is_hpet_enabled() && hpet_use_timer) + hpet_last = hpet_readl(HPET_COUNTER); +#endif + write_sequnlock(&monotonic_lock); + return 0; +} + #ifndef CONFIG_X86_TSC /* disable flag for tsc. Takes effect by clearing the TSC cpu flag * in cpu/common.c */ @@ -573,6 +586,7 @@ static struct timer_opts timer_tsc = { .monotonic_clock = monotonic_clock_tsc, .delay = delay_tsc, .read_timer = read_timer_tsc, + .resume = tsc_resume, }; struct init_timer_opts __initdata timer_tsc_init = { diff -puN include/asm-i386/timer.h~add-suspend-resume-for-timer include/asm-i386/timer.h --- 25/include/asm-i386/timer.h~add-suspend-resume-for-timer Fri Jun 24 16:59:04 2005 +++ 25-akpm/include/asm-i386/timer.h Fri Jun 24 16:59:04 2005 @@ -1,6 +1,7 @@ #ifndef _ASMi386_TIMER_H #define _ASMi386_TIMER_H #include +#include /** * struct timer_ops - used to define a timer source @@ -23,6 +24,8 @@ struct timer_opts { unsigned long long (*monotonic_clock)(void); void (*delay)(unsigned long); unsigned long (*read_timer)(void); + int (*suspend)(pm_message_t state); + int (*resume)(void); }; struct init_timer_opts { _