diff options
author | john stultz <johnstul@us.ibm.com> | 2006-06-26 03:25:16 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@g5.osdl.org> | 2006-06-26 12:58:21 -0400 |
commit | 6415ce9a922a1446e7ee0ac9b016082232ebe373 (patch) | |
tree | 6f90fe4360ce23d54d23277b588730ccb5ba1d84 /arch/i386/kernel | |
parent | 7f9f303aa33c7acc7b4aa9ebea25cbd990bc707b (diff) |
[PATCH] time: fix time going backward w/ clock=pit
Here is the PIT fix against the TOD patches that Tim pointed out. Many
thanks to Tim for hunting this down.
Cc: Tim Mann <mann@vmware.com>
Signed-off-by: John Stultz <johnstul@us.ibm.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'arch/i386/kernel')
-rw-r--r-- | arch/i386/kernel/i8253.c | 45 |
1 files changed, 39 insertions, 6 deletions
diff --git a/arch/i386/kernel/i8253.c b/arch/i386/kernel/i8253.c index 38aa7f19f1d3..477b24daff53 100644 --- a/arch/i386/kernel/i8253.c +++ b/arch/i386/kernel/i8253.c | |||
@@ -41,9 +41,25 @@ static cycle_t pit_read(void) | |||
41 | { | 41 | { |
42 | unsigned long flags; | 42 | unsigned long flags; |
43 | int count; | 43 | int count; |
44 | u64 jifs; | 44 | u32 jifs; |
45 | static int old_count; | ||
46 | static u32 old_jifs; | ||
45 | 47 | ||
46 | spin_lock_irqsave(&i8253_lock, flags); | 48 | spin_lock_irqsave(&i8253_lock, flags); |
49 | /* | ||
50 | * Although our caller may have the read side of xtime_lock, | ||
51 | * this is now a seqlock, and we are cheating in this routine | ||
52 | * by having side effects on state that we cannot undo if | ||
53 | * there is a collision on the seqlock and our caller has to | ||
54 | * retry. (Namely, old_jifs and old_count.) So we must treat | ||
55 | * jiffies as volatile despite the lock. We read jiffies | ||
56 | * before latching the timer count to guarantee that although | ||
57 | * the jiffies value might be older than the count (that is, | ||
58 | * the counter may underflow between the last point where | ||
59 | * jiffies was incremented and the point where we latch the | ||
60 | * count), it cannot be newer. | ||
61 | */ | ||
62 | jifs = jiffies; | ||
47 | outb_p(0x00, PIT_MODE); /* latch the count ASAP */ | 63 | outb_p(0x00, PIT_MODE); /* latch the count ASAP */ |
48 | count = inb_p(PIT_CH0); /* read the latched count */ | 64 | count = inb_p(PIT_CH0); /* read the latched count */ |
49 | count |= inb_p(PIT_CH0) << 8; | 65 | count |= inb_p(PIT_CH0) << 8; |
@@ -55,12 +71,29 @@ static cycle_t pit_read(void) | |||
55 | outb(LATCH >> 8, PIT_CH0); | 71 | outb(LATCH >> 8, PIT_CH0); |
56 | count = LATCH - 1; | 72 | count = LATCH - 1; |
57 | } | 73 | } |
58 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
59 | 74 | ||
60 | jifs = jiffies_64; | 75 | /* |
76 | * It's possible for count to appear to go the wrong way for a | ||
77 | * couple of reasons: | ||
78 | * | ||
79 | * 1. The timer counter underflows, but we haven't handled the | ||
80 | * resulting interrupt and incremented jiffies yet. | ||
81 | * 2. Hardware problem with the timer, not giving us continuous time, | ||
82 | * the counter does small "jumps" upwards on some Pentium systems, | ||
83 | * (see c't 95/10 page 335 for Neptun bug.) | ||
84 | * | ||
85 | * Previous attempts to handle these cases intelligently were | ||
86 | * buggy, so we just do the simple thing now. | ||
87 | */ | ||
88 | if (count > old_count && jifs == old_jifs) { | ||
89 | count = old_count; | ||
90 | } | ||
91 | old_count = count; | ||
92 | old_jifs = jifs; | ||
93 | |||
94 | spin_unlock_irqrestore(&i8253_lock, flags); | ||
61 | 95 | ||
62 | jifs -= INITIAL_JIFFIES; | 96 | count = (LATCH - 1) - count; |
63 | count = (LATCH-1) - count; | ||
64 | 97 | ||
65 | return (cycle_t)(jifs * LATCH) + count; | 98 | return (cycle_t)(jifs * LATCH) + count; |
66 | } | 99 | } |
@@ -69,7 +102,7 @@ static struct clocksource clocksource_pit = { | |||
69 | .name = "pit", | 102 | .name = "pit", |
70 | .rating = 110, | 103 | .rating = 110, |
71 | .read = pit_read, | 104 | .read = pit_read, |
72 | .mask = CLOCKSOURCE_MASK(64), | 105 | .mask = CLOCKSOURCE_MASK(32), |
73 | .mult = 0, | 106 | .mult = 0, |
74 | .shift = 20, | 107 | .shift = 20, |
75 | }; | 108 | }; |