This patch uses the lost tick information returned by mark_offset()
function in dyntick, to recover time.

Original code by Srivatsa Vaddagiri <vatsa@in.ibm.com>
Updated by Con Kolivas <kernel@kolivas.org>

Signed-off-by: Con Kolivas <kernel@kolivas.org>

 arch/i386/kernel/time.c                 |   13 ++++++++-----
 arch/i386/kernel/timers/timer_cyclone.c |    4 +++-
 arch/i386/kernel/timers/timer_hpet.c    |    4 +++-
 arch/i386/kernel/timers/timer_none.c    |    3 ++-
 arch/i386/kernel/timers/timer_pit.c     |    3 ++-
 arch/i386/kernel/timers/timer_pm.c      |    3 ++-
 arch/i386/kernel/timers/timer_tsc.c     |   13 +++++++++----
 include/asm-i386/timer.h                |    2 +-
 8 files changed, 30 insertions(+), 15 deletions(-)

Index: linux-2.6.16-rc4-dt/arch/i386/kernel/time.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/time.c	2006-02-18 10:36:52.000000000 +1100
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/time.c	2006-02-23 11:24:02.000000000 +1100
@@ -245,7 +245,7 @@ EXPORT_SYMBOL(profile_pc);
  * timer_interrupt() needs to keep up the real-time clock,
  * as well as call the "do_timer()" routine every clocktick
  */
-static inline void do_timer_interrupt(int irq, struct pt_regs *regs)
+static inline void do_timer_interrupt(int irq, struct pt_regs *regs, int lost)
 {
 #ifdef CONFIG_X86_IO_APIC
 	if (timer_ack) {
@@ -263,7 +263,8 @@ static inline void do_timer_interrupt(in
 	}
 #endif
 
-	do_timer_interrupt_hook(regs);
+	if (!dyntick_enabled() || lost)
+		do_timer_interrupt_hook(regs);
 
 
 	if (MCA_bus) {
@@ -288,6 +289,8 @@ static inline void do_timer_interrupt(in
  */
 irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+	int lost;
+
 	/*
 	 * Here we are in the timer irq handler. We just have irqs locally
 	 * disabled but we don't know if the timer_bh is running on the other
@@ -297,9 +300,9 @@ irqreturn_t timer_interrupt(int irq, voi
 	 */
 	write_seqlock(&xtime_lock);
 
-	cur_timer->mark_offset();
- 
-	do_timer_interrupt(irq, regs);
+	lost = cur_timer->mark_offset();
+
+	do_timer_interrupt(irq, regs, lost);
 
 	write_sequnlock(&xtime_lock);
 
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_cyclone.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_cyclone.c	2005-08-29 13:31:19.000000000 +1000
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_cyclone.c	2006-02-23 11:24:02.000000000 +1100
@@ -45,7 +45,7 @@ static seqlock_t monotonic_lock = SEQLOC
 	} while (high != cyclone_timer[1]);
 
 
-static void mark_offset_cyclone(void)
+static int mark_offset_cyclone(void)
 {
 	unsigned long lost, delay;
 	unsigned long delta = last_cyclone_low;
@@ -101,6 +101,8 @@ static void mark_offset_cyclone(void)
 	 */
 	if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
 		jiffies_64++;
+
+	return lost;
 }
 
 static unsigned long get_offset_cyclone(void)
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_hpet.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_hpet.c	2006-01-03 17:36:14.000000000 +1100
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_hpet.c	2006-02-23 11:24:02.000000000 +1100
@@ -101,7 +101,7 @@ static unsigned long get_offset_hpet(voi
 	return edx;
 }
 
-static void mark_offset_hpet(void)
+static int mark_offset_hpet(void)
 {
 	unsigned long long this_offset, last_offset;
 	unsigned long offset;
@@ -124,6 +124,8 @@ static void mark_offset_hpet(void)
 	this_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
 	monotonic_base += cycles_2_ns(this_offset - last_offset);
 	write_sequnlock(&monotonic_lock);
+
+	return 1;
 }
 
 static void delay_hpet(unsigned long loops)
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_none.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_none.c	2004-12-25 10:14:46.000000000 +1100
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_none.c	2006-02-23 11:24:02.000000000 +1100
@@ -1,9 +1,10 @@
 #include <linux/init.h>
 #include <asm/timer.h>
 
-static void mark_offset_none(void)
+static int mark_offset_none(void)
 {
 	/* nothing needed */
+	return 1;
 }
 
 static unsigned long get_offset_none(void)
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_pit.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_pit.c	2006-01-03 17:36:14.000000000 +1100
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_pit.c	2006-02-23 11:24:02.000000000 +1100
@@ -32,9 +32,10 @@ static int __init init_pit(char* overrid
 	return 0;
 }
 
-static void mark_offset_pit(void)
+static int mark_offset_pit(void)
 {
 	/* nothing needed */
+	return 1;
 }
 
 static unsigned long long monotonic_clock_pit(void)
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_pm.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_pm.c	2005-10-28 20:21:34.000000000 +1000
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_pm.c	2006-02-23 11:24:02.000000000 +1100
@@ -148,7 +148,7 @@ static inline u32 cyc2us(u32 cycles)
  * this gets called during each timer interrupt
  *   - Called while holding the writer xtime_lock
  */
-static void mark_offset_pmtmr(void)
+static int mark_offset_pmtmr(void)
 {
 	u32 lost, delta, last_offset;
 	static int first_run = 1;
@@ -184,6 +184,7 @@ static void mark_offset_pmtmr(void)
 		first_run = 0;
 		offset_delay = 0;
 	}
+	return lost;
 }
 
 static int pmtmr_resume(void)
Index: linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_tsc.c
===================================================================
--- linux-2.6.16-rc4-dt.orig/arch/i386/kernel/timers/timer_tsc.c	2006-02-18 10:36:52.000000000 +1100
+++ linux-2.6.16-rc4-dt/arch/i386/kernel/timers/timer_tsc.c	2006-02-23 11:24:02.000000000 +1100
@@ -181,10 +181,11 @@ static void delay_tsc(unsigned long loop
 }
 
 #ifdef CONFIG_HPET_TIMER
-static void mark_offset_tsc_hpet(void)
+static int mark_offset_tsc_hpet(void)
 {
 	unsigned long long this_offset, last_offset;
- 	unsigned long offset, temp, hpet_current;
+	unsigned long offset, temp, hpet_current;
+	int lost_ticks = 0;
 
 	write_seqlock(&monotonic_lock);
 	last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
@@ -207,7 +208,7 @@ static void mark_offset_tsc_hpet(void)
 	offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
 	if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))
 					&& detect_lost_ticks) {
-		int lost_ticks = (offset - hpet_last) / hpet_tick;
+		lost_ticks = (offset - hpet_last) / hpet_tick;
 		jiffies_64 += lost_ticks;
 	}
 	hpet_last = hpet_current;
@@ -228,6 +229,8 @@ static void mark_offset_tsc_hpet(void)
 	delay_at_last_interrupt = hpet_current - offset;
 	ASM_MUL64_REG(temp, delay_at_last_interrupt,
 			hpet_usec_quotient, delay_at_last_interrupt);
+
+	return lost_ticks;
 }
 #endif
 
@@ -361,7 +364,7 @@ int recalibrate_cpu_khz(void)
 }
 EXPORT_SYMBOL(recalibrate_cpu_khz);
 
-static void mark_offset_tsc(void)
+static int mark_offset_tsc(void)
 {
 	unsigned long lost,delay;
 	unsigned long delta = last_tsc_low;
@@ -471,6 +474,8 @@ static void mark_offset_tsc(void)
 	 */
 	if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
 		jiffies_64++;
+
+	return lost;
 }
 
 static int __init init_tsc(char* override)
Index: linux-2.6.16-rc4-dt/include/asm-i386/timer.h
===================================================================
--- linux-2.6.16-rc4-dt.orig/include/asm-i386/timer.h	2005-10-28 20:22:01.000000000 +1000
+++ linux-2.6.16-rc4-dt/include/asm-i386/timer.h	2006-02-23 11:24:02.000000000 +1100
@@ -19,7 +19,7 @@
  */
 struct timer_opts {
 	char* name;
-	void (*mark_offset)(void);
+	int (*mark_offset)(void);
 	unsigned long (*get_offset)(void);
 	unsigned long long (*monotonic_clock)(void);
 	void (*delay)(unsigned long);
