Skip to contentSkip to conversation

add suspend/resume for timer

PATCH In review Shaohua Li 2 participants 2 messages Jun 23, 2005 – Jun 25, 2005

lore
Share & tools
lore

Shaohua Li

author
#0 lore raw

PATCH 1/1 add suspend/resume for timer

Hi,
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.
I know Linux will introduce a new timer core system, but it maybe take a long time, so I send this patch to fix bugs at hand. If the new timer system will be merged soon, please ignore this one.

Thanks,
Shaohua

---

 linux-2.6.12-mm1-root/arch/i386/kernel/time.c              |   10 ++++
 linux-2.6.12-mm1-root/arch/i386/kernel/timers/timer_hpet.c |   14 ++++++
 linux-2.6.12-mm1-root/arch/i386/kernel/timers/timer_pit.c  |   27 -------------
 linux-2.6.12-mm1-root/arch/i386/kernel/timers/timer_pm.c   |    9 ++++
 linux-2.6.12-mm1-root/arch/i386/kernel/timers/timer_tsc.c  |   14 ++++++
 linux-2.6.12-mm1-root/include/asm-i386/timer.h             |    3 +
 6 files changed, 50 insertions(+), 27 deletions(-)
include/asm-i386/timer.h renamed from include/asm-i386/timer.h~last_tick_fix +3 -0
diff -puN include/asm-i386/timer.h~last_tick_fix include/asm-i386/timer.h
@@ -1,6 +1,7 @@
 #ifndef _ASMi386_TIMER_H
 #define _ASMi386_TIMER_H
 #include <linux/init.h>
+#include <linux/pm.h>
 
 /**
  * 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 {
arch/i386/kernel/timers/timer_hpet.c renamed from arch/i386/kernel/timers/timer_hpet.c~last_tick_fix +14 -0
diff -puN arch/i386/kernel/timers/timer_hpet.c~last_tick_fix arch/i386/kernel/timers/timer_hpet.c
@@ -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 __ca
 	.monotonic_clock =	monotonic_clock_hpet,
 	.delay = 		delay_hpet,
 	.read_timer = 		read_timer_tsc,
+	.resume	=		hpet_resume,
 };
 
 struct init_timer_opts __initdata timer_hpet_init = {
arch/i386/kernel/timers/timer_pit.c renamed from arch/i386/kernel/timers/timer_pit.c~last_tick_fix +0 -27
diff -puN arch/i386/kernel/timers/timer_pit.c~last_tick_fix arch/i386/kernel/timers/timer_pit.c
@@ -177,30 +177,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);
-
arch/i386/kernel/timers/timer_tsc.c renamed from arch/i386/kernel/timers/timer_tsc.c~last_tick_fix +14 -0
diff -puN arch/i386/kernel/timers/timer_tsc.c~last_tick_fix arch/i386/kernel/timers/timer_tsc.c
@@ -544,6 +544,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 */
@@ -574,6 +587,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 = {
arch/i386/kernel/timers/timer_pm.c renamed from arch/i386/kernel/timers/timer_pm.c~last_tick_fix +9 -0
diff -puN arch/i386/kernel/timers/timer_pm.c~last_tick_fix arch/i386/kernel/timers/timer_pm.c
@@ -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 = {
arch/i386/kernel/time.c renamed from arch/i386/kernel/time.c~last_tick_fix +10 -0
diff -puN arch/i386/kernel/time.c~last_tick_fix arch/i386/kernel/time.c
@@ -380,6 +380,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)
 {
 	/*
@@ -388,6 +389,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;
 }
 
@@ -401,6 +406,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);
@@ -410,6 +416,10 @@ static int timer_resume(struct sys_devic
 	jiffies += sleep_length;
 	wall_jiffies += sleep_length;
 	touch_softlockup_watchdog();
+	if (last_timer->resume)
+		last_timer->resume();
+	cur_timer = last_timer;
+	last_timer = NULL;
 	return 0;
 }
 

_

#1 lore raw

Hi!

Quoted 7 lines

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.
I know Linux will introduce a new timer core system, but it maybe take a long time, so I send this patch to fix bugs at hand. If the new timer system will be merged soon, please ignore this one.

Patch looks good to me. Setting up timers as system devices was ugly, and this fixes it. Good.
Pavel

Signature · 1 line
Boycott Kodak -- for their patent abuse against Java.