All of lore.kernel.org
 help / color / mirror / Atom feed
* Re:[parisc-linux] [PATCH] timer_interrupt and gettimeoffset.
@ 2006-08-30 16:38 Joel Soete
  2006-08-30 16:52 ` [parisc-linux] " Grant Grundler
  0 siblings, 1 reply; 39+ messages in thread
From: Joel Soete @ 2006-08-30 16:38 UTC (permalink / raw)
  To: carlos; +Cc: willy, James.Bottomley, parisc-linux

> A review of 'arch/parisc/kernel/time.c' and in particular
> 'timer_interrupt' and 'gettimeoffset'  show that this code has a
> couple of design flaws.
> 
> 1. cr16 is treated as signed.
> 2. No attempt is made to handle cr16 wrapping.
> 3. jiffies vs. wall_jiffies adjustment is incorrect.
> 
> I have rewritten 'timer_interrupt'.
> 
> A. cr16 is treated as an unsigned long.
> B. The following 3 scenarios exist.
> 
> 1. The timer interrupt fires, and 'now' comes after 'next_tick'
mmm (just a thought) what's up if now comes after next_tick but cr16 has
already wrap 1, 2, ... times?

> 2. The timer interrupt fires, and 'now' has wrapped and is before 'next=
_tick'
> 3. The timer interrupt fires, and 'now' is *just* before 'next_tick'
> 
> In theory 99% of the time we should be in scenario 1. On occasion we
> may miss a timer interrupt, end up close to wrapping, and we get
> scenario 2. Scenario 3 is just plain wrong and for now I will BUG() if
> the timer fires before we intended.
> 
> I have adjusted 'gettimeoffset' in the following fashion:
> 
> A. cr16 is treated as an unsigned long.
> B. Never ever ever return a negative adjustment.
> C. (jiffies - wall_jiffies) adjustment is always positive and added to =
usec.
> D. The 3 timer scenarios (same as above), exist.
> 
> I assert that 'gettimeoffset' should never return a negative value. It
> represents the postive adjustment accounting for the fact that we are
> *part* of the way through a tick.
> 
> The patch is attached. Please review. I booted this on my a500 without
> any problems. I don't really know how to look for problems so I ran
> the glibc testsuite and didn't get any extra regressions.
> 
> The real test will be to run this on a 715/50 and look for Guy's
> reported problems. Guy, would you mind patching your kernel with this
> patch and testing again?
> 
Make sense to me but patch doesn't works for me on b2k 32bit kernel ;-(
it was hanging (no error msg on lcd display) just after "Memory: 262144k
available" then I have to force reboot power off/on cycle.

Thanks,
    Joel

 
> Cheers,
> Carlos.
> 
> =0A=0A----------=0AClub Scarlet : Tout le monde gagne! Si vous devenez =
aujourd'hui Scarlet One grace a un client existant de Scarlet, vous recev=
ez tous les deux un cadeau d'une valeur de 50 euros! Surfez vite sur http=
://www.clubscarlet.be

_______________________________________________
parisc-linux mailing list
parisc-linux@lists.parisc-linux.org
http://lists.parisc-linux.org/mailman/listinfo/parisc-linux

^ permalink raw reply	[flat|nested] 39+ messages in thread
[parent not found: <44FDE867.3090204@scarlet.be>]
* [parisc-linux] [PATCH] timer_interrupt and gettimeoffset.
@ 2006-08-30  4:48 Carlos O'Donell
  2006-08-31  6:53 ` Grant Grundler
  0 siblings, 1 reply; 39+ messages in thread
From: Carlos O'Donell @ 2006-08-30  4:48 UTC (permalink / raw)
  To: parisc-linux, willy, James Bottomley

[-- Attachment #1: Type: text/plain, Size: 1673 bytes --]

A review of 'arch/parisc/kernel/time.c' and in particular
'timer_interrupt' and 'gettimeoffset'  show that this code has a
couple of design flaws.

1. cr16 is treated as signed.
2. No attempt is made to handle cr16 wrapping.
3. jiffies vs. wall_jiffies adjustment is incorrect.

I have rewritten 'timer_interrupt'.

A. cr16 is treated as an unsigned long.
B. The following 3 scenarios exist.

1. The timer interrupt fires, and 'now' comes after 'next_tick'
2. The timer interrupt fires, and 'now' has wrapped and is before 'next_tick'
3. The timer interrupt fires, and 'now' is *just* before 'next_tick'

In theory 99% of the time we should be in scenario 1. On occasion we
may miss a timer interrupt, end up close to wrapping, and we get
scenario 2. Scenario 3 is just plain wrong and for now I will BUG() if
the timer fires before we intended.

I have adjusted 'gettimeoffset' in the following fashion:

A. cr16 is treated as an unsigned long.
B. Never ever ever return a negative adjustment.
C. (jiffies - wall_jiffies) adjustment is always positive and added to usec.
D. The 3 timer scenarios (same as above), exist.

I assert that 'gettimeoffset' should never return a negative value. It
represents the postive adjustment accounting for the fact that we are
*part* of the way through a tick.

The patch is attached. Please review. I booted this on my a500 without
any problems. I don't really know how to look for problems so I ran
the glibc testsuite and didn't get any extra regressions.

The real test will be to run this on a 715/50 and look for Guy's
reported problems. Guy, would you mind patching your kernel with this
patch and testing again?

Cheers,
Carlos.

[-- Attachment #2: patch-2006-08-29-time.diff --]
[-- Type: text/x-patch, Size: 5421 bytes --]

diff --git a/arch/parisc/kernel/time.c b/arch/parisc/kernel/time.c
index 5facc9b..ce7dc5e 100644
--- a/arch/parisc/kernel/time.c
+++ b/arch/parisc/kernel/time.c
@@ -44,32 +44,57 @@ #endif
 
 irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	long now;
-	long next_tick;
-	int nticks;
+	unsigned long now;
+	unsigned long next_tick;
+	unsigned long next_next_tick;
+	unsigned long difference;
+        unsigned long remainder;
+	int nticks = 0;
 	int cpu = smp_processor_id();
 
 	profile_tick(CPU_PROFILING, regs);
 
-	now = mfctl(16);
-	/* initialize next_tick to time at last clocktick */
+	/* Initialize next_tick to the expected tick time. */
 	next_tick = cpu_data[cpu].it_value;
+	next_next_tick = next_tick + clocktick;
 
-	/* since time passes between the interrupt and the mfctl()
-	 * above, it is never true that last_tick + clocktick == now.  If we
-	 * never miss a clocktick, we could set next_tick = last_tick + clocktick
-	 * but maybe we'll miss ticks, hence the loop.
-	 *
-	 * Variables are *signed*.
-	 */
+	/* Get current interval timer reading, reads as 64/32 bit
+           value depending if you have a 64/32 bit machine.  */
+	now = mfctl(16);
 
-	nticks = 0;
-	while((next_tick - now) < halftick) {
-		next_tick += clocktick;
+	/* Determine how much time elapsed.  */
+	if (now > next_tick) {
+		/* Scenario 1: "now" is late.  */
+		nticks = (now - next_tick) / clocktick;
+		remainder = (now - next_tick) % clocktick;
+	}
+        else {
+		/* "now" is either early or cr16 wrapped.  */
+		if (next_next_tick < next_tick) {
+			/* Scenario 2: A missed clock tick would wrap. */
+			difference = ~0UL - (next_tick - now);
+			nticks = difference / clocktick;
+			remainder = difference % clocktick;
+		} else {
+			/* Scenario 3: We didn't miss a clock tick. 
+			   "now" is early?  */
+			printk ("timer_interrupt: Now is early, now = %lx, next_tick = %lx, clocktick = %lx\n", now, next_tick, clocktick);
+			BUG ();
+		}
+	}
+
+	/* Check the remainder to see how far we are into the next tick?  */
+	if (remainder > halftick) {
+		/* More than 1/2 way in? Count a tick.  */
 		nticks++;
 	}
-	mtctl(next_tick, 16);
-	cpu_data[cpu].it_value = next_tick;
+
+	/* Add any full ticks that have elapsed.  */
+	next_next_tick += nticks * clocktick;
+
+        /* Only bottom 32-bits of next_tick are written to cr16.  */
+	mtctl(next_next_tick, 16);
+	cpu_data[cpu].it_value = next_next_tick;
 
 	while (nticks--) {
 #ifdef CONFIG_SMP
@@ -124,21 +149,47 @@ #ifndef CONFIG_SMP
 	 *    Once parisc-linux learns the cr16 difference between processors,
 	 *    this could be made to work.
 	 */
-	long last_tick;
-	long elapsed_cycles;
+	unsigned long now;
+	unsigned long last_tick;
+	unsigned long next_tick;
+	unsigned long next_next_tick;
+	unsigned long elapsed_cycles;
+	unsigned long usec;
+	unsigned long lost;
 
 	/* it_value is the intended time of the next tick */
-	last_tick = cpu_data[smp_processor_id()].it_value;
+	next_tick = cpu_data[smp_processor_id()].it_value;
+	next_next_tick = next_tick + clocktick;
 
-	/* Subtract one tick and account for possible difference between
-	 * when we expected the tick and when it actually arrived.
-	 * (aka wall vs real)
-	 */
-	last_tick -= clocktick * (jiffies - wall_jiffies + 1);
-	elapsed_cycles = mfctl(16) - last_tick;
+	/* This represents lost jiffies.  */
+	lost = clocktick * (jiffies - wall_jiffies); 
+	
+	/* We roll back 1 tick.  */
+	last_tick = next_tick - clocktick;
+
+	/* Read the hardware interval timer.  */
+	now = mfctl(16);
 
-	/* the precision of this math could be improved */
-	return elapsed_cycles / (PAGE0->mem_10msec / 10000);
+	if (now > last_tick) {
+		/* Scenario 1: "now" is later than last_tick.  */
+		elapsed_cycles = now - last_tick;
+	} else {
+		/* "now" is either early or cr16 wrapped.  */
+		if (next_next_tick < last_tick) {
+			/* Scenario 2: A missed clock tick would wrap. */
+			elapsed_cycles = ~0UL - (last_tick - now);
+		} else {
+			/* Scenario 3: We didn't miss a clock tick. 
+			   "now" is early?  */
+			printk ("gettimeoffset: Now is early, now = %lx, last_tick = %lx, next_tick = %lx, clocktick = %lx\n", now, last_tick, next_tick, clocktick);
+			BUG ();
+		}
+	}
+
+	/* FIXME: Improve precision. */
+	usec = elapsed_cycles * 10000 / PAGE0->mem_10msec;
+	usec += lost;
+	return usec;
 #else
 	return 0;
 #endif
@@ -149,6 +200,7 @@ do_gettimeofday (struct timeval *tv)
 {
 	unsigned long flags, seq, usec, sec;
 
+	/* Hold xtime_lock and adjust timeval.  */
 	do {
 		seq = read_seqbegin_irqsave(&xtime_lock, flags);
 		usec = gettimeoffset();
@@ -156,25 +208,13 @@ do_gettimeofday (struct timeval *tv)
 		usec += (xtime.tv_nsec / 1000);
 	} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
 
-	if (unlikely(usec > LONG_MAX)) {
-		/* This can happen if the gettimeoffset adjustment is
-		 * negative and xtime.tv_nsec is smaller than the
-		 * adjustment */
-		printk(KERN_ERR "do_gettimeofday() spurious xtime.tv_nsec of %ld\n", usec);
-		usec += USEC_PER_SEC;
-		--sec;
-		/* This should never happen, it means the negative
-		 * time adjustment was more than a second, so there's
-		 * something seriously wrong */
-		BUG_ON(usec > LONG_MAX);
-	}
-
-
+	/* Move adjusted usec's into sec's.  */
 	while (usec >= USEC_PER_SEC) {
 		usec -= USEC_PER_SEC;
 		++sec;
 	}
 
+	/* Return adjusted result.  */
 	tv->tv_sec = sec;
 	tv->tv_usec = usec;
 }

[-- Attachment #3: Type: text/plain, Size: 169 bytes --]

_______________________________________________
parisc-linux mailing list
parisc-linux@lists.parisc-linux.org
http://lists.parisc-linux.org/mailman/listinfo/parisc-linux

^ permalink raw reply related	[flat|nested] 39+ messages in thread

end of thread, other threads:[~2006-09-06  0:24 UTC | newest]

Thread overview: 39+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2006-08-30 16:38 Re:[parisc-linux] [PATCH] timer_interrupt and gettimeoffset Joel Soete
2006-08-30 16:52 ` [parisc-linux] " Grant Grundler
2006-08-30 20:23   ` Carlos O'Donell
2006-09-02 15:52     ` James Bottomley
2006-09-03 15:00       ` Carlos O'Donell
2006-09-03 15:17         ` James Bottomley
2006-09-03 16:14         ` James Bottomley
2006-09-03 19:31           ` Grant Grundler
2006-09-04 15:51             ` James Bottomley
2006-09-04 16:57               ` John David Anglin
2006-09-04 17:23                 ` James Bottomley
2006-09-04 19:12                   ` John David Anglin
2006-09-04 21:21                 ` Grant Grundler
2006-09-04 22:47                   ` James Bottomley
2006-09-04 23:52                     ` John David Anglin
2006-09-05  5:12                       ` Grant Grundler
2006-09-05 15:53                         ` James Bottomley
2006-09-05 22:49                           ` Grant Grundler
2006-09-05 22:59                             ` James Bottomley
2006-09-05 23:41                             ` John David Anglin
2006-09-06  0:24                         ` John David Anglin
2006-09-03 19:38       ` Grant Grundler
2006-09-03 19:59         ` John David Anglin
2006-09-04  0:09           ` Grant Grundler
2006-09-04  0:58             ` John David Anglin
2006-09-04  3:51             ` John David Anglin
2006-09-04 15:49         ` James Bottomley
     [not found] <44FDE867.3090204@scarlet.be>
2006-09-05 21:35 ` John David Anglin
  -- strict thread matches above, loose matches on Subject: below --
2006-08-30  4:48 Carlos O'Donell
2006-08-31  6:53 ` Grant Grundler
2006-08-31 18:52   ` Carlos O'Donell
2006-08-31 21:46     ` Grant Grundler
2006-09-03 14:54       ` Carlos O'Donell
2006-09-03 19:55         ` Grant Grundler
2006-09-03 20:29           ` James Bottomley
2006-09-03 20:30           ` Carlos O'Donell
2006-09-03 21:22             ` John David Anglin
2006-09-01 22:48   ` Grant Grundler
2006-09-01 22:59     ` Grant Grundler

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.