2005-11-12 04:48:54

by john stultz

[permalink] [raw]
Subject: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

All,
I had hoped to submit this to -mm today, but since Ingo pointed
out an issue in the __delay code, I'm going to wait a week so the new fix
can be better tested.

The following patchset applies against 2.6.14-mm2 (with some offsets
and fuzz) and provides a generic timekeeping subsystem that is independent
of the timer interrupt. This allows for robust and correct behavior in
cases of late or lost ticks, avoids interpolation errors, reduces
duplication in arch specific code, and allows or assists future changes
such as high-res timers, dynamic ticks, or realtime preemption.
Additionally, it provides finer nanosecond resolution values to the
clock_gettime functions.

The patch set provides the minimal NTP changes, the clocksource
abstraction, the core timekeeping code as well as the code to convert
the i386 and x86-64 archs. I have started on converting more arches, but
for now I'm focusing on i386 and x86-64.

Thomas Gleixner has been quite helpful in deeply reviewing and
suggesting changes to the code, so many thanks to him for his help!

New in this release:
o Use ktimer for periodic_hook
o minor performance tweaks
o i386 __delay fix
o docs and howtos in Documentation/timekeeping.txt

Still on the TODO list:
o Get a weeks worth of testing
o Submit to -mm (planned for the next release)

I'd like to thank the following people who have contributed ideas,
criticism, testing and code that has helped shape this work:

George Anzinger, Nish Aravamudan, Max Asbock, Dominik Brodowski, Thomas
Gleixner, Darren Hart, Christoph Lameter, Matt Mackal, Keith Mannthey,
Ingo Molnar, Martin Schwidefsky, Frank Sorenson, Ulrich Windl, Darrick
Wong, Roman Zippel and any others whom I've accidentally forgotten.

Please let me know if you have any comments or feedback.

thanks
-john


2005-11-12 04:49:00

by john stultz

[permalink] [raw]
Subject: [PATCH 1/13] Time: Reduced NTP rework (part 1)

All,
With Roman's suggestions, I've been working on reducing the footprint
of my timeofday patches. This is the first of two patches that reworks
some of the interrupt time NTP adjustments so that it could be re-used
with the timeofday patches. The motivation of the change is to logically
separate the code which adjusts xtime and the code that decides, based
on the NTP state variables, how much per tick to adjust xtime.

Thus this patch should not affect the existing behavior, but just
separate the logical functionality so it can be re-used.

This patch applies on top of my ntp-shift-right patch I posted awhile
back.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

timer.c | 96 ++++++++++++++++++++++++++++++++++++++++++----------------------
1 files changed, 63 insertions(+), 33 deletions(-)

linux-2.6.14_timeofday-ntp-part1_B10.patch
============================================
diff --git a/kernel/timer.c b/kernel/timer.c
index 636c669..30a1979 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -589,6 +589,7 @@ static long time_adj; /* tick adjust (
long time_reftime; /* time at last adjustment (s) */
long time_adjust;
long time_next_adjust;
+long time_adjust_step; /* per tick time_adjust step */

/*
* this routine handles the overflow of the microsecond field
@@ -725,45 +726,52 @@ static void second_overflow(void)
#endif
}

-/* in the NTP reference this is called "hardclock()" */
-static void update_wall_time_one_tick(void)
+/**
+ * ntp_advance() - increments the NTP state machine
+ *
+ * Must be holding the xtime writelock when calling.
+ *
+ */
+static void ntp_advance(unsigned long interval_ns)
{
- long time_adjust_step, delta_nsec;
+ static unsigned long interval_sum;

- if ((time_adjust_step = time_adjust) != 0 ) {
- /*
- * We are doing an adjtime thing. Prepare time_adjust_step to
- * be within bounds. Note that a positive time_adjust means we
- * want the clock to run faster.
- *
- * Limit the amount of the step to be in the range
- * -tickadj .. +tickadj
- */
- time_adjust_step = min(time_adjust_step, (long)tickadj);
- time_adjust_step = max(time_adjust_step, (long)-tickadj);
+ /* increment the interval sum */
+ interval_sum += interval_ns;
+
+ /* calculate the per tick singleshot adjtime adjustment step */
+ while (interval_ns >= tick_nsec) {
+ time_adjust_step = time_adjust;
+ if (time_adjust_step) {
+ /* We are doing an adjtime thing.
+ *
+ * Prepare time_adjust_step to be within bounds.
+ * Note that a positive time_adjust means we want
+ * the clock to run faster.
+ *
+ * Limit the amount of the step to be in the range
+ * -tickadj .. +tickadj
+ */
+ time_adjust_step = min(time_adjust_step, (long)tickadj);
+ time_adjust_step = max(time_adjust_step,
+ (long)-tickadj);

- /* Reduce by this step the amount of time left */
- time_adjust -= time_adjust_step;
- }
- delta_nsec = tick_nsec + time_adjust_step * 1000;
- /*
- * Advance the phase, once it gets to one microsecond, then
- * advance the tick more.
- */
- time_phase += time_adj;
- if ((time_phase >= FINENSEC) || (time_phase <= -FINENSEC)) {
- long ltemp = shift_right(time_phase, (SHIFT_SCALE - 10));
- time_phase -= ltemp << (SHIFT_SCALE - 10);
- delta_nsec += ltemp;
+ /* Reduce by this step the amount of time left */
+ time_adjust -= time_adjust_step;
+ }
+ interval_ns -= tick_nsec;
}
- xtime.tv_nsec += delta_nsec;
- time_interpolator_update(delta_nsec);

/* Changes by adjtime() do not take effect till next tick. */
if (time_next_adjust != 0) {
time_adjust = time_next_adjust;
time_next_adjust = 0;
}
+
+ while (interval_sum >= NSEC_PER_SEC) {
+ interval_sum -= NSEC_PER_SEC;
+ second_overflow();
+ }
}

/*
@@ -775,14 +783,36 @@ static void update_wall_time_one_tick(vo
*/
static void update_wall_time(unsigned long ticks)
{
+ long delta_nsec;
+
do {
ticks--;
- update_wall_time_one_tick();
- if (xtime.tv_nsec >= 1000000000) {
- xtime.tv_nsec -= 1000000000;
+
+ /* Calculate the nsec delta using the
+ * precomputed NTP adjustments:
+ * tick_nsec, time_adjust_step, time_adj
+ */
+ delta_nsec = tick_nsec + time_adjust_step * 1000;
+ /*
+ * Advance the phase, once it gets to one microsecond, then
+ * advance the tick more.
+ */
+ time_phase += time_adj;
+ if ((time_phase >= FINENSEC) || (time_phase <= -FINENSEC)) {
+ long ltemp = shift_right(time_phase,
+ (SHIFT_SCALE - 10));
+ time_phase -= ltemp << (SHIFT_SCALE - 10);
+ delta_nsec += ltemp;
+ }
+
+ xtime.tv_nsec += delta_nsec;
+ if (xtime.tv_nsec >= NSEC_PER_SEC) {
+ xtime.tv_nsec -= NSEC_PER_SEC;
xtime.tv_sec++;
- second_overflow();
}
+ ntp_advance(tick_nsec);
+ time_interpolator_update(delta_nsec);
+
} while (ticks);
}

2005-11-12 04:49:20

by john stultz

[permalink] [raw]
Subject: [PATCH 3/13] Time: Clocksource Infrastructure

All,
This patch introduces the clocksource management infrastructure. A
clocksource is a driver-like architecture generic abstraction of a
freerunning counter. This patch defines the clocksource structure, and
provides management code for registering, selecting, accessing and
scaling clocksources. The clocksource structure is influenced by the
time_interpolator code, although I feel it has a cleaner interface and
avoids preserving system state in the clocksource structure.

Additionally, this patch includes the trivial jiffies clocksource, a
lowest common denominator clocksource, provided mainly for use as an
example.

This patch applies on top of my ntp cleanup patchset.

Since this patch provides the groundwork for the generic timeofday core,
it will not function without the generic timeofday patches to follow.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

Documentation/kernel-parameters.txt | 14 +
include/linux/clocksource.h | 293 ++++++++++++++++++++++++++++++++++++
kernel/Makefile | 1
kernel/time/Makefile | 1
kernel/time/clocksource.c | 286 +++++++++++++++++++++++++++++++++++
kernel/time/jiffies.c | 74 +++++++++
6 files changed, 665 insertions(+), 4 deletions(-)

linux-2.6.14_timeofday-clocksource-core_B10.patch
============================================
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 5dffcfe..f1e3cb7 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -52,6 +52,7 @@ restrictions referred to are that the re
MTD MTD support is enabled.
NET Appropriate network support is enabled.
NUMA NUMA support is enabled.
+ GENERIC_TIME The generic timeofday code is enabled.
NFS Appropriate NFS support is enabled.
OSS OSS sound support is enabled.
PARIDE The ParIDE subsystem is enabled.
@@ -329,10 +330,11 @@ running once the system is up.
Value can be changed at runtime via
/selinux/checkreqprot.

- clock= [BUGS=IA-32,HW] gettimeofday timesource override.
- Forces specified timesource (if avaliable) to be used
- when calculating gettimeofday(). If specicified
- timesource is not avalible, it defaults to PIT.
+ clock= [BUGS=IA-32, HW] gettimeofday clocksource override.
+ [Deprecated]
+ Forces specified clocksource (if avaliable) to be used
+ when calculating gettimeofday(). If specified
+ clocksource is not avalible, it defaults to PIT.
Format: { pit | tsc | cyclone | pmtmr }

hpet= [IA-32,HPET] option to disable HPET and use PIT.
@@ -1477,6 +1479,10 @@ running once the system is up.

time Show timing data prefixed to each printk message line

+ clocksource= [GENERIC_TIME] Override the default clocksource
+ Override the default clocksource and use the clocksource
+ with the name specified.
+
tipar.timeout= [HW,PPT]
Set communications timeout in tenths of a second
(default 15).
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
new file mode 100644
index 0000000..597cf0b
--- /dev/null
+++ b/include/linux/clocksource.h
@@ -0,0 +1,293 @@
+/* linux/include/linux/clocksource.h
+ *
+ * This file contains the structure definitions for clocksources.
+ *
+ * If you are not a clocksource, or the time of day code, you should
+ * not be including this file!
+ */
+#ifndef _LINUX_CLOCKSOURCE_H
+#define _LINUX_CLOCKSOURCE_H
+
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/list.h>
+#include <asm/io.h>
+#include <asm/div64.h>
+
+/**
+ * struct clocksource - hardware abstraction for a free running counter
+ * Provides mostly state-free accessors to the underlying hardware.
+ *
+ * @name: ptr to clocksource name
+ * @list: list head for registration
+ * @rating: rating value for selection (higher is better)
+ * To avoid rating inflation the following
+ * list should give you a guide as to how
+ * to assign your clocksource a rating
+ * 1-99: Unfit for real use
+ * Only available for bootup and testing purposes.
+ * 100-199: Base level usability.
+ * Functional for real use, but not desired.
+ * 200-299: Good.
+ * A correct and usable clocksource.
+ * 300-399: Desired.
+ * A reasonably fast and accurate clocksource.
+ * 400-499: Perfect
+ * The ideal clocksource. A must-use where
+ * available.
+ * @read: returns a cycle value
+ * @mask: bitmask for two's complement
+ * subtraction of non 64 bit counters
+ * @mult: cycle to nanosecond multiplier
+ * @shift: cycle to nanosecond divisor (power of two)
+ * @update_callback: called when safe to alter clocksource values
+ * @is_continuous: defines if clocksource is free-running.
+ * @vread: vsyscall read function
+ * @vdata: vsyscall data value passed to read function
+ */
+struct clocksource {
+ char* name;
+ struct list_head list;
+ int rating;
+ cycle_t (*read)(void);
+ cycle_t mask;
+ u32 mult;
+ u32 shift;
+ int (*update_callback)(void);
+ int is_continuous;
+ cycle_t (*vread)(void*);
+ void* vdata;
+};
+
+
+/**
+ * clocksource_khz2mult - calculates mult from khz and shift
+ * @khz: Clocksource frequency in KHz
+ * @shift_constant: Clocksource shift factor
+ *
+ * Helper functions that converts a khz counter frequency to a timsource
+ * multiplier, given the clocksource shift value
+ */
+static inline u32 clocksource_khz2mult(u32 khz, u32 shift_constant)
+{
+ /* khz = cyc/(Million ns)
+ * mult/2^shift = ns/cyc
+ * mult = ns/cyc * 2^shift
+ * mult = 1Million/khz * 2^shift
+ * mult = 1000000 * 2^shift / khz
+ * mult = (1000000<<shift) / khz
+ */
+ u64 tmp = ((u64)1000000) << shift_constant;
+ tmp += khz/2; /* round for do_div */
+ do_div(tmp, khz);
+ return (u32)tmp;
+}
+
+/**
+ * clocksource_hz2mult - calculates mult from hz and shift
+ * @hz: Clocksource frequency in Hz
+ * @shift_constant: Clocksource shift factor
+ *
+ * Helper functions that converts a hz counter
+ * frequency to a timsource multiplier, given the
+ * clocksource shift value
+ */
+static inline u32 clocksource_hz2mult(u32 hz, u32 shift_constant)
+{
+ /* hz = cyc/(Billion ns)
+ * mult/2^shift = ns/cyc
+ * mult = ns/cyc * 2^shift
+ * mult = 1Billion/hz * 2^shift
+ * mult = 1000000000 * 2^shift / hz
+ * mult = (1000000000<<shift) / hz
+ */
+ u64 tmp = ((u64)1000000000) << shift_constant;
+ tmp += hz/2; /* round for do_div */
+ do_div(tmp, hz);
+ return (u32)tmp;
+}
+
+/**
+ * read_clocksource: - Access the clocksource's current cycle value
+ * @cs: pointer to clocksource being read
+ *
+ * Uses the clocksource to return the current cycle_t value
+ */
+static inline cycle_t read_clocksource(struct clocksource *cs)
+{
+ return cs->read();
+}
+
+/**
+ * ppm_to_mult_adj - Converts shifted ppm values to mult adjustment
+ * @cs: Pointer to clocksource
+ * @ppm: Shifted PPM value
+ *
+ * Helper which converts a shifted ppm value to clocksource mult_adj value.
+ *
+ * XXX - this could use some optimization
+ */
+static inline int ppm_to_mult_adj(struct clocksource *cs, int ppm)
+{
+ u64 mult_adj;
+ int ret_adj;
+
+ /* The basic math is as follows:
+ * cyc * mult/2^shift * (1 + ppm/MILL) = scaled ns
+ * We want to precalculate the ppm factor so it can be added
+ * to the multiplyer saving the extra multiplication step.
+ * cyc * (mult/2^shift + (mult/2^shift) * (ppm/MILL)) =
+ * cyc * (mult/2^shift + (mult*ppm/MILL)/2^shift) =
+ * cyc * (mult + (mult*ppm/MILL))/2^shift =
+ * Thus we want to calculate the value of:
+ * mult*ppm/MILL
+ */
+ mult_adj = abs(ppm);
+ mult_adj = (mult_adj * cs->mult)>>SHIFT_USEC;
+ mult_adj += 1000000/2; /* round for div*/
+ do_div(mult_adj, 1000000);
+ if (ppm < 0)
+ ret_adj = -(int)mult_adj;
+ else
+ ret_adj = (int)mult_adj;
+ return ret_adj;
+}
+
+/**
+ * cyc2ns - converts clocksource cycles to nanoseconds
+ * @cs: Pointer to clocksource
+ * @ntp_adj: Multiplier adjustment value
+ * @cycles: Cycles
+ *
+ * Uses the clocksource and ntp ajdustment to convert cycle_ts to nanoseconds.
+ *
+ * XXX - This could use some mult_lxl_ll() asm optimization
+ */
+static inline nsec_t cyc2ns(struct clocksource *cs, int ntp_adj, cycle_t cycles)
+{
+ u64 ret;
+ ret = (u64)cycles;
+ ret *= (cs->mult + ntp_adj);
+ ret >>= cs->shift;
+ return (nsec_t)ret;
+}
+
+/**
+ * cyc2ns_rem - converts clocksource cycles to nanoseconds w/ remainder
+ * @cs: Pointer to clocksource
+ * @ntp_adj: Multiplier adjustment value
+ * @cycles: Cycles
+ * @rem: Remainder
+ *
+ * Uses the clocksource and ntp ajdustment interval to convert cycle_t to
+ * nanoseconds. Add in remainder portion which is stored in (ns<<cs->shift)
+ * units and save the new remainder off.
+ *
+ * XXX - This could use some mult_lxl_ll() asm optimization.
+ */
+static inline nsec_t cyc2ns_rem(struct clocksource *cs, int ntp_adj, cycle_t cycles, u64* rem)
+{
+ u64 ret;
+ ret = (u64)cycles;
+ ret *= (cs->mult + ntp_adj);
+ if (rem) {
+ ret += *rem;
+ *rem = ret & ((1<<cs->shift)-1);
+ }
+ ret >>= cs->shift;
+ return (nsec_t)ret;
+}
+
+
+/**
+ * struct clocksource_interval - Fixed interval conversion structure
+ *
+ * @cycles: A specified number of cycles
+ * @nsecs: The number of nanoseconds equivalent to the cycles value
+ * @remainder: Non-integer nanosecond remainder stored in (ns<<cs->shift) units
+ * @remainder_ns_overflow: Value at which the remainder is equal to
+ * one second
+ *
+ * This is a optimization structure used by cyc2ns_fixed_rem() to avoid the
+ * multiply in cyc2ns().
+ *
+ * Unless you're the timeofday_periodic_hook, you should not be using this!
+ */
+struct clocksource_interval {
+ cycle_t cycles;
+ nsec_t nsecs;
+ u64 remainder;
+ u64 remainder_ns_overflow;
+};
+
+/**
+ * calculate_clocksource_interval - Calculates a clocksource interval struct
+ *
+ * @c: Pointer to clocksource.
+ * @adj: Multiplyer adjustment.
+ * @length_nsec: Desired interval length in nanoseconds.
+ *
+ * Calculates a fixed cycle/nsec interval for a given clocksource/adjustment
+ * pair and interval request.
+ *
+ * Unless you're the timeofday_periodic_hook, you should not be using this!
+ */
+static inline struct clocksource_interval
+calculate_clocksource_interval(struct clocksource *c, long adj,
+ unsigned long length_nsec)
+{
+ struct clocksource_interval ret;
+ u64 tmp;
+
+ /* XXX - All of this could use a whole lot of optimization */
+ tmp = length_nsec;
+ tmp <<= c->shift;
+ do_div(tmp, c->mult+adj);
+
+ ret.cycles = (cycle_t)tmp;
+ if(ret.cycles == 0)
+ ret.cycles = 1;
+
+ ret.remainder = 0;
+ ret.remainder_ns_overflow = 1 << c->shift;
+ ret.nsecs = cyc2ns_rem(c, adj, ret.cycles, &ret.remainder);
+
+ return ret;
+}
+
+/**
+ * cyc2ns_fixed_rem -
+ * converts clocksource cycles to nanoseconds using fixed intervals
+ *
+ * @interval: precalculated clocksource_interval structure
+ * @cycles: Number of clocksource cycles
+ * @rem: Remainder
+ *
+ * Uses a precalculated fixed cycle/nsec interval to convert cycles to
+ * nanoseconds. Returns the unaccumulated cycles in the cycles pointer as
+ * well as uses and updates the value at the remainder pointer
+ *
+ * Unless you're the timeofday_periodic_hook, you should not be using this!
+ */
+static inline nsec_t cyc2ns_fixed_rem(struct clocksource_interval interval, cycle_t *cycles, u64* rem)
+{
+ nsec_t delta_nsec = 0;
+ while(*cycles > interval.cycles) {
+ delta_nsec += interval.nsecs;
+ *cycles -= interval.cycles;
+ *rem += interval.remainder;
+ while(*rem > interval.remainder_ns_overflow) {
+ *rem -= interval.remainder_ns_overflow;
+ delta_nsec += 1;
+ }
+ }
+ return delta_nsec;
+}
+
+
+/* used to install a new clocksource */
+void register_clocksource(struct clocksource*);
+void reselect_clocksource(void);
+struct clocksource* get_next_clocksource(void);
+#endif
diff --git a/kernel/Makefile b/kernel/Makefile
index 0fa9cb2..4abc89f 100644
--- a/kernel/Makefile
+++ b/kernel/Makefile
@@ -10,6 +10,7 @@ obj-y = sched.o fork.o exec_domain.o
kthread.o wait.o kfifo.o sys_ni.o posix-cpu-timers.o \
ktimers.o

+obj-$(CONFIG_GENERIC_TIME) += time/
obj-$(CONFIG_FUTEX) += futex.o
obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
obj-$(CONFIG_SMP) += cpu.o spinlock.o
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
new file mode 100644
index 0000000..e1dfd8e
--- /dev/null
+++ b/kernel/time/Makefile
@@ -0,0 +1 @@
+obj-y += clocksource.o jiffies.o
diff --git a/kernel/time/clocksource.c b/kernel/time/clocksource.c
new file mode 100644
index 0000000..2ed06e9
--- /dev/null
+++ b/kernel/time/clocksource.c
@@ -0,0 +1,286 @@
+/*
+ * linux/kernel/time/clocksource.c
+ *
+ * This file contains the functions which manage clocksource drivers.
+ *
+ * Copyright (C) 2004, 2005 IBM, John Stultz ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO WishList:
+ * o Allow clocksource drivers to be unregistered
+ * o get rid of clocksource_jiffies extern
+ */
+
+#include <linux/clocksource.h>
+#include <linux/sysdev.h>
+#include <linux/init.h>
+#include <linux/module.h>
+
+/* XXX - Would like a better way for initializing curr_clocksource */
+extern struct clocksource clocksource_jiffies;
+
+/*[Clocksource internal variables]---------
+ * curr_clocksource:
+ * currently selected clocksource. Initialized to clocksource_jiffies.
+ * next_clocksource:
+ * pending next selected clocksource.
+ * clocksource_list:
+ * linked list with the registered clocksources
+ * clocksource_lock:
+ * protects manipulations to curr_clocksource and next_clocksource
+ * and the clocksource_list
+ * override_name:
+ * Name of the user-specified clocksource.
+ */
+static struct clocksource *curr_clocksource = &clocksource_jiffies;
+static struct clocksource *next_clocksource;
+static LIST_HEAD(clocksource_list);
+static seqlock_t clocksource_lock = SEQLOCK_UNLOCKED;
+
+static char override_name[32];
+
+
+/**
+ * get_next_clocksource - Returns the selected clocksource
+ *
+ */
+struct clocksource *get_next_clocksource(void)
+{
+ write_seqlock(&clocksource_lock);
+ if (next_clocksource) {
+ curr_clocksource = next_clocksource;
+ next_clocksource = NULL;
+ }
+ write_sequnlock(&clocksource_lock);
+
+ return curr_clocksource;
+}
+
+
+/**
+ * select_clocksource - Finds the best registered clocksource.
+ *
+ * Private function. Must have a writelock on clocksource_lock
+ * when called.
+ */
+static struct clocksource *select_clocksource(void)
+{
+ struct clocksource *best = NULL;
+ struct list_head *tmp;
+
+ list_for_each(tmp, &clocksource_list) {
+ struct clocksource *src;
+
+ src = list_entry(tmp, struct clocksource, list);
+ if (!best)
+ best = src;
+
+ /* Check for override */
+ if (strlen(src->name) == strlen(override_name) &&
+ !strcmp(src->name, override_name)) {
+ best = src;
+ break;
+ }
+ /* Pick the highest rating */
+ if (src->rating > best->rating)
+ best = src;
+ }
+ return best;
+}
+
+
+/**
+ * is_registered_source - Checks if clocksource is registered
+ * @c: pointer to a clocksource
+ *
+ * Private helper function, should not be used externally.
+ *
+ * Returns one if the clocksource is already registered, zero otherwise.
+ */
+static inline int is_registered_source(struct clocksource *c)
+{
+ struct list_head *tmp;
+ int len = strlen(c->name);
+
+ list_for_each(tmp, &clocksource_list) {
+ struct clocksource *src;
+
+ src = list_entry(tmp, struct clocksource, list);
+ if (strlen(src->name) == len && !strcmp(src->name, c->name))
+ return 1;
+ }
+ return 0;
+}
+
+
+/**
+ * register_clocksource - Used to install new clocksources
+ * @t: clocksource to be registered
+ *
+ */
+void register_clocksource(struct clocksource *c)
+{
+ write_seqlock(&clocksource_lock);
+
+ /* check if clocksource is already registered */
+ if (is_registered_source(c)) {
+ printk("register_clocksource: Cannot register %s. Already registered!",
+ c->name);
+ } else {
+ list_add(&c->list, &clocksource_list);
+ /* select next clocksource */
+ next_clocksource = select_clocksource();
+ }
+ write_sequnlock(&clocksource_lock);
+}
+EXPORT_SYMBOL(register_clocksource);
+
+
+/**
+ * reselect_clocksource - Rescan list for next clocksource
+ *
+ * A quick helper function to be used if a clocksource changes its
+ * rating. Forces the clocksource list to be re-scaned for the best
+ * clocksource.
+ */
+void reselect_clocksource(void)
+{
+ write_seqlock(&clocksource_lock);
+ next_clocksource = select_clocksource();
+ write_sequnlock(&clocksource_lock);
+}
+
+
+/**
+ * sysfs_show_clocksources - sysfs interface for listing clocksource
+ * @dev: unused
+ * @buf: char buffer to be filled with clocksource list
+ *
+ * Provides sysfs interface for listing registered clocksources
+ */
+static ssize_t sysfs_show_clocksources(struct sys_device *dev, char *buf)
+{
+ char* curr = buf;
+ struct list_head *tmp;
+
+ write_seqlock(&clocksource_lock);
+
+ list_for_each(tmp, &clocksource_list) {
+ struct clocksource *src;
+
+ src = list_entry(tmp, struct clocksource, list);
+ /* Mark current clocksource w/ a star */
+ if (src == curr_clocksource)
+ curr += sprintf(curr, "*");
+ curr += sprintf(curr, "%s ", src->name);
+ }
+ write_sequnlock(&clocksource_lock);
+
+ curr += sprintf(curr, "\n");
+ return curr - buf;
+}
+
+
+/**
+ * sysfs_override_clocksource - interface for manually overriding clocksource
+ * @dev: unused
+ * @buf: name of override clocksource
+ * @count: length of buffer
+ *
+ * Takes input from sysfs interface for manually overriding the default
+ * clocksource selction
+ */
+static ssize_t sysfs_override_clocksource(struct sys_device *dev,
+ const char *buf, size_t count)
+{
+ /* Strings from sysfs write are not 0 terminated ! */
+ if (count >= sizeof(override_name))
+ return -EINVAL;
+ /* Strip of \n */
+ if (buf[count-1] == '\n')
+ count--;
+ if (count < 1)
+ return -EINVAL;
+
+ write_seqlock(&clocksource_lock);
+
+ /* copy the name given */
+ memcpy(override_name, buf, count);
+ override_name[count] = 0;
+
+ /* try to select it */
+ next_clocksource = select_clocksource();
+
+ write_sequnlock(&clocksource_lock);
+ return count;
+}
+
+
+/* Sysfs setup bits:
+ */
+static SYSDEV_ATTR(clocksource, 0600, sysfs_show_clocksources, sysfs_override_clocksource);
+
+static struct sysdev_class clocksource_sysclass = {
+ set_kset_name("clocksource"),
+};
+
+static struct sys_device device_clocksource = {
+ .id = 0,
+ .cls = &clocksource_sysclass,
+};
+
+static int init_clocksource_sysfs(void)
+{
+ int error = sysdev_class_register(&clocksource_sysclass);
+ if (!error) {
+ error = sysdev_register(&device_clocksource);
+ if (!error)
+ error = sysdev_create_file(&device_clocksource, &attr_clocksource);
+ }
+ return error;
+}
+device_initcall(init_clocksource_sysfs);
+
+
+/**
+ * boot_override_clocksource - boot clock override
+ * @str: override name
+ *
+ * Takes a clocksource= boot argument and uses it
+ * as the clocksource override name
+ */
+static int __init boot_override_clocksource(char* str)
+{
+ if (str)
+ strlcpy(override_name, str, sizeof(override_name));
+ return 1;
+}
+__setup("clocksource=", boot_override_clocksource);
+
+
+/**
+ * boot_override_clock - Compatibility layer for deprecated boot option
+ * @str: override name
+ *
+ * DEPRECATED! Takes a clock= boot argument and uses it
+ * as the clocksource override name
+ */
+static int __init boot_override_clock(char* str)
+{
+ printk("Warning! clock= boot option is deprecated.\n");
+ return boot_override_clocksource(str);
+}
+__setup("clock=", boot_override_clock);
diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c
new file mode 100644
index 0000000..ad817e0
--- /dev/null
+++ b/kernel/time/jiffies.c
@@ -0,0 +1,74 @@
+/***********************************************************************
+* linux/kernel/time/jiffies.c
+*
+* This file contains the jiffies based clocksource.
+*
+* Copyright (C) 2004, 2005 IBM, John Stultz ([email protected])
+*
+* This program is free software; you can redistribute it and/or modify
+* it under the terms of the GNU General Public License as published by
+* the Free Software Foundation; either version 2 of the License, or
+* (at your option) any later version.
+*
+* This program is distributed in the hope that it will be useful,
+* but WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*
+************************************************************************/
+#include <linux/clocksource.h>
+#include <linux/jiffies.h>
+#include <linux/init.h>
+
+/* The Jiffies based clocksource is the lowest common
+ * denominator clock source which should function on
+ * all systems. It has the same coarse resolution as
+ * the timer interrupt frequency HZ and it suffers
+ * inaccuracies caused by missed or lost timer
+ * interrupts and the inability for the timer
+ * interrupt hardware to accuratly tick at the
+ * requested HZ value. It is also not reccomended
+ * for "tick-less" systems.
+ */
+#define NSEC_PER_JIFFY ((u32)((((u64)NSEC_PER_SEC)<<8)/ACTHZ))
+
+/* Since jiffies uses a simple NSEC_PER_JIFFY multiplier
+ * conversion, the .shift value could be zero. However
+ * this would make NTP adjustments impossible as they are
+ * in units of 1/2^.shift. Thus we use JIFFIES_SHIFT to
+ * shift both the nominator and denominator the same
+ * amount, and give ntp adjustments in units of 1/2^8
+ *
+ * The value 8 is somewhat carefully chosen, as anything
+ * larger can result in overflows. NSEC_PER_JIFFY grows as
+ * HZ shrinks, so values greater then 8 overflow 32bits when
+ * HZ=100.
+ */
+#define JIFFIES_SHIFT 8
+
+static cycle_t jiffies_read(void)
+{
+ cycle_t ret = get_jiffies_64();
+ return ret;
+}
+
+struct clocksource clocksource_jiffies = {
+ .name = "jiffies",
+ .rating = 0, /* lowest rating*/
+ .read = jiffies_read,
+ .mask = (cycle_t)-1,
+ .mult = NSEC_PER_JIFFY << JIFFIES_SHIFT, /* See above for details */
+ .shift = JIFFIES_SHIFT,
+ .is_continuous = 0, /* tick based, not free running */
+};
+
+static int __init init_jiffies_clocksource(void)
+{
+ register_clocksource(&clocksource_jiffies);
+ return 0;
+}
+module_init(init_jiffies_clocksource);

2005-11-12 04:49:41

by john stultz

[permalink] [raw]
Subject: [PATCH 4/13] Time: Generic Timekeeping Infrastructure

All,

This patch implements my generic timeofday framework. This common
infrastructure is intended to be used by any arch to reduce code
duplication, improve robustness in the face of late or lost ticks, and
enable finer granularity timekeeping.

The major change with this code is that it allows timekeeping to be
independent of the timer interrupt. This provides a linear mapping
(ignoring ntp adjustments) between a hardware clocksource counter and
the time of day. This allows for lost ticks or other software delays to
not affect timekeeping.

Included below is timeofday.c (which includes all the time of day
management and accessor functions), and minimal hooks into arch
independent code. This patch does not remove the current timekeeping
code, allowing architectures to move over when they are ready.

This patch applies on top of my clocksource managment patch.

The patch does nothing without at least minimal architecture specific
hooks (i386, x86-64 and other architecture examples to follow), and it
should be able to be applied to a tree without affecting the existing
code.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

Documentation/timekeeping.txt | 246 ++++++++++++++
drivers/char/hangcheck-timer.c | 5
include/asm-generic/timeofday.h | 29 +
include/linux/time.h | 25 +
include/linux/timeofday.h | 44 ++
include/linux/timex.h | 2
include/sound/timer.h | 1
init/main.c | 2
kernel/ktimers.c | 1
kernel/posix-timers.c | 2
kernel/time.c | 17 +
kernel/time/Makefile | 2
kernel/time/timeofday.c | 680 ++++++++++++++++++++++++++++++++++++++++
kernel/timer.c | 6
14 files changed, 1056 insertions(+), 6 deletions(-)

linux-2.6.14_timeofday-core_B10.patch
============================================
diff --git a/Documentation/timekeeping.txt b/Documentation/timekeeping.txt
new file mode 100644
index 0000000..59b7dcc
--- /dev/null
+++ b/Documentation/timekeeping.txt
@@ -0,0 +1,246 @@
+How timekeeping works with CONFIG_GENERIC_TIME
+========================================================================
+
+The generic timekeeping code maintains and allows access to the systems understanding of how much time has passed from a certain point. However, in order to measure the passing of time, the generic timekeeping code relies on the clocksource abstraction. A clocksource abstracts a free running counter who's value increases at a known frequency.
+
+In the generic timekeeping code, we use a pointer to a selected clocksource to measure the passing of time.
+
+struct clocksource *clock
+
+The clocksource has some limitations however. Since its likely of fixed width, it will not increment forever and will overflow. In order to still properly keep time, we must occasionally accumulate an interval of time. In the generic timekeeping code, we accumulate the amount of time system the system booted into the value system_time, which keeps nanosecond resolution in a ktime_t storage.
+
+ktime_t system_time
+
+Since its likely your system has not been running continually since midnight on the 1st of January in 1970, we must provide an offset from that time in accordance with conventions. This only occasionally changed (via settimeofday()) offset is the wall_time_offset value, which is also stored as a ktime_t.
+
+ktime_t wall_time_offset
+
+
+Since we accumulate time in intervals, we need a base cycle value that we can use to generate an offset from the time value kept in system_time. We store this value in cycle_last.
+
+cycle_t cycle_last;
+
+
+Further since all clocks drift somewhat from each other, we use the adjustment values provided via adjtimex() to correct our clocksource frequency for each interval. This frequency adjustment value is stored in ntp_adj.
+
+long ntp_adj;
+
+Now that we've covered the core global variables for timekeeping, lets look at how we maintain these values.
+
+As stated above, we want to avoid the clocksource from overflowing on us, so we accumulate a time interval periodically. This periodic accumulation function is called timeofday_periodic_hook(). In simplified pseudo code, it logically is presented as:
+
+timeofday_periodic_hook():
+ cycle_now = read_clocksource(clock)
+ cycle_delta = (cycle_now - cycle_last) & clock->mask
+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)
+ system_time += nsec
+ cycle_last = cycle_now
+
+ /* do other stuff */
+
+You can see we read the cycle value from the clocksource, calculate a cycle delta for the interval since we last called timeofday_periodic_hook(), convert that cycle delta to a nanosecond interval (for now ignore ntp_adj), add it to the system time and finally set our cycle_last value to cycle_now for the next interval. Using this simple algorithm we can correctly measure and record the passing of time.
+
+But just storing this info isn't very useful, we also want to make it available to be used elsewhere. So how do we provide a notion of how much time has passed inbetween calls to timeofday_periodic_hook()?
+
+First, lets create a function that calculates the time since the last call to timeofday_peridoic_hook().
+
+get_nsec_offset():
+ cycle_now = read_clocksource(clock)
+ cycle_delta = (cycle_now - cycle_last) & clock->mask
+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)
+ return nsec
+
+Here you can see, we read the clocksource, calculate a cycle interval, and convert that to a nanosecond interval. Just like how it is done in timeofday_periodic_hook!
+
+Now lets use this function to provide the number of nanoseconds that the system has been running:
+
+do_monotonic_clock():
+ return system_time + get_nsec_offset()
+
+Here we trivially add the nanosecond offset since the last timeofday_periodic_hook() to the value of system_time which was stored at the last timeofday_periodic_hook().
+
+Note that since we use the same method to calculate time intervals, assuming each function is atomic and the clocksource functions as it should, time cannot go backward!
+
+Now to get the time of day using the standard convention:
+
+do_gettimeofday():
+ return do_monotonic_clock() + wall_time_offset
+
+We simply add the wall_time_offset, and we have the number of nanoseconds since 1970 began!
+
+
+Of course, in real life, things are not so static. We have to handle a number of dynamic values that may change and affect timekeeping. In order to do these safely, we must only change values in-between intervals. This means the periodic_hook call must handle these changes.
+
+Since clocksources can be changed while the system is running, we need to check for and possibly switch to using new clocksources in the periodic_hook call. Further, clocksources may change their frequency. Since this must be done only at a safe point, we use the update_callback function pointer (for more details, see "How to write a clocksource driver" below), this too must be done in-between intervals in the periodic_hook call. Finally, since the ntp adjustment made in the cyc2ns conversion is not static, we need to update the ntp state machine and get a calculate a new adjustment value.
+
+This adds some extra pseudo code to the timeofday_periodic_hook function:
+
+timeofday_periodic_hook():
+ cycle_now = read_clocksource(clock)
+ cycle_delta = (cycle_now - cycle_last) & clock->mask
+ nsec = cyc2ns(clock, cycle_delta, ntp_adj)
+ system_time += nsec
+ cycle_last = cycle_now
+
+ next = get_next_clocksource()
+ if(next != clock):
+ cycle_last = read_clocksource(next)
+ clock = next
+
+ if(clock->update_callback):
+ clock->update_callback()
+
+ ntp_advance(nsec)
+ ppm = ntp_get_ppm_adjustment()
+ ntp_adj = ppm_to_mult_adj(clock, ppm)
+
+
+Unfortunately, the actual timeofday_periodic_hook code is not as simple as this pseudo code. For performance concerns, much has been done to pre-calculate values and use them repeatedly. Thus be aware that the code in timeofday.c is more complex, however the functional logic is the same.
+
+
+How to port an architecture to GENERIC_TIME
+========================================================================
+Porting an architecture to the GENERIC_TIME timekeeping code consists of moving a little bit of code around then deleting a fair amount. It is my hope that this will reduce the arch specific maintenance work around timekeeping.
+
+Porting an arch usually requires the following steps.
+
+1. Define CONFIG_GENERIC_TIME in the arches Kconfig
+2. Implmenting the following functions
+ nsec_t read_persistent_clock(void)
+ void sync_persistent_clock(struct timespec ts)
+3. Removing all of the arch specific timekeeping code
+ do_gettimeofday()
+ do_settimeofday()
+ etc
+4. Implementing clocksource drivers
+ See "How to write a clocksource driver" for more details
+
+The exeptions to the above are:
+
+5. If the arch is has no continuous clocksource
+ A) Implement 1-3 in the above list.
+ B) Define CONFIG_IS_TICK_BASED in arches Kconfig
+ C) Implement the "long arch_getoffset(void)" function
+
+6. If the arch supports vsyscall gettimeofday (see x86_64 for reference)
+ A) Implement 1-4 in the above list
+ B) Define GENERIC_TIME_VSYSCALL
+ C) Implement arch_update_vsyscall_gtod()
+ D) Implement vsyscall gettimeofday (similar to __get_realtime_clock_ts)
+ E) Implement vread functions for supported clocksources
+
+
+
+How to write a clocksource driver.
+========================================================================
+First, a quick summary of what a clocksource driver provides.
+
+Simply put, a clocksource is a abstraction of a free running increasing counter. The abstraction provides the minimal amount of info for that counter to be usable for timekeeping. Those required values are:
+ 1. It's name
+ 2. A rating value for selection priority
+ 3. A read function pointer
+ 4. A mask value for correct twos-complement subtraction
+ 5. A mult and shift pair that aproximate the counter frequency
+ mult/(2^shift) ~= nanoseconds per cycle
+
+Additionally, there are other optionally set values that allow for advanced functinoality. Those values are:
+ 6. The update_callback function.
+ 7. The is_continuous flag.
+ 8. The vread function pointer
+ 9. The vdata pointer value
+
+
+Now lets go over these values in detail.
+
+1. Name.
+ The clocksource's name should be unique since it is used for both identification as well as for manually overriding the default clocksource selection. The name length must be shorter then 32 characters in order for it to be properly overrided.
+
+2. Rating value
+ This rating value is used as a priority value for clocksource selection. It has no direct connection to quality or physical properties of the clocksource, but is to be set and manipulated to guarantee that the best (by no specific metric) clocksource that will provide correct timekeeping is automatically selected. Rating suggestions can be found in include/linux/clocksource.h
+
+3. Read function pointer
+ This pointer should point to a function that returns an unsigned increasing cycle value from the clocksource. The value should have a coverage from zero to the maximum cycle value the clocksource can provide. This does not have to be direct hardware value and can also be a software counter. An example of a software counter is the jiffies clocksource.
+
+4. The mask value
+ This value should be the largest power of two that is smaller then the maximum cycle value. This allows twos complement subtraction to work on overflow boundary conditions if the max value is less then (cycle_t)-1. So for example, if we have a 16 bit counter (ie: one that loops to zero after 0x0000FFFF), the mask would be 0xFFFF. So then when finding the cycle difference around a overflow, where now = 0x0013 and then = 0xFFEE, we can compute the cycle delta properly using the equation:
+ delta = (now - then)&mask
+ delta = (0x0013 - 0xFFEE) & 0xFFFF
+ delta = 0xFFFF0025 & 0xFFFF /* note the unmasked negative value */
+ delta = 0x25
+
+5. The mult and shift pair
+ These 32bit values approximate the nanosecond per cycle frequency of the clocksource using the equation: mult/(2^shift). If you have a khz or hz frequency value, the mult value for a given shift value can be easily calculated using the clocksource_hz2mult() and clocksource_khz2mult() helper functions. When selecting a shift value, it is important to be careful. Larger shift values give a finer precision in the cycle to nanosecond conversion and allows for more exact NTP adjustments. However if you select too large a shift value, the resulting mult value might overflow a cycle_t * mult computation.
+
+
+So if you have a simple hardware counter that does not change frequency, filling in the above should be sufficient for a functional clocksource. But read on for details on implementing a more complex clocksource.
+
+6. The update_callback function pointer.
+ If this function pointer is non-NULL, it will be called every periodic hook when it is safe for the clocksource to change its state. This would be necessary in the case where the counter frequency changes, for example. One user of this function pointer is the TSC clocksource. When the TSC frequency changes (which may occur if the cpu changes frequency) we need to notify the clocksource at a safe point where that state may change. Thus, if the TSC has changed frequency we set the new mult/shift values in the update_callback function.
+
+7. The is_continuous flag.
+ This flag variable (0 if false, 1 if true) denotes that the clocksource is continuous. This means that it is a purely hardware driven clocksource and is not dependent on any software code to run for it to increment properly. This denotation will be useful in the future when timer ticks may be disabled for long periods of time. Doing so using software clocksources, like the jiffies clocksource, would cause timekeeping problems.
+
+8. The vread function pointer.
+ This function pointer points to a user-space accessible function that reads the clocksource. This is used in userspace gettimeofday implementations to improve performance. See the x86-64 TSC clocksource implementation for an example.
+
+8. The vdata pointer.
+ This pointer is passed to the vread function pointer in a userspace gettimeofday implementation. Its usage is dependent on the vread implementation, but if the pointer points to data, that data must be readable from userspace.
+
+
+Now lets write a quick clocksource for an imaginary bit of hardware. Here are the specs:
+
+ A 32bit counter can be found at the MMIO address 0xFEEDF000. It runs at 100Mhz. To enable it, the the low bit of the address 0xFEEDF0F0 must be set to one.
+
+So lets start out an empty cool-counter.c file, and define the clocksource.
+
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+#define COOL_READ_PTR 0xFEEDF000
+#define COOL_START_PTR 0xFEEDF0F0
+
+static __iomem *cool_ptr = COOL_READ_PTR;
+
+struct clocksource clocksource_cool
+{
+ .name = "cool",
+ .rating = 200, /* its a pretty decent clock */
+ .mask = 0xFFFFFFFF, /* 32 bits */
+ .mult = 0, /*to be computed */
+ .shift = 10,
+}
+
+
+Now let's write the read function:
+
+cycle_t cool_counter_read(void)
+{
+ cycle_t ret = readl(cool_ptr);
+ return ret;
+}
+
+Finally, lets write the init function:
+
+void cool_counter_init(void)
+{
+ __iomem *ptr = COOL_START_PTR;
+ u32 val;
+
+ /* start the counter */
+ val = readl(ptr);
+ val |= 0x1;
+ writel(val, ptr);
+
+ /* finish initializing the clocksource */
+ clocksource_cool.read = cool_counter_read;
+ clocksource_cool.mult = clocksource_khz2mult(100000,
+ clocksource_cool.shift);
+
+ /* register the clocksource */
+ register_clocksource(&clocksource_cool);
+}
+module_init(cool_counter_init);
+
+
+Now wasn't that easy!
diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c
index 66e53dd..59d8c48 100644
--- a/drivers/char/hangcheck-timer.c
+++ b/drivers/char/hangcheck-timer.c
@@ -49,6 +49,7 @@
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <linux/sysrq.h>
+#include <linux/timeofday.h>


#define VERSION_STR "0.9.0"
@@ -130,8 +131,12 @@ __setup("hcheck_dump_tasks", hangcheck_p
#endif

#ifdef HAVE_MONOTONIC
+#ifndef CONFIG_GENERIC_TIME
extern unsigned long long monotonic_clock(void);
#else
+#define monotonic_clock() ktime_to_ns(get_monotonic_clock())
+#endif
+#else
static inline unsigned long long monotonic_clock(void)
{
# ifdef __s390__
diff --git a/include/asm-generic/timeofday.h b/include/asm-generic/timeofday.h
new file mode 100644
index 0000000..f8cab2c
--- /dev/null
+++ b/include/asm-generic/timeofday.h
@@ -0,0 +1,29 @@
+/* linux/include/asm-generic/timeofday.h
+ *
+ * This file contains the asm-generic interface
+ * to the arch specific calls used by the time of day subsystem
+ */
+#ifndef _ASM_GENERIC_TIMEOFDAY_H
+#define _ASM_GENERIC_TIMEOFDAY_H
+#include <linux/types.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/timeofday.h>
+#include <linux/clocksource.h>
+
+#include <asm/div64.h>
+#ifdef CONFIG_GENERIC_TIME
+/* Required externs */
+extern nsec_t read_persistent_clock(void);
+extern void sync_persistent_clock(struct timespec ts);
+
+#ifdef CONFIG_GENERIC_TIME_VSYSCALL
+extern void arch_update_vsyscall_gtod(struct timespec wall_time,
+ cycle_t offset_base, struct clocksource* clock,
+ int ntp_adj);
+#else
+#define arch_update_vsyscall_gtod(x,y,z,w) {}
+#endif /* CONFIG_GENERIC_TIME_VSYSCALL */
+
+#endif /* CONFIG_GENERIC_TIME */
+#endif
diff --git a/include/linux/time.h b/include/linux/time.h
index 1c16140..d0d0bf8 100644
--- a/include/linux/time.h
+++ b/include/linux/time.h
@@ -28,6 +28,10 @@ struct timezone {

#ifdef __KERNEL__

+/* timeofday base types */
+typedef s64 nsec_t;
+typedef u64 cycle_t;
+
/* Parameters used to convert the timespec values */
#define MSEC_PER_SEC (1000L)
#define USEC_PER_SEC (1000000L)
@@ -42,8 +46,6 @@ static __inline__ int timespec_equal(str
#define timespec_valid(ts) \
(((ts)->tv_sec >= 0) && (((unsigned) (ts)->tv_nsec) < NSEC_PER_SEC))

-typedef s64 nsec_t;
-
extern unsigned long
mktime (unsigned int year, unsigned int mon,
unsigned int day, unsigned int hour,
@@ -73,7 +75,6 @@ extern long do_utimes(char __user * file
struct itimerval;
extern int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue);
extern int do_getitimer(int which, struct itimerval *value);
-extern void getnstimeofday (struct timespec *tv);

extern struct timespec timespec_trunc(struct timespec t, unsigned gran);

@@ -109,6 +110,24 @@ static inline nsec_t timeval_to_ns(struc
extern void ns_to_timespec(struct timespec *ts, nsec_t nsec);
extern void ns_to_timeval(struct timeval *tv, nsec_t nsec);

+static inline void normalize_timespec(struct timespec *ts)
+{
+ while (unlikely((unsigned long)ts->tv_nsec >= NSEC_PER_SEC)) {
+ ts->tv_nsec -= NSEC_PER_SEC;
+ ts->tv_sec++;
+ }
+}
+
+static inline void timespec_add_ns(struct timespec *a, nsec_t ns)
+{
+ while(unlikely(ns >= NSEC_PER_SEC)) {
+ ns -= NSEC_PER_SEC;
+ a->tv_sec++;
+ }
+ a->tv_nsec += ns;
+ normalize_timespec(a);
+}
+
#endif /* __KERNEL__ */

#define NFDBITS __NFDBITS
diff --git a/include/linux/timeofday.h b/include/linux/timeofday.h
new file mode 100644
index 0000000..334aa41
--- /dev/null
+++ b/include/linux/timeofday.h
@@ -0,0 +1,44 @@
+/* linux/include/linux/timeofday.h
+ *
+ * This file contains the interface to the time of day subsystem
+ */
+#ifndef _LINUX_TIMEOFDAY_H
+#define _LINUX_TIMEOFDAY_H
+#include <linux/calc64.h>
+#include <linux/types.h>
+#include <linux/ktime.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+
+#ifdef CONFIG_GENERIC_TIME
+
+/* Kernel internal interfaces */
+extern ktime_t get_monotonic_clock(void);
+extern ktime_t get_realtime_clock(void);
+extern ktime_t get_realtime_offset(void);
+
+/* Timepsec based interfaces for user space functionality */
+extern void get_realtime_clock_ts(struct timespec *ts);
+extern void get_monotonic_clock_ts(struct timespec *ts);
+
+/* legacy timeofday interfaces*/
+#define getnstimeofday(ts) get_realtime_clock_ts(ts)
+extern void getnstimeofday(struct timespec *ts);
+extern void do_gettimeofday(struct timeval *tv);
+extern int do_settimeofday(struct timespec *ts);
+
+/* Internal functions */
+extern int timeofday_is_continuous(void);
+extern void timeofday_init(void);
+
+#ifndef CONFIG_IS_TICK_BASED
+#define arch_getoffset() (0)
+#else
+extern unsigned long arch_getoffset(void);
+#endif
+
+#else /* CONFIG_GENERIC_TIME */
+#define timeofday_init()
+extern void getnstimeofday(struct timespec *ts);
+#endif /* CONFIG_GENERIC_TIME */
+#endif /* _LINUX_TIMEOFDAY_H */
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 10d8c71..16e603b 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -313,6 +313,7 @@ int ntp_leapsecond(struct timespec now);
__x < 0 ? -(-__x >> __s) : __x >> __s; \
})

+#ifndef CONFIG_GENERIC_TIME

#ifdef CONFIG_TIME_INTERPOLATION

@@ -368,6 +369,7 @@ time_interpolator_reset(void)
}

#endif /* !CONFIG_TIME_INTERPOLATION */
+#endif /* !CONFIG_GENERIC_TIME */

#endif /* KERNEL */

diff --git a/include/sound/timer.h b/include/sound/timer.h
index b55f38a..6a31988 100644
--- a/include/sound/timer.h
+++ b/include/sound/timer.h
@@ -25,6 +25,7 @@

#include <sound/asound.h>
#include <linux/interrupt.h>
+#include <linux/timeofday.h>

typedef enum sndrv_timer_class snd_timer_class_t;
typedef enum sndrv_timer_slave_class snd_timer_slave_class_t;
diff --git a/init/main.c b/init/main.c
index f48a3e7..f4c01c8 100644
--- a/init/main.c
+++ b/init/main.c
@@ -47,6 +47,7 @@
#include <linux/rmap.h>
#include <linux/mempolicy.h>
#include <linux/key.h>
+#include <linux/timeofday.h>
#include <net/sock.h>

#include <asm/io.h>
@@ -489,6 +490,7 @@ asmlinkage void __init start_kernel(void
init_timers();
ktimers_init();
softirq_init();
+ timeofday_init();
time_init();

/*
diff --git a/kernel/ktimers.c b/kernel/ktimers.c
index 5ed2441..d594a76 100644
--- a/kernel/ktimers.c
+++ b/kernel/ktimers.c
@@ -30,6 +30,7 @@
#include <linux/percpu.h>
#include <linux/notifier.h>
#include <linux/syscalls.h>
+#include <linux/timeofday.h>
#include <linux/interrupt.h>

#include <asm/uaccess.h>
diff --git a/kernel/posix-timers.c b/kernel/posix-timers.c
index 1a1717a..8ed83e1 100644
--- a/kernel/posix-timers.c
+++ b/kernel/posix-timers.c
@@ -34,7 +34,7 @@
#include <linux/smp_lock.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
-#include <linux/time.h>
+#include <linux/timeofday.h>

#include <asm/uaccess.h>
#include <asm/semaphore.h>
diff --git a/kernel/time.c b/kernel/time.c
index c589b71..5e93a4e 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -38,6 +38,7 @@

#include <asm/uaccess.h>
#include <asm/unistd.h>
+#include <linux/timeofday.h>

/*
* The timezone where the local system is located. Used as a default by some
@@ -128,6 +129,7 @@ asmlinkage long sys_gettimeofday(struct
* as real UNIX machines always do it. This avoids all headaches about
* daylight saving times and warping kernel clocks.
*/
+#ifndef CONFIG_GENERIC_TIME
static inline void warp_clock(void)
{
write_seqlock_irq(&xtime_lock);
@@ -137,6 +139,18 @@ static inline void warp_clock(void)
write_sequnlock_irq(&xtime_lock);
clock_was_set();
}
+#else /* !CONFIG_GENERIC_TIME */
+/* XXX - this is somewhat cracked out and should
+ be checked [email protected]
+*/
+static inline void warp_clock(void)
+{
+ struct timespec ts;
+ getnstimeofday(&ts);
+ ts.tv_sec += sys_tz.tz_minuteswest * 60;
+ do_settimeofday(&ts);
+}
+#endif /* !CONFIG_GENERIC_TIME */

/*
* In case for some reason the CMOS clock has not already been running
@@ -484,6 +498,7 @@ struct timespec timespec_trunc(struct ti
}
EXPORT_SYMBOL(timespec_trunc);

+#ifndef CONFIG_GENERIC_TIME
#ifdef CONFIG_TIME_INTERPOLATION
void getnstimeofday (struct timespec *tv)
{
@@ -569,6 +584,8 @@ void getnstimeofday(struct timespec *tv)
EXPORT_SYMBOL_GPL(getnstimeofday);
#endif

+#endif /* !CONFIG_GENERIC_TIME */
+
/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.
* Assumes input in normal date format, i.e. 1980-12-31 23:59:59
* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.
diff --git a/kernel/time/Makefile b/kernel/time/Makefile
index e1dfd8e..4bdb5e6 100644
--- a/kernel/time/Makefile
+++ b/kernel/time/Makefile
@@ -1 +1 @@
-obj-y += clocksource.o jiffies.o
+obj-y += clocksource.o jiffies.o timeofday.o
diff --git a/kernel/time/timeofday.c b/kernel/time/timeofday.c
new file mode 100644
index 0000000..42ec5fc
--- /dev/null
+++ b/kernel/time/timeofday.c
@@ -0,0 +1,680 @@
+/*
+ * linux/kernel/time/timeofday.c
+ *
+ * This file contains the functions which access and manage
+ * the system's time of day functionality.
+ *
+ * Copyright (C) 2003, 2004, 2005 IBM, John Stultz ([email protected])
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * TODO WishList:
+ * o See XXX's below.
+ */
+
+#include <linux/timeofday.h>
+#include <linux/clocksource.h>
+#include <linux/ktime.h>
+#include <linux/timex.h>
+#include <linux/ktimer.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/sysdev.h>
+#include <linux/jiffies.h>
+#include <asm/timeofday.h>
+
+/* Periodic hook interval */
+#define PERIODIC_INTERVAL_MS 50
+
+/* [ktime_t based variables]
+ * system_time:
+ * Monotonically increasing counter of the number of nanoseconds
+ * since boot.
+ * wall_time_offset:
+ * Offset added to system_time to provide accurate time-of-day
+ */
+static ktime_t system_time;
+static ktime_t wall_time_offset;
+
+/* [timespec based variables]
+ * These variables mirror teh ktime_t based variables to avoid
+ * performance issues in the userspace syscall paths.
+ *
+ * wall_time_ts:
+ * timespec holding the current wall time.
+ * mono_time_ts:
+ * timespec holding the current monotonic time.
+ * monotonic_time_offset_ts:
+ * timespec holding the difference between wall and monotonic time.
+ */
+static struct timespec wall_time_ts;
+static struct timespec mono_time_ts;
+static struct timespec monotonic_time_offset_ts;
+
+/* [cycle based variables]
+ * cycle_last:
+ * Value of the clocksource at the last timeofday_periodic_hook()
+ * (adjusted only minorly to account for rounded off cycles)
+ */
+static cycle_t cycle_last;
+
+/* [clocksource_interval variables]
+ * ts_interval:
+ * This clocksource_interval is used in the fixed interval
+ * cycles to nanosecond calculation.
+ * INTERVAL_LEN:
+ * This constant is the requested fixed interval period
+ * in nanoseconds.
+ */
+struct clocksource_interval ts_interval;
+#define INTERVAL_LEN ((PERIODIC_INTERVAL_MS-1)*1000000)
+
+/* [clocksource data]
+ * clock:
+ * current clocksource pointer
+ */
+static struct clocksource *clock;
+
+/* [NTP adjustment]
+ * ntp_adj:
+ * value of the current ntp adjustment, stored in
+ * clocksource multiplier units.
+ */
+int ntp_adj;
+
+/* [locks]
+ * system_time_lock:
+ * generic lock for all locally scoped time values
+ */
+static seqlock_t system_time_lock = SEQLOCK_UNLOCKED;
+
+
+/* [suspend/resume info]
+ * time_suspend_state:
+ * variable that keeps track of suspend state
+ * suspend_start:
+ * start of the suspend call
+ */
+static enum {
+ TIME_RUNNING,
+ TIME_SUSPENDED
+} time_suspend_state = TIME_RUNNING;
+
+static nsec_t suspend_start;
+
+/* [Soft-Timers]
+ * timeofday_timer:
+ * soft-timer used to call timeofday_periodic_hook()
+ */
+struct ktimer timeofday_timer;
+
+/**
+ * update_legacy_time_values - sync legacy time values
+ *
+ * This function is necessary for a smooth transition to the
+ * new timekeeping code. When all the xtime/wall_to_monotonic
+ * users are converted this function can be removed.
+ *
+ * system_time_lock must be held by the caller
+ */
+static void update_legacy_time_values(void)
+{
+ unsigned long flags;
+
+ write_seqlock_irqsave(&xtime_lock, flags);
+
+ xtime = wall_time_ts;
+ set_normalized_timespec(&wall_to_monotonic,
+ -monotonic_time_offset_ts.tv_sec,
+ -monotonic_time_offset_ts.tv_nsec);
+
+ write_sequnlock_irqrestore(&xtime_lock, flags);
+
+ /* since time state has changed, notify vsyscall code */
+ arch_update_vsyscall_gtod(wall_time_ts, cycle_last, clock, ntp_adj);
+}
+
+/**
+ * __get_nsec_offset - Returns nanoseconds since last call to periodic_hook
+ *
+ * private function, must hold system_time_lock lock when being
+ * called. Returns the number of nanoseconds since the
+ * last call to timeofday_periodic_hook() (adjusted by NTP scaling)
+ */
+static inline nsec_t __get_nsec_offset(void)
+{
+ cycle_t cycle_now, cycle_delta;
+ nsec_t ns_offset;
+
+ /* read clocksource */
+ cycle_now = read_clocksource(clock);
+
+ /* calculate the delta since the last timeofday_periodic_hook */
+ cycle_delta = (cycle_now - cycle_last) & clock->mask;
+
+ /* convert to nanoseconds */
+ ns_offset = cyc2ns(clock, ntp_adj, cycle_delta);
+
+ /* Special case for jiffies tick/offset based systems
+ * add arch specific offset
+ */
+ ns_offset += arch_getoffset();
+
+ return ns_offset;
+}
+
+/**
+ * __get_monotonic_clock - Returns monotonically increasing nanoseconds
+ *
+ * private function, must hold system_time_lock lock when being
+ * called. Returns the monotonically increasing number of
+ * nanoseconds since the system booted (adjusted by NTP scaling)
+ */
+static inline ktime_t __get_monotonic_clock(void)
+{
+ nsec_t offset = __get_nsec_offset();
+ return ktime_add_ns(system_time, offset);
+}
+
+/**
+ * get_monotonic_clock - Returns monotonic time in ktime_t format
+ *
+ * Returns the monotonically increasing number of nanoseconds
+ * since the system booted via __monotonic_clock()
+ */
+ktime_t get_monotonic_clock(void)
+{
+ unsigned long seq;
+ ktime_t ret;
+
+ /* atomically read __get_monotonic_clock() */
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ ret = __get_monotonic_clock();
+
+ } while (read_seqretry(&system_time_lock, seq));
+
+ return ret;
+}
+
+EXPORT_SYMBOL_GPL(get_monotonic_clock);
+
+/**
+ * get_realtime_clock - Returns the timeofday in ktime_t format
+ *
+ * Returns the wall time in ktime_t format. The resolution
+ * is nanoseconds
+ */
+ktime_t get_realtime_clock(void)
+{
+ unsigned long seq;
+ ktime_t ret;
+
+ /* atomically read __get_monotonic_clock() */
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ ret = __get_monotonic_clock();
+ ret = ktime_add(ret, wall_time_offset);
+
+ } while (read_seqretry(&system_time_lock, seq));
+
+ return ret;
+}
+
+/**
+ * get_realtime_offset - Returns the offset of realtime clock
+ *
+ * Returns the number of nanoseconds in ktime_t storage format which
+ * represents the offset of the realtime clock to the the monotonic clock
+ */
+ktime_t get_realtime_offset(void)
+{
+ unsigned long seq;
+ ktime_t ret;
+
+ /* atomically read wall_time_offset */
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ ret = wall_time_offset;
+
+ } while (read_seqretry(&system_time_lock, seq));
+
+ return ret;
+}
+
+/**
+ * get_monotonic_clock_ts - Returns monotonic time in timespec format
+ *
+ * @ts: pointer to the timespec to be set
+ *
+ * Returns a timespec of nanoseconds since the system booted and
+ * store the result in the timespec variable pointed to by @ts
+ */
+void get_monotonic_clock_ts(struct timespec *ts)
+{
+ unsigned long seq;
+ nsec_t offset;
+
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ *ts = mono_time_ts;
+ offset = __get_nsec_offset();
+ } while (read_seqretry(&system_time_lock, seq));
+
+ timespec_add_ns(ts, offset);
+}
+
+/**
+ * __get_realtime_clock_ts - Returns the time of day in a timespec
+ *
+ * @ts: pointer to the timespec to be set
+ *
+ * Returns the time of day in a timespec. Used by
+ * do_gettimeofday() and get_realtime_clock_ts().
+ *
+ */
+static inline void __get_realtime_clock_ts(struct timespec *ts)
+{
+ unsigned long seq;
+ nsec_t nsecs;
+
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ *ts = wall_time_ts;
+ nsecs = __get_nsec_offset();
+
+ } while (read_seqretry(&system_time_lock, seq));
+
+ timespec_add_ns(ts, nsecs);
+}
+
+/**
+ * get_realtime_clock_ts - Returns the time of day in a timespec
+ * @ts: pointer to the timespec to be set
+ *
+ * Returns the time of day in a timespec.
+ */
+void get_realtime_clock_ts(struct timespec *ts)
+{
+ __get_realtime_clock_ts(ts);
+}
+
+EXPORT_SYMBOL(get_realtime_clock_ts);
+
+
+/**
+ * do_gettimeofday - Returns the time of day in a timeval
+ * @tv: pointer to the timeval to be set
+ *
+ * NOTE: Users should be converted to using get_realtime_clock_ts()
+ */
+void do_gettimeofday(struct timeval *tv)
+{
+ struct timespec now_ts;
+ __get_realtime_clock_ts(&now_ts);
+ tv->tv_sec = now_ts.tv_sec;
+ tv->tv_usec = now_ts.tv_nsec/1000;
+}
+
+EXPORT_SYMBOL(do_gettimeofday);
+
+/**
+ * do_settimeofday - Sets the time of day
+ * @tv: pointer to the timespec variable containing the new time
+ *
+ * Sets the time of day to the new time and update NTP and notify ktimers
+ */
+int do_settimeofday(struct timespec *tv)
+{
+ unsigned long flags;
+ ktime_t newtime;
+
+ newtime = timespec_to_ktime(*tv);
+
+ write_seqlock_irqsave(&system_time_lock, flags);
+
+ /* calculate the new offset from the monotonic clock */
+ wall_time_offset = ktime_sub(newtime, __get_monotonic_clock());
+
+ /* update the internal timespec variables */
+ ktime_to_timespec(&wall_time_ts,
+ ktime_add(system_time, wall_time_offset));
+ ktime_to_timespec(&monotonic_time_offset_ts, wall_time_offset);
+
+ ntp_clear();
+ update_legacy_time_values();
+
+ write_sequnlock_irqrestore(&system_time_lock, flags);
+
+ /* signal ktimers about time change */
+ clock_was_set();
+
+ return 0;
+}
+
+EXPORT_SYMBOL(do_settimeofday);
+
+/**
+ * __increment_system_time - Increments system time
+ * @delta: nanosecond delta to add to the time variables
+ *
+ * Private helper that increments system_time and related
+ * timekeeping variables.
+ */
+static inline void __increment_system_time(nsec_t delta)
+{
+ system_time = ktime_add_ns(system_time, delta);
+ timespec_add_ns(&wall_time_ts, delta);
+ timespec_add_ns(&mono_time_ts, delta);
+}
+
+/**
+ * timeofday_suspend_hook - allows the timeofday subsystem to be shutdown
+ * @dev: unused
+ * @state: unused
+ *
+ * This function allows the timeofday subsystem to be shutdown for a period
+ * of time. Called when going into suspend/hibernate mode.
+ */
+static int timeofday_suspend_hook(struct sys_device *dev, pm_message_t state)
+{
+ unsigned long flags;
+
+ write_seqlock_irqsave(&system_time_lock, flags);
+
+ BUG_ON(time_suspend_state != TIME_RUNNING);
+
+ /* First off, save suspend start time
+ * then quickly accumulate the current nsec offset.
+ * These two calls hopefully occur quickly
+ * because the difference between reads will
+ * accumulate as time drift on resume.
+ */
+ suspend_start = read_persistent_clock();
+ __increment_system_time(__get_nsec_offset());
+
+ time_suspend_state = TIME_SUSPENDED;
+
+ write_sequnlock_irqrestore(&system_time_lock, flags);
+
+ return 0;
+}
+
+/**
+ * timeofday_resume_hook - Resumes the timeofday subsystem.
+ * @dev: unused
+ *
+ * This function resumes the timeofday subsystem from a previous call
+ * to timeofday_suspend_hook.
+ */
+static int timeofday_resume_hook(struct sys_device *dev)
+{
+ nsec_t suspend_end, suspend_time;
+ unsigned long flags;
+
+ write_seqlock_irqsave(&system_time_lock, flags);
+
+ BUG_ON(time_suspend_state != TIME_SUSPENDED);
+
+ /* Read persistent clock to mark the end of
+ * the suspend interval then rebase the
+ * cycle_last to current clocksource value.
+ * Again, time between these two calls will
+ * not be accounted for and will show up as
+ * time drift.
+ */
+ suspend_end = read_persistent_clock();
+ cycle_last = read_clocksource(clock);
+
+ /* calculate suspend time and add it to system time */
+ suspend_time = suspend_end - suspend_start;
+ __increment_system_time(suspend_time);
+
+ ntp_clear();
+
+ time_suspend_state = TIME_RUNNING;
+
+ update_legacy_time_values();
+
+ write_sequnlock_irqrestore(&system_time_lock, flags);
+
+ /* inform ktimers about time change */
+ clock_was_set();
+
+ return 0;
+}
+
+/* sysfs resume/suspend bits */
+static struct sysdev_class timeofday_sysclass = {
+ .resume = timeofday_resume_hook,
+ .suspend = timeofday_suspend_hook,
+ set_kset_name("timeofday"),
+};
+
+static struct sys_device device_timer = {
+ .id = 0,
+ .cls = &timeofday_sysclass,
+};
+
+static int timeofday_init_device(void)
+{
+ int error = sysdev_class_register(&timeofday_sysclass);
+
+ if (!error)
+ error = sysdev_register(&device_timer);
+
+ return error;
+}
+
+device_initcall(timeofday_init_device);
+
+
+/**
+ * timeofday_periodic_hook - Does periodic update of timekeeping values.
+ * @unused: unused value
+ *
+ * Calculates the delta since the last call, updates system time and
+ * clears the offset.
+ *
+ * Called via timeofday_timer.
+ */
+static void timeofday_periodic_hook(void* unused)
+{
+ unsigned long flags;
+
+ cycle_t cycle_now, cycle_delta;
+ nsec_t delta_nsec;
+ static u64 remainder;
+
+ long leapsecond;
+ struct clocksource* next;
+
+ int ppm;
+ static int ppm_last;
+
+ int something_changed = 0;
+ struct clocksource old_clock;
+ static nsec_t second_check;
+ ktime_t expire_time;
+
+ write_seqlock_irqsave(&system_time_lock, flags);
+
+ /* read time source & calc time since last call*/
+ cycle_now = read_clocksource(clock);
+ cycle_delta = (cycle_now - cycle_last) & clock->mask;
+
+ delta_nsec = cyc2ns_fixed_rem(ts_interval, &cycle_delta, &remainder);
+ cycle_last = (cycle_now - cycle_delta)&clock->mask;
+
+ /* update system_time */
+ __increment_system_time(delta_nsec);
+
+ /* advance the ntp state machine by ns interval*/
+ ntp_advance(delta_nsec);
+
+ /* Only call ntp_leapsecond and ntp_sync once a sec */
+ second_check += delta_nsec;
+ if (second_check >= NSEC_PER_SEC) {
+ /* do ntp leap second processing*/
+ leapsecond = ntp_leapsecond(wall_time_ts);
+ if (leapsecond) {
+ wall_time_offset = ktime_add_ns(wall_time_offset,
+ leapsecond * NSEC_PER_SEC);
+ wall_time_ts.tv_sec += leapsecond;
+ monotonic_time_offset_ts.tv_sec += leapsecond;
+ }
+ /* sync the persistent clock */
+ if (ntp_synced())
+ sync_persistent_clock(wall_time_ts);
+ second_check -= NSEC_PER_SEC;
+ }
+
+ /* if necessary, switch clocksources */
+ next = get_next_clocksource();
+ if (next != clock) {
+ /* immediately set new cycle_last */
+ cycle_last = read_clocksource(next);
+ /* update cycle_now to avoid problems in accumulation later */
+ cycle_now = cycle_last;
+ /* swap clocksources */
+ old_clock = *clock;
+ clock = next;
+ printk(KERN_INFO "Time: %s clocksource has been installed.\n",
+ clock->name);
+ ntp_clear();
+ ntp_adj = 0;
+ remainder = 0;
+ something_changed = 1;
+ }
+
+ /* now is a safe time, so allow clocksource to adjust
+ * itself (for example: to make cpufreq changes).
+ */
+ if (clock->update_callback) {
+ /* since clocksource state might change,
+ * keep a copy, but only if we've not
+ * already changed timesources
+ */
+ if (!something_changed)
+ old_clock = *clock;
+ if (clock->update_callback()) {
+ remainder = 0;
+ something_changed = 1;
+ }
+ }
+
+ /* check for new PPM adjustment */
+ ppm = ntp_get_ppm_adjustment();
+ if (ppm_last != ppm) {
+ /* make sure old_clock is set */
+ if (!something_changed)
+ old_clock = *clock;
+ something_changed = 1;
+ }
+
+ /* if something changed, recalculate the ntp adjustment value */
+ if (something_changed) {
+ /* accumulate current leftover cycles using old_clock */
+ if (cycle_delta) {
+ delta_nsec = cyc2ns_rem(&old_clock, ntp_adj,
+ cycle_delta, &remainder);
+ cycle_last = cycle_now;
+ __increment_system_time(delta_nsec);
+ ntp_advance(delta_nsec);
+ }
+
+ /* recalculate the ntp adjustment and fixed interval values */
+ ppm_last = ppm;
+ ntp_adj = ppm_to_mult_adj(clock, ppm);
+ ts_interval = calculate_clocksource_interval(clock, ntp_adj,
+ INTERVAL_LEN);
+ }
+
+ update_legacy_time_values();
+
+ write_sequnlock_irqrestore(&system_time_lock, flags);
+
+ /* Set us up to go off on the next interval */
+ expire_time = ktime_set(0, PERIODIC_INTERVAL_MS*1000000);
+ ktimer_start(&timeofday_timer, &expire_time, KTIMER_REL);
+}
+
+/**
+ * timeofday_is_continuous - check to see if timekeeping is free running
+ *
+ */
+int timeofday_is_continuous(void)
+{
+ unsigned long seq;
+ int ret;
+ do {
+ seq = read_seqbegin(&system_time_lock);
+
+ ret = clock->is_continuous;
+
+ } while (read_seqretry(&system_time_lock, seq));
+
+ return ret;
+}
+
+/**
+ * timeofday_init - Initializes time variables
+ */
+void __init timeofday_init(void)
+{
+ unsigned long flags;
+ ktime_t expire_time;
+
+ write_seqlock_irqsave(&system_time_lock, flags);
+
+ /* initialize the clock variable */
+ clock = get_next_clocksource();
+
+ /* initialize cycle_last offset base */
+ cycle_last = read_clocksource(clock);
+
+ /* initialize wall_time_offset to now*/
+ /* XXX - this should be something like ns_to_ktime() */
+ wall_time_offset = ktime_add_ns(wall_time_offset,
+ read_persistent_clock());
+
+ /* initialize timespec values */
+ ktime_to_timespec(&wall_time_ts,
+ ktime_add(system_time, wall_time_offset));
+ ktime_to_timespec(&monotonic_time_offset_ts, wall_time_offset);
+
+
+ /* clear NTP scaling factor & state machine */
+ ntp_adj = 0;
+ ntp_clear();
+ ts_interval = calculate_clocksource_interval(clock, ntp_adj,
+ INTERVAL_LEN);
+
+ /* initialize legacy time values */
+ update_legacy_time_values();
+
+ write_sequnlock_irqrestore(&system_time_lock, flags);
+
+ /* Install timeofday_periodic_hook timer */
+ ktimer_init(&timeofday_timer);
+ expire_time = ktime_set(0, PERIODIC_INTERVAL_MS*1000000);
+ timeofday_timer.function = timeofday_periodic_hook;
+ ktimer_start(&timeofday_timer, &expire_time, KTIMER_REL);
+}
diff --git a/kernel/timer.c b/kernel/timer.c
index f0c986b..a94687e 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -28,7 +28,7 @@
#include <linux/swap.h>
#include <linux/notifier.h>
#include <linux/thread_info.h>
-#include <linux/time.h>
+#include <linux/timeofday.h>
#include <linux/jiffies.h>
#include <linux/posix-timers.h>
#include <linux/cpu.h>
@@ -888,6 +888,7 @@ void ntp_advance(unsigned long interval_

}

+#ifndef CONFIG_GENERIC_TIME
/*
* Using a loop looks inefficient, but "ticks" is
* usually just one (we shouldn't be losing ticks,
@@ -940,6 +941,9 @@ static void update_wall_time(unsigned lo

} while (ticks);
}
+#else /* !CONFIG_GENERIC_TIME */
+#define update_wall_time(x)
+#endif /* !CONFIG_GENERIC_TIME */

/*
* Called from the timer interrupt handler to charge one tick to the current

2005-11-12 04:50:12

by john stultz

[permalink] [raw]
Subject: [PATCH 5/13] Time: i386 Conversion - part 1: Move timer_pit.c to i8253.c

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the first of six, is just a simple
cleanup for the i386 arch in preparation of moving to the generic
timeofday infrastructure. It simply moves some code from timer_pit.c to
i8253.c.

It applies on top of my timeofday-core patch. This patch is part the
timeofday-arch-i386 patchset, so without the following parts it is not
expected to compile (although just this one should).

thanks
-john

Signed-off-by: John Stultz <[email protected]>

Makefile | 2 -
i8253.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++
time.c | 6 -----
timers/timer_pit.c | 13 ------------
4 files changed, 58 insertions(+), 20 deletions(-)

linux-2.6.14_timeofday-arch-i386-part1_B10.patch
============================================
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index f10de0f..7bc053f 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -7,7 +7,7 @@ extra-y := head.o init_task.o vmlinux.ld
obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \
pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \
- doublefault.o quirks.o i8237.o
+ doublefault.o quirks.o i8237.o i8253.o

obj-y += cpu/
obj-y += timers/
diff --git a/arch/i386/kernel/i8253.c b/arch/i386/kernel/i8253.c
new file mode 100644
index 0000000..e11928d
--- /dev/null
+++ b/arch/i386/kernel/i8253.c
@@ -0,0 +1,57 @@
+/*
+ * i8253.c 8253/PIT functions
+ *
+ */
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/spinlock.h>
+#include <linux/sysdev.h>
+#include <linux/module.h>
+
+#include <asm/delay.h>
+#include <asm/i8253.h>
+#include <asm/io.h>
+
+#include "io_ports.h"
+
+DEFINE_SPINLOCK(i8253_lock);
+EXPORT_SYMBOL(i8253_lock);
+
+void setup_pit_timer(void)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&i8253_lock, flags);
+ outb_p(0x34,PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
+ udelay(10);
+ outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
+ udelay(10);
+ 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 --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index 41c5b2d..5a9f253 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -82,11 +82,6 @@ extern unsigned long wall_jiffies;
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);

-#include <asm/i8253.h>
-
-DEFINE_SPINLOCK(i8253_lock);
-EXPORT_SYMBOL(i8253_lock);
-
struct timer_opts *cur_timer __read_mostly = &timer_none;

/*
@@ -400,7 +395,6 @@ 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);
diff --git a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c
index e42e46d..4676576 100644
--- a/arch/i386/kernel/timers/timer_pit.c
+++ b/arch/i386/kernel/timers/timer_pit.c
@@ -161,16 +161,3 @@ struct init_timer_opts __initdata timer_
.init = init_pit,
.opts = &timer_pit,
};
-
-void setup_pit_timer(void)
-{
- unsigned long flags;
-
- spin_lock_irqsave(&i8253_lock, flags);
- outb_p(0x34,PIT_MODE); /* binary, mode 2, LSB/MSB, ch 0 */
- udelay(10);
- outb_p(LATCH & 0xff , PIT_CH0); /* LSB */
- udelay(10);
- outb(LATCH >> 8 , PIT_CH0); /* MSB */
- spin_unlock_irqrestore(&i8253_lock, flags);
-}

2005-11-12 04:50:15

by john stultz

[permalink] [raw]
Subject: [PATCH 6/13] Time: i386 Conversion - part 2: Move timer_tsc.c to tsc.c

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the second of six, is a cleanup patch
for the i386 arch in preparation of moving the the generic timeofday
infrastructure. It moves some code from timer_tsc.c to a new tsc.c
file.

It applies on top of my timeofday-arch-i386-part1 patch. This patch is
part the timeofday-arch-i386 patchset, so without the following parts it
is not expected to compile.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/i386/kernel/Makefile | 2
arch/i386/kernel/timers/common.c | 84 ---------
arch/i386/kernel/timers/timer_tsc.c | 212 -------------------------
arch/i386/kernel/tsc.c | 303 ++++++++++++++++++++++++++++++++++++
include/asm-i386/timex.h | 34 ----
include/asm-i386/tsc.h | 44 +++++
6 files changed, 349 insertions(+), 330 deletions(-)

linux-2.6.14_timeofday-arch-i386-part2_B10.patch
============================================
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 7bc053f..4c4e1e5 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -7,7 +7,7 @@ extra-y := head.o init_task.o vmlinux.ld
obj-y := process.o semaphore.o signal.o entry.o traps.o irq.o vm86.o \
ptrace.o time.o ioport.o ldt.o setup.o i8259.o sys_i386.o \
pci-dma.o i386_ksyms.o i387.o dmi_scan.o bootflag.o \
- doublefault.o quirks.o i8237.o i8253.o
+ doublefault.o quirks.o i8237.o i8253.o tsc.o

obj-y += cpu/
obj-y += timers/
diff --git a/arch/i386/kernel/timers/common.c b/arch/i386/kernel/timers/common.c
index 8163fe0..535f4d8 100644
--- a/arch/i386/kernel/timers/common.c
+++ b/arch/i386/kernel/timers/common.c
@@ -14,66 +14,6 @@

#include "mach_timer.h"

-/* ------ Calibrate the TSC -------
- * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
- * Too much 64-bit arithmetic here to do this cleanly in C, and for
- * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
- * output busy loop as low as possible. We avoid reading the CTC registers
- * directly because of the awkward 8-bit access mechanism of the 82C54
- * device.
- */
-
-#define CALIBRATE_TIME (5 * 1000020/HZ)
-
-unsigned long calibrate_tsc(void)
-{
- mach_prepare_counter();
-
- {
- unsigned long startlow, starthigh;
- unsigned long endlow, endhigh;
- unsigned long count;
-
- rdtsc(startlow,starthigh);
- mach_countup(&count);
- rdtsc(endlow,endhigh);
-
-
- /* Error: ECTCNEVERSET */
- if (count <= 1)
- goto bad_ctc;
-
- /* 64-bit subtract - gcc just messes up with long longs */
- __asm__("subl %2,%0\n\t"
- "sbbl %3,%1"
- :"=a" (endlow), "=d" (endhigh)
- :"g" (startlow), "g" (starthigh),
- "0" (endlow), "1" (endhigh));
-
- /* Error: ECPUTOOFAST */
- if (endhigh)
- goto bad_ctc;
-
- /* Error: ECPUTOOSLOW */
- if (endlow <= CALIBRATE_TIME)
- goto bad_ctc;
-
- __asm__("divl %2"
- :"=a" (endlow), "=d" (endhigh)
- :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
-
- return endlow;
- }
-
- /*
- * The CTC wasn't reliable: we got a hit on the very first read,
- * or the CPU was so fast/slow that the quotient wouldn't fit in
- * 32 bits..
- */
-bad_ctc:
- return 0;
-}
-
#ifdef CONFIG_HPET_TIMER
/* ------ Calibrate the TSC using HPET -------
* Return 2^32 * (1 / (TSC clocks per usec)) for getting the CPU freq.
@@ -146,27 +86,3 @@ unsigned long read_timer_tsc(void)
rdtscl(retval);
return retval;
}
-
-
-/* calculate cpu_khz */
-void init_cpu_khz(void)
-{
- if (cpu_has_tsc) {
- unsigned long tsc_quotient = calibrate_tsc();
- if (tsc_quotient) {
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000;
- __asm__("divl %2"
- :"=a" (cpu_khz), "=d" (edx)
- :"r" (tsc_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %u.%03u MHz processor.\n",
- cpu_khz / 1000, cpu_khz % 1000);
- }
- }
- }
-}
-
diff --git a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c
index d395e3b..93ec4c9 100644
--- a/arch/i386/kernel/timers/timer_tsc.c
+++ b/arch/i386/kernel/timers/timer_tsc.c
@@ -32,10 +32,6 @@ static unsigned long hpet_last;
static struct timer_opts timer_tsc;
#endif

-static inline void cpufreq_delayed_get(void);
-
-int tsc_disable __devinitdata = 0;
-
static int use_tsc;
/* Number of usecs that the last interrupt was delayed */
static int delay_at_last_interrupt;
@@ -45,39 +41,6 @@ static unsigned long last_tsc_high; /* m
static unsigned long long monotonic_base;
static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;

-/* convert from cycles(64bits) => nanoseconds (64bits)
- * basic equation:
- * ns = cycles / (freq / ns_per_sec)
- * ns = cycles * (ns_per_sec / freq)
- * ns = cycles * (10^9 / (cpu_khz * 10^3))
- * ns = cycles * (10^6 / cpu_khz)
- *
- * Then we use scaling math (suggested by [email protected]) to get:
- * ns = cycles * (10^6 * SC / cpu_khz) / SC
- * ns = cycles * cyc2ns_scale / SC
- *
- * And since SC is a constant power of two, we can convert the div
- * into a shift.
- *
- * We can use khz divisor instead of mhz to keep a better percision, since
- * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
- * ([email protected])
- *
- * [email protected] "math is hard, lets go shopping!"
- */
-static unsigned long cyc2ns_scale;
-#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
-
-static inline void set_cyc2ns_scale(unsigned long cpu_khz)
-{
- cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
-}
-
-static inline unsigned long long cycles_2_ns(unsigned long long cyc)
-{
- return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
-}
-
static int count2; /* counter for mark_offset_tsc() */

/* Cached *multiplier* to convert TSC counts to microseconds.
@@ -135,29 +98,6 @@ static unsigned long long monotonic_cloc
return base + cycles_2_ns(this_offset - last_offset);
}

-/*
- * Scheduler clock - returns current time in nanosec units.
- */
-unsigned long long sched_clock(void)
-{
- unsigned long long this_offset;
-
- /*
- * In the NUMA case we dont use the TSC as they are not
- * synchronized across all CPUs.
- */
-#ifndef CONFIG_NUMA
- if (!use_tsc)
-#endif
- /* no locking but a rare wrong value is not a big deal */
- return jiffies_64 * (1000000000 / HZ);
-
- /* Read the Time Stamp Counter */
- rdtscll(this_offset);
-
- /* return the value in ns */
- return cycles_2_ns(this_offset);
-}

static void delay_tsc(unsigned long loops)
{
@@ -222,127 +162,6 @@ static void mark_offset_tsc_hpet(void)
#endif


-#ifdef CONFIG_CPU_FREQ
-#include <linux/workqueue.h>
-
-static unsigned int cpufreq_delayed_issched = 0;
-static unsigned int cpufreq_init = 0;
-static struct work_struct cpufreq_delayed_get_work;
-
-static void handle_cpufreq_delayed_get(void *v)
-{
- unsigned int cpu;
- for_each_online_cpu(cpu) {
- cpufreq_get(cpu);
- }
- cpufreq_delayed_issched = 0;
-}
-
-/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
- * to verify the CPU frequency the timing core thinks the CPU is running
- * at is still correct.
- */
-static inline void cpufreq_delayed_get(void)
-{
- if (cpufreq_init && !cpufreq_delayed_issched) {
- cpufreq_delayed_issched = 1;
- printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");
- schedule_work(&cpufreq_delayed_get_work);
- }
-}
-
-/* If the CPU frequency is scaled, TSC-based delays will need a different
- * loops_per_jiffy value to function properly.
- */
-
-static unsigned int ref_freq = 0;
-static unsigned long loops_per_jiffy_ref = 0;
-
-#ifndef CONFIG_SMP
-static unsigned long fast_gettimeoffset_ref = 0;
-static unsigned int cpu_khz_ref = 0;
-#endif
-
-static int
-time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
-{
- struct cpufreq_freqs *freq = data;
-
- if (val != CPUFREQ_RESUMECHANGE)
- write_seqlock_irq(&xtime_lock);
- if (!ref_freq) {
- ref_freq = freq->old;
- loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
-#ifndef CONFIG_SMP
- fast_gettimeoffset_ref = fast_gettimeoffset_quotient;
- cpu_khz_ref = cpu_khz;
-#endif
- }
-
- if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
- (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
- (val == CPUFREQ_RESUMECHANGE)) {
- if (!(freq->flags & CPUFREQ_CONST_LOOPS))
- cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
-#ifndef CONFIG_SMP
- if (cpu_khz)
- cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
- if (use_tsc) {
- if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
- fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
- set_cyc2ns_scale(cpu_khz);
- }
- }
-#endif
- }
-
- if (val != CPUFREQ_RESUMECHANGE)
- write_sequnlock_irq(&xtime_lock);
-
- return 0;
-}
-
-static struct notifier_block time_cpufreq_notifier_block = {
- .notifier_call = time_cpufreq_notifier
-};
-
-
-static int __init cpufreq_tsc(void)
-{
- int ret;
- INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL);
- ret = cpufreq_register_notifier(&time_cpufreq_notifier_block,
- CPUFREQ_TRANSITION_NOTIFIER);
- if (!ret)
- cpufreq_init = 1;
- return ret;
-}
-core_initcall(cpufreq_tsc);
-
-#else /* CONFIG_CPU_FREQ */
-static inline void cpufreq_delayed_get(void) { return; }
-#endif
-
-int recalibrate_cpu_khz(void)
-{
-#ifndef CONFIG_SMP
- unsigned int cpu_khz_old = cpu_khz;
-
- if (cpu_has_tsc) {
- init_cpu_khz();
- cpu_data[0].loops_per_jiffy =
- cpufreq_scale(cpu_data[0].loops_per_jiffy,
- cpu_khz_old,
- cpu_khz);
- return 0;
- } else
- return -ENODEV;
-#else
- return -ENODEV;
-#endif
-}
-EXPORT_SYMBOL(recalibrate_cpu_khz);

static void mark_offset_tsc(void)
{
@@ -548,37 +367,6 @@ 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 */
-static int __init tsc_setup(char *str)
-{
- tsc_disable = 1;
- return 1;
-}
-#else
-static int __init tsc_setup(char *str)
-{
- printk(KERN_WARNING "notsc: Kernel compiled with CONFIG_X86_TSC, "
- "cannot disable TSC.\n");
- return 1;
-}
-#endif
-__setup("notsc", tsc_setup);
-


/************************************************************/
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c
new file mode 100644
index 0000000..c562292
--- /dev/null
+++ b/arch/i386/kernel/tsc.c
@@ -0,0 +1,303 @@
+/*
+ * This code largely moved from arch/i386/kernel/timer/timer_tsc.c
+ * which was originally moved from arch/i386/kernel/time.c.
+ * See comments there for proper credits.
+ */
+
+#include <linux/init.h>
+#include <linux/timex.h>
+#include <linux/cpufreq.h>
+#include <asm/io.h>
+#include "mach_timer.h"
+
+int tsc_disable __initdata = 0;
+#ifndef CONFIG_X86_TSC
+/* disable flag for tsc. Takes effect by clearing the TSC cpu flag
+ * in cpu/common.c */
+static int __init tsc_setup(char *str)
+{
+ tsc_disable = 1;
+ return 1;
+}
+#else
+static int __init tsc_setup(char *str)
+{
+ printk(KERN_WARNING "notsc: Kernel compiled with CONFIG_X86_TSC, "
+ "cannot disable TSC.\n");
+ return 1;
+}
+#endif
+__setup("notsc", tsc_setup);
+
+
+int read_current_timer(unsigned long *timer_val)
+{
+ if (cur_timer->read_timer) {
+ *timer_val = cur_timer->read_timer();
+ return 0;
+ }
+ return -1;
+}
+
+
+/* convert from cycles(64bits) => nanoseconds (64bits)
+ * basic equation:
+ * ns = cycles / (freq / ns_per_sec)
+ * ns = cycles * (ns_per_sec / freq)
+ * ns = cycles * (10^9 / (cpu_khz * 10^3))
+ * ns = cycles * (10^6 / cpu_khz)
+ *
+ * Then we use scaling math (suggested by [email protected]) to get:
+ * ns = cycles * (10^6 * SC / cpu_khz) / SC
+ * ns = cycles * cyc2ns_scale / SC
+ *
+ * And since SC is a constant power of two, we can convert the div
+ * into a shift.
+ *
+ * We can use khz divisor instead of mhz to keep a better percision, since
+ * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
+ * ([email protected])
+ *
+ * [email protected] "math is hard, lets go shopping!"
+ */
+static unsigned long cyc2ns_scale;
+#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
+
+static inline void set_cyc2ns_scale(unsigned long cpu_khz)
+{
+ cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
+}
+
+static inline unsigned long long cycles_2_ns(unsigned long long cyc)
+{
+ return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
+}
+
+/*
+ * Scheduler clock - returns current time in nanosec units.
+ */
+unsigned long long sched_clock(void)
+{
+ unsigned long long this_offset;
+
+ /*
+ * In the NUMA case we dont use the TSC as they are not
+ * synchronized across all CPUs.
+ */
+#ifndef CONFIG_NUMA
+ if (!use_tsc)
+#endif
+ /* no locking but a rare wrong value is not a big deal */
+ return jiffies_64 * (1000000000 / HZ);
+
+ /* Read the Time Stamp Counter */
+ rdtscll(this_offset);
+
+ /* return the value in ns */
+ return cycles_2_ns(this_offset);
+}
+
+/* ------ Calibrate the TSC -------
+ * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
+ * Too much 64-bit arithmetic here to do this cleanly in C, and for
+ * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
+ * output busy loop as low as possible. We avoid reading the CTC registers
+ * directly because of the awkward 8-bit access mechanism of the 82C54
+ * device.
+ */
+
+#define CALIBRATE_TIME (5 * 1000020/HZ)
+
+unsigned long calibrate_tsc(void)
+{
+ mach_prepare_counter();
+
+ {
+ unsigned long startlow, starthigh;
+ unsigned long endlow, endhigh;
+ unsigned long count;
+
+ rdtsc(startlow,starthigh);
+ mach_countup(&count);
+ rdtsc(endlow,endhigh);
+
+
+ /* Error: ECTCNEVERSET */
+ if (count <= 1)
+ goto bad_ctc;
+
+ /* 64-bit subtract - gcc just messes up with long longs */
+ __asm__("subl %2,%0\n\t"
+ "sbbl %3,%1"
+ :"=a" (endlow), "=d" (endhigh)
+ :"g" (startlow), "g" (starthigh),
+ "0" (endlow), "1" (endhigh));
+
+ /* Error: ECPUTOOFAST */
+ if (endhigh)
+ goto bad_ctc;
+
+ /* Error: ECPUTOOSLOW */
+ if (endlow <= CALIBRATE_TIME)
+ goto bad_ctc;
+
+ __asm__("divl %2"
+ :"=a" (endlow), "=d" (endhigh)
+ :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
+
+ return endlow;
+ }
+
+ /*
+ * The CTC wasn't reliable: we got a hit on the very first read,
+ * or the CPU was so fast/slow that the quotient wouldn't fit in
+ * 32 bits..
+ */
+bad_ctc:
+ return 0;
+}
+
+int recalibrate_cpu_khz(void)
+{
+#ifndef CONFIG_SMP
+ unsigned long cpu_khz_old = cpu_khz;
+
+ if (cpu_has_tsc) {
+ init_cpu_khz();
+ cpu_data[0].loops_per_jiffy =
+ cpufreq_scale(cpu_data[0].loops_per_jiffy,
+ cpu_khz_old,
+ cpu_khz);
+ return 0;
+ } else
+ return -ENODEV;
+#else
+ return -ENODEV;
+#endif
+}
+EXPORT_SYMBOL(recalibrate_cpu_khz);
+
+
+/* calculate cpu_khz */
+void init_cpu_khz(void)
+{
+ if (cpu_has_tsc) {
+ unsigned long tsc_quotient = calibrate_tsc();
+ if (tsc_quotient) {
+ /* report CPU clock rate in Hz.
+ * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
+ * clock/second. Our precision is about 100 ppm.
+ */
+ { unsigned long eax=0, edx=1000;
+ __asm__("divl %2"
+ :"=a" (cpu_khz), "=d" (edx)
+ :"r" (tsc_quotient),
+ "0" (eax), "1" (edx));
+ printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
+ }
+ }
+ }
+}
+
+
+#ifdef CONFIG_CPU_FREQ
+#include <linux/workqueue.h>
+
+static unsigned int cpufreq_delayed_issched = 0;
+static unsigned int cpufreq_init = 0;
+static struct work_struct cpufreq_delayed_get_work;
+
+static void handle_cpufreq_delayed_get(void *v)
+{
+ unsigned int cpu;
+ for_each_online_cpu(cpu) {
+ cpufreq_get(cpu);
+ }
+ cpufreq_delayed_issched = 0;
+}
+
+/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
+ * to verify the CPU frequency the timing core thinks the CPU is running
+ * at is still correct.
+ */
+void cpufreq_delayed_get(void)
+{
+ if (cpufreq_init && !cpufreq_delayed_issched) {
+ cpufreq_delayed_issched = 1;
+ printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");
+ schedule_work(&cpufreq_delayed_get_work);
+ }
+}
+
+/* If the CPU frequency is scaled, TSC-based delays will need a different
+ * loops_per_jiffy value to function properly.
+ */
+
+static unsigned int ref_freq = 0;
+static unsigned long loops_per_jiffy_ref = 0;
+
+#ifndef CONFIG_SMP
+static unsigned long fast_gettimeoffset_ref = 0;
+static unsigned long cpu_khz_ref = 0;
+#endif
+
+static int
+time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
+ void *data)
+{
+ struct cpufreq_freqs *freq = data;
+
+ if (val != CPUFREQ_RESUMECHANGE)
+ write_seqlock_irq(&xtime_lock);
+ if (!ref_freq) {
+ ref_freq = freq->old;
+ loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
+#ifndef CONFIG_SMP
+ fast_gettimeoffset_ref = fast_gettimeoffset_quotient;
+ cpu_khz_ref = cpu_khz;
+#endif
+ }
+
+ if ((val == CPUFREQ_PRECHANGE && freq->old < freq->new) ||
+ (val == CPUFREQ_POSTCHANGE && freq->old > freq->new) ||
+ (val == CPUFREQ_RESUMECHANGE)) {
+ if (!(freq->flags & CPUFREQ_CONST_LOOPS))
+ cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
+#ifndef CONFIG_SMP
+ if (cpu_khz)
+ cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
+ if (use_tsc) {
+ if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
+ fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
+ set_cyc2ns_scale(cpu_khz);
+ }
+ }
+#endif
+ }
+
+ if (val != CPUFREQ_RESUMECHANGE)
+ write_sequnlock_irq(&xtime_lock);
+
+ return 0;
+}
+
+static struct notifier_block time_cpufreq_notifier_block = {
+ .notifier_call = time_cpufreq_notifier
+};
+
+
+static int __init cpufreq_tsc(void)
+{
+ int ret;
+ INIT_WORK(&cpufreq_delayed_get_work, handle_cpufreq_delayed_get, NULL);
+ ret = cpufreq_register_notifier(&time_cpufreq_notifier_block,
+ CPUFREQ_TRANSITION_NOTIFIER);
+ if (!ret)
+ cpufreq_init = 1;
+ return ret;
+}
+core_initcall(cpufreq_tsc);
+
+#else /* CONFIG_CPU_FREQ */
+void cpufreq_delayed_get(void) { return; }
+#endif
diff --git a/include/asm-i386/timex.h b/include/asm-i386/timex.h
index 292b5a6..ebcc74e 100644
--- a/include/asm-i386/timex.h
+++ b/include/asm-i386/timex.h
@@ -8,6 +8,7 @@

#include <linux/config.h>
#include <asm/processor.h>
+#include <asm/tsc.h>

#ifdef CONFIG_X86_ELAN
# define CLOCK_TICK_RATE 1189200 /* AMD Elan has different frequency! */
@@ -16,39 +17,6 @@
#endif


-/*
- * Standard way to access the cycle counter on i586+ CPUs.
- * Currently only used on SMP.
- *
- * If you really have a SMP machine with i486 chips or older,
- * compile for that, and this will just always return zero.
- * That's ok, it just means that the nicer scheduling heuristics
- * won't work for you.
- *
- * We only use the low 32 bits, and we'd simply better make sure
- * that we reschedule before that wraps. Scheduling at least every
- * four billion cycles just basically sounds like a good idea,
- * regardless of how fast the machine is.
- */
-typedef unsigned long long cycles_t;
-
-static inline cycles_t get_cycles (void)
-{
- unsigned long long ret=0;
-
-#ifndef CONFIG_X86_TSC
- if (!cpu_has_tsc)
- return 0;
-#endif
-
-#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC)
- rdtscll(ret);
-#endif
- return ret;
-}
-
-extern unsigned int cpu_khz;
-
extern int read_current_timer(unsigned long *timer_value);
#define ARCH_HAS_READ_CURRENT_TIMER 1

diff --git a/include/asm-i386/tsc.h b/include/asm-i386/tsc.h
new file mode 100644
index 0000000..86288f2
--- /dev/null
+++ b/include/asm-i386/tsc.h
@@ -0,0 +1,44 @@
+/*
+ * linux/include/asm-i386/tsc.h
+ *
+ * i386 TSC related functions
+ */
+#ifndef _ASM_i386_TSC_H
+#define _ASM_i386_TSC_H
+
+#include <linux/config.h>
+#include <asm/processor.h>
+
+/*
+ * Standard way to access the cycle counter on i586+ CPUs.
+ * Currently only used on SMP.
+ *
+ * If you really have a SMP machine with i486 chips or older,
+ * compile for that, and this will just always return zero.
+ * That's ok, it just means that the nicer scheduling heuristics
+ * won't work for you.
+ *
+ * We only use the low 32 bits, and we'd simply better make sure
+ * that we reschedule before that wraps. Scheduling at least every
+ * four billion cycles just basically sounds like a good idea,
+ * regardless of how fast the machine is.
+ */
+typedef unsigned long long cycles_t;
+
+static inline cycles_t get_cycles (void)
+{
+ unsigned long long ret=0;
+
+#ifndef CONFIG_X86_TSC
+ if (!cpu_has_tsc)
+ return 0;
+#endif
+
+#if defined(CONFIG_X86_GENERIC) || defined(CONFIG_X86_TSC)
+ rdtscll(ret);
+#endif
+ return ret;
+}
+
+extern unsigned int cpu_khz;
+#endif

2005-11-12 04:51:01

by john stultz

[permalink] [raw]
Subject: [PATCH 12/13] Time: i386/x86-64 Clocksource Drivers

All,

This patch implements the time sources shared between i386 and x86-64
(acpi_pm, cyclone, hpet, pit, tsc and tsc-interp). The patch should
apply on top of the timeofday-arch-i386-part4 patch as well as the
arch-x86-64 patch

The patch should be fairly straight forward, only adding the new
clocksources.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/i386/kernel/Makefile | 1
arch/i386/kernel/hpet.c | 66 ++++++++++++++++
arch/i386/kernel/i8253.c | 55 ++++++++++++++
arch/i386/kernel/tsc.c | 75 +++++++++++++++++++
drivers/Makefile | 1
drivers/clocksource/Makefile | 3
drivers/clocksource/acpi_pm.c | 152 +++++++++++++++++++++++++++++++++++++++
drivers/clocksource/cyclone.c | 144 ++++++++++++++++++++++++++++++++++++
drivers/clocksource/tsc-interp.c | 112 ++++++++++++++++++++++++++++
9 files changed, 609 insertions(+)

linux-2.6.14_timeofday-clocks-i386_B10.patch
============================================
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 5d1d9c9..984d488 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -33,6 +33,7 @@ obj-$(CONFIG_ACPI_SRAT) += srat.o
obj-$(CONFIG_HPET_TIMER) += time_hpet.o
obj-$(CONFIG_EFI) += efi.o efi_stub.o
obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
+obj-$(CONFIG_HPET_TIMER) += hpet.o

EXTRA_AFLAGS := -traditional

diff --git a/arch/i386/kernel/hpet.c b/arch/i386/kernel/hpet.c
new file mode 100644
index 0000000..67ebcf8
--- /dev/null
+++ b/arch/i386/kernel/hpet.c
@@ -0,0 +1,66 @@
+#include <linux/clocksource.h>
+#include <linux/hpet.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/hpet.h>
+
+#define HPET_MASK (0xFFFFFFFF)
+#define HPET_SHIFT 22
+
+/* FSEC = 10^-15 NSEC = 10^-9 */
+#define FSEC_PER_NSEC 1000000
+
+static void *hpet_ptr;
+
+static cycle_t read_hpet(void)
+{
+ return (cycle_t)readl(hpet_ptr);
+}
+
+struct clocksource clocksource_hpet = {
+ .name = "hpet",
+ .rating = 250,
+ .read = read_hpet,
+ .mask = (cycle_t)HPET_MASK,
+ .mult = 0, /* set below */
+ .shift = HPET_SHIFT,
+ .is_continuous = 1,
+};
+
+static int __init init_hpet_clocksource(void)
+{
+ unsigned long hpet_period;
+ void __iomem* hpet_base;
+ u64 tmp;
+
+ if (!hpet_address)
+ return -ENODEV;
+
+ /* calculate the hpet address */
+ hpet_base =
+ (void __iomem*)ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
+ hpet_ptr = hpet_base + HPET_COUNTER;
+
+ /* calculate the frequency */
+ hpet_period = readl(hpet_base + HPET_PERIOD);
+
+
+ /* hpet period is in femto seconds per cycle
+ * so we need to convert this to ns/cyc units
+ * aproximated by mult/2^shift
+ *
+ * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
+ * fsec/cyc * 1ns/1000000fsec * 2^shift = mult
+ * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
+ * (fsec/cyc << shift)/1000000 = mult
+ * (hpet_period << shift)/FSEC_PER_NSEC = mult
+ */
+ tmp = (u64)hpet_period << HPET_SHIFT;
+ do_div(tmp, FSEC_PER_NSEC);
+ clocksource_hpet.mult = (u32)tmp;
+
+ register_clocksource(&clocksource_hpet);
+ return 0;
+}
+module_init(init_hpet_clocksource);
diff --git a/arch/i386/kernel/i8253.c b/arch/i386/kernel/i8253.c
index e11928d..320968e 100644
--- a/arch/i386/kernel/i8253.c
+++ b/arch/i386/kernel/i8253.c
@@ -6,6 +6,7 @@
#include <linux/jiffies.h>
#include <linux/spinlock.h>
#include <linux/sysdev.h>
+#include <linux/clocksource.h>
#include <linux/module.h>

#include <asm/delay.h>
@@ -55,3 +56,57 @@ static int __init init_timer_sysfs(void)
}

device_initcall(init_timer_sysfs);
+
+
+/* Since the PIT overflows every tick, its not very useful
+ * to just read by itself. So use jiffies to emulate a free
+ * running counter.
+ */
+
+static cycle_t pit_read(void)
+{
+ unsigned long flags, seq;
+ int count;
+ u64 jifs;
+
+ do {
+ seq = read_seqbegin(&xtime_lock);
+
+ spin_lock_irqsave(&i8253_lock, flags);
+
+ outb_p(0x00, PIT_MODE); /* latch the count ASAP */
+ count = inb_p(PIT_CH0); /* read the latched count */
+ count |= inb_p(PIT_CH0) << 8;
+
+ /* VIA686a test code... reset the latch if count > max + 1 */
+ if (count > LATCH) {
+ outb_p(0x34, PIT_MODE);
+ outb_p(LATCH & 0xff, PIT_CH0);
+ outb(LATCH >> 8, PIT_CH0);
+ count = LATCH - 1;
+ }
+ spin_unlock_irqrestore(&i8253_lock, flags);
+ jifs = get_jiffies_64() - INITIAL_JIFFIES;
+ } while (read_seqretry(&xtime_lock, seq));
+
+ count = (LATCH-1) - count;
+
+ return (cycle_t)(jifs * LATCH) + count;
+}
+
+static struct clocksource clocksource_pit = {
+ .name = "pit",
+ .rating = 110,
+ .read = pit_read,
+ .mask = (cycle_t)-1,
+ .mult = 0,
+ .shift = 20,
+};
+
+static int __init init_pit_clocksource(void)
+{
+ clocksource_pit.mult = clocksource_hz2mult(CLOCK_TICK_RATE, 20);
+ register_clocksource(&clocksource_pit);
+ return 0;
+}
+module_init(init_pit_clocksource);
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c
index 5608c21..3f9d1e8 100644
--- a/arch/i386/kernel/tsc.c
+++ b/arch/i386/kernel/tsc.c
@@ -306,3 +306,78 @@ static int __init cpufreq_tsc(void)
core_initcall(cpufreq_tsc);

#endif
+
+/* Clock source code */
+#include <linux/clocksource.h>
+
+static unsigned long current_tsc_khz = 0;
+static int tsc_update_callback(void);
+
+static cycle_t read_tsc(void)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret;
+}
+
+static cycle_t read_tsc_c3(void)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret + tsc_read_c3_time();
+}
+
+
+static struct clocksource clocksource_tsc = {
+ .name = "tsc",
+ .rating = 300,
+ .read = read_tsc,
+ .mask = (cycle_t)-1,
+ .mult = 0, /* to be set */
+ .shift = 22,
+ .update_callback = tsc_update_callback,
+ .is_continuous = 1,
+};
+
+static int tsc_update_callback(void)
+{
+ int change = 0;
+ /* check to see if we should switch to the safe clocksource */
+ if (tsc_read_c3_time() &&
+ strncmp(clocksource_tsc.name, "c3tsc", 5)) {
+ printk("Falling back to C3 safe TSC\n");
+ clocksource_tsc.read = read_tsc_c3;
+ clocksource_tsc.name = "c3tsc";
+ change = 1;
+ }
+
+ if (clocksource_tsc.rating != 50 && check_tsc_unstable()) {
+ clocksource_tsc.rating = 50;
+ reselect_clocksource();
+ change = 1;
+ }
+ /* only update if tsc_khz has changed */
+ if (current_tsc_khz != tsc_khz){
+ current_tsc_khz = tsc_khz;
+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+ clocksource_tsc.shift);
+ change = 1;
+ }
+ return change;
+}
+
+static int __init init_tsc_clocksource(void)
+{
+
+ /* TSC initialization is done in arch/i386/kernel/tsc.c */
+ if (cpu_has_tsc && tsc_khz) {
+ current_tsc_khz = tsc_khz;
+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+ clocksource_tsc.shift);
+ register_clocksource(&clocksource_tsc);
+ }
+ return 0;
+}
+
+module_init(init_tsc_clocksource);
+
diff --git a/drivers/Makefile b/drivers/Makefile
index fac1e16..b23511f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -69,3 +69,4 @@ obj-$(CONFIG_SGI_IOC4) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
+obj-$(CONFIG_GENERIC_TIME) += clocksource/
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
new file mode 100644
index 0000000..45f60ad
--- /dev/null
+++ b/drivers/clocksource/Makefile
@@ -0,0 +1,3 @@
+#XXX doesn't boot! obj-$(CONFIG_X86) += tsc-interp.o
+obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
+obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
new file mode 100644
index 0000000..2523c71
--- /dev/null
+++ b/drivers/clocksource/acpi_pm.c
@@ -0,0 +1,152 @@
+/*
+ * linux/drivers/clocksource/acpi_pm.c
+ *
+ * This file contains the ACPI PM based clocksource.
+ *
+ * This code was largely moved from the i386 timer_pm.c file
+ * which was (C) Dominik Brodowski <[email protected]> 2003
+ * and contained the following comments:
+ *
+ * Driver to use the Power Management Timer (PMTMR) available in some
+ * southbridges as primary timing source for the Linux kernel.
+ *
+ * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
+ * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
+ *
+ * This file is licensed under the GPL v2.
+ */
+
+
+#include <linux/clocksource.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <asm/io.h>
+
+/* Number of PMTMR ticks expected during calibration run */
+#define PMTMR_TICKS_PER_SEC 3579545
+
+#if (defined(CONFIG_X86) && (!defined(CONFIG_X86_64)))
+#include "mach_timer.h"
+#define PMTMR_EXPECTED_RATE ((PMTMR_TICKS_PER_SEC*CALIBRATE_TIME_MSEC)/1000)
+#endif
+
+/* The I/O port the PMTMR resides at.
+ * The location is detected during setup_arch(),
+ * in arch/i386/acpi/boot.c */
+extern u32 acpi_pmtmr_ioport;
+extern int acpi_pmtmr_buggy;
+
+#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
+
+
+static inline u32 read_pmtmr(void)
+{
+ /* mask the output to 24 bits */
+ return inl(acpi_pmtmr_ioport) & ACPI_PM_MASK;
+}
+
+static cycle_t acpi_pm_read_verified(void)
+{
+ u32 v1=0,v2=0,v3=0;
+ /* It has been reported that because of various broken
+ * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock
+ * source is not latched, so you must read it multiple
+ * times to insure a safe value is read.
+ */
+ do {
+ v1 = read_pmtmr();
+ v2 = read_pmtmr();
+ v3 = read_pmtmr();
+ } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
+ || (v3 > v1 && v3 < v2));
+
+ return (cycle_t)v2;
+}
+
+
+static cycle_t acpi_pm_read(void)
+{
+ return (cycle_t)read_pmtmr();
+}
+
+struct clocksource clocksource_acpi_pm = {
+ .name = "acpi_pm",
+ .rating = 200,
+ .read = acpi_pm_read,
+ .mask = (cycle_t)ACPI_PM_MASK,
+ .mult = 0, /*to be caluclated*/
+ .shift = 22,
+ .is_continuous = 1,
+};
+
+#if (defined(CONFIG_X86) && (!defined(CONFIG_X86_64)))
+/*
+ * Some boards have the PMTMR running way too fast. We check
+ * the PMTMR rate against PIT channel 2 to catch these cases.
+ */
+static int __init verify_pmtmr_rate(void)
+{
+ u32 value1, value2;
+ unsigned long count, delta;
+
+ mach_prepare_counter();
+ value1 = read_pmtmr();
+ mach_countup(&count);
+ value2 = read_pmtmr();
+ delta = (value2 - value1) & ACPI_PM_MASK;
+
+ /* Check that the PMTMR delta is within 5% of what we expect */
+ if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
+ delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
+ printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% of normal - aborting.\n", 100UL * delta / PMTMR_EXPECTED_RATE);
+ return -1;
+ }
+
+ return 0;
+}
+#else
+#define verify_pmtmr_rate() (0)
+#endif
+
+static int __init init_acpi_pm_clocksource(void)
+{
+ u32 value1, value2;
+ unsigned int i;
+
+ if (!acpi_pmtmr_ioport)
+ return -ENODEV;
+
+ clocksource_acpi_pm.mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC,
+ clocksource_acpi_pm.shift);
+
+ /* "verify" this timing source */
+ value1 = read_pmtmr();
+ for (i = 0; i < 10000; i++) {
+ value2 = read_pmtmr();
+ if (value2 == value1)
+ continue;
+ if (value2 > value1)
+ goto pm_good;
+ if ((value2 < value1) && ((value2) < 0xFFF))
+ goto pm_good;
+ printk(KERN_INFO "PM-Timer had inconsistent results: 0x%#x, 0x%#x - aborting.\n", value1, value2);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1);
+ return -ENODEV;
+
+pm_good:
+ if (verify_pmtmr_rate() != 0)
+ return -ENODEV;
+
+ /* check to see if pmtmr is known buggy */
+ if (acpi_pmtmr_buggy) {
+ clocksource_acpi_pm.read = acpi_pm_read_verified;
+ clocksource_acpi_pm.rating = 110;
+ }
+
+ register_clocksource(&clocksource_acpi_pm);
+ return 0;
+}
+
+module_init(init_acpi_pm_clocksource);
diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c
new file mode 100644
index 0000000..5630195
--- /dev/null
+++ b/drivers/clocksource/cyclone.c
@@ -0,0 +1,144 @@
+#include <linux/clocksource.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/timex.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include "mach_timer.h"
+
+#define CYCLONE_CBAR_ADDR 0xFEB00CD0 /* base address ptr*/
+#define CYCLONE_PMCC_OFFSET 0x51A0 /* offset to control register */
+#define CYCLONE_MPCS_OFFSET 0x51A8 /* offset to select register */
+#define CYCLONE_MPMC_OFFSET 0x51D0 /* offset to count register */
+#define CYCLONE_TIMER_FREQ 100000000
+#define CYCLONE_TIMER_MASK (0xFFFFFFFF) /* 32 bit mask */
+
+int use_cyclone = 0;
+static void __iomem *cyclone_ptr;
+
+static cycle_t read_cyclone(void)
+{
+ return (cycle_t)readl(cyclone_ptr);
+}
+
+struct clocksource clocksource_cyclone = {
+ .name = "cyclone",
+ .rating = 250,
+ .read = read_cyclone,
+ .mask = (cycle_t)CYCLONE_TIMER_MASK,
+ .mult = 10,
+ .shift = 0,
+ .is_continuous = 1,
+};
+
+static unsigned long __init calibrate_cyclone(void)
+{
+ u64 delta64;
+ unsigned long start, end;
+ unsigned long i, count;
+ unsigned long cyclone_freq_khz;
+
+ /* repeat 3 times to make sure the cache is warm */
+ for(i=0; i < 3; i++) {
+ mach_prepare_counter();
+ start = readl(cyclone_ptr);
+ mach_countup(&count);
+ end = readl(cyclone_ptr);
+ }
+
+ delta64 = end - start;
+
+ delta64 += CALIBRATE_TIME_MSEC/2; /* round for do_div */
+ do_div(delta64,CALIBRATE_TIME_MSEC);
+
+ cyclone_freq_khz = (unsigned long)delta64;
+
+ printk("calculated cyclone_freq: %lu khz\n", cyclone_freq_khz);
+ return cyclone_freq_khz;
+}
+
+static int __init init_cyclone_clocksource(void)
+{
+ unsigned long base; /* saved value from CBAR */
+ unsigned long offset;
+ u32 __iomem* reg;
+ u32 __iomem* volatile cyclone_timer; /* Cyclone MPMC0 register */
+ unsigned long khz;
+ int i;
+
+ /*make sure we're on a summit box*/
+ if (!use_cyclone) return -ENODEV;
+
+ printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
+
+ /* find base address */
+ offset = CYCLONE_CBAR_ADDR;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if(!reg){
+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
+ return -ENODEV;
+ }
+ /* even on 64bit systems, this is only 32bits */
+ base = readl(reg);
+ if(!base){
+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
+ return -ENODEV;
+ }
+ iounmap(reg);
+
+ /* setup PMCC */
+ offset = base + CYCLONE_PMCC_OFFSET;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if(!reg){
+ printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
+ return -ENODEV;
+ }
+ writel(0x00000001,reg);
+ iounmap(reg);
+
+ /* setup MPCS */
+ offset = base + CYCLONE_MPCS_OFFSET;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if(!reg){
+ printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
+ return -ENODEV;
+ }
+ writel(0x00000001,reg);
+ iounmap(reg);
+
+ /* map in cyclone_timer */
+ offset = base + CYCLONE_MPMC_OFFSET;
+ cyclone_timer = ioremap_nocache(offset, sizeof(u64));
+ if(!cyclone_timer){
+ printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
+ return -ENODEV;
+ }
+
+ /*quick test to make sure its ticking*/
+ for(i=0; i<3; i++){
+ u32 old = readl(cyclone_timer);
+ int stall = 100;
+ while(stall--) barrier();
+ if(readl(cyclone_timer) == old){
+ printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
+ iounmap(cyclone_timer);
+ cyclone_timer = NULL;
+ return -ENODEV;
+ }
+ }
+ cyclone_ptr = cyclone_timer;
+
+ /* sort out mult/shift values */
+ khz = calibrate_cyclone();
+ clocksource_cyclone.shift = 22;
+ clocksource_cyclone.mult = clocksource_khz2mult(khz,
+ clocksource_cyclone.shift);
+
+ register_clocksource(&clocksource_cyclone);
+
+ return 0;
+}
+
+module_init(init_cyclone_clocksource);
diff --git a/drivers/clocksource/tsc-interp.c b/drivers/clocksource/tsc-interp.c
new file mode 100644
index 0000000..fee083e
--- /dev/null
+++ b/drivers/clocksource/tsc-interp.c
@@ -0,0 +1,112 @@
+/* TSC-Jiffies Interpolation clocksource
+ Example interpolation clocksource.
+TODO:
+ o per-cpu TSC offsets
+*/
+#include <linux/clocksource.h>
+#include <linux/timer.h>
+#include <linux/timex.h>
+#include <linux/init.h>
+#include <linux/jiffies.h>
+#include <linux/threads.h>
+#include <linux/smp.h>
+
+static unsigned long current_tsc_khz = 0;
+
+static seqlock_t tsc_interp_lock = SEQLOCK_UNLOCKED;
+static unsigned long tsc_then;
+static unsigned long jiffies_then;
+struct timer_list tsc_interp_timer;
+
+static unsigned long mult, shift;
+
+#define NSEC_PER_JIFFY ((((unsigned long long)NSEC_PER_SEC)<<8)/ACTHZ)
+#define SHIFT_VAL 22
+
+static cycle_t read_tsc_interp(void);
+static void tsc_interp_update_callback(void);
+
+static struct clocksource clocksource_tsc_interp = {
+ .name = "tsc-interp",
+ .rating = 150,
+ .type = CLOCKSOURCE_FUNCTION,
+ .read_fnct = read_tsc_interp,
+ .mask = (cycle_t)((1ULL<<32)-1),
+ .mult = 1<<SHIFT_VAL,
+ .shift = SHIFT_VAL,
+ .update_callback = tsc_interp_update_callback,
+};
+
+static void tsc_interp_sync(unsigned long unused)
+{
+ cycle_t tsc_now;
+ unsigned long jiffies_now;
+
+ do {
+ jiffies_now = jiffies;
+ rdtscll(tsc_now);
+ } while (jiffies_now != jiffies);
+
+ write_seqlock(&tsc_interp_lock);
+ jiffies_then = jiffies_now;
+ tsc_then = tsc_now;
+ write_sequnlock(&tsc_interp_lock);
+
+ mod_timer(&tsc_interp_timer, jiffies+1);
+}
+
+
+static cycle_t read_tsc_interp(void)
+{
+ cycle_t ret;
+ cycle_t now, then;
+ unsigned long jiffs_now, jiffs_then;
+ unsigned long seq;
+
+ do {
+ seq = read_seqbegin(&tsc_interp_lock);
+
+ jiffs_now = jiffies;
+ jiffs_then = jiffies_then;
+ then = tsc_then;
+
+ } while (read_seqretry(&tsc_interp_lock, seq));
+
+ rdtscll(now);
+ ret = (cycle_t)jiffs_then * NSEC_PER_JIFFY;
+ if (jiffs_then == jiffs_now)
+ ret += min((cycle_t)NSEC_PER_JIFFY,(cycle_t)((now - then)*mult)>> shift);
+ else
+ ret += (cycle_t)(jiffs_now - jiffs_then)*NSEC_PER_JIFFY;
+
+ return ret;
+}
+
+static void tsc_interp_update_callback(void)
+{
+ /* only update if tsc_khz has changed */
+ if (current_tsc_khz != tsc_khz){
+ current_tsc_khz = tsc_khz;
+ mult = clocksource_khz2mult(current_tsc_khz, shift);
+ }
+}
+
+
+static int __init init_tsc_interp_clocksource(void)
+{
+ /* TSC initialization is done in arch/i386/kernel/tsc.c */
+ if (cpu_has_tsc && tsc_khz) {
+ current_tsc_khz = tsc_khz;
+ shift = SHIFT_VAL;
+ mult = clocksource_khz2mult(current_tsc_khz, shift);
+ /* setup periodic soft-timer */
+ init_timer(&tsc_interp_timer);
+ tsc_interp_timer.function = tsc_interp_sync;
+ tsc_interp_timer.expires = jiffies;
+ add_timer(&tsc_interp_timer);
+
+ register_clocksource(&clocksource_tsc_interp);
+ }
+ return 0;
+}
+module_init(init_tsc_interp_clocksource);

2005-11-12 04:51:09

by john stultz

[permalink] [raw]
Subject: [PATCH 8/13] Time: i386 Conversion - part 4: ACPI PM variable renaming

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the fourth of six, renames some ACPI PM
variables.

It applies on top of my timeofday-arch-i386-part3 patch. This patch is
part the timeofday-arch-i386 patchset, so without the following parts it
is not expected to compile.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

boot.c | 18 ++++++++++--------
1 files changed, 10 insertions(+), 8 deletions(-)

linux-2.6.14_timeofday-arch-i386-part4_B10.patch
============================================
diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c
index bb9ecdc..6d4bd42 100644
--- a/arch/i386/kernel/acpi/boot.c
+++ b/arch/i386/kernel/acpi/boot.c
@@ -612,7 +612,8 @@ static int __init acpi_parse_hpet(unsign
#endif

#ifdef CONFIG_X86_PM_TIMER
-extern u32 pmtmr_ioport;
+u32 acpi_pmtmr_ioport;
+int acpi_pmtmr_buggy;
#endif

static int __init acpi_parse_fadt(unsigned long phys, unsigned long size)
@@ -640,21 +641,22 @@ static int __init acpi_parse_fadt(unsign
ACPI_ADR_SPACE_SYSTEM_IO)
return 0;

- pmtmr_ioport = fadt->xpm_tmr_blk.address;
+ acpi_pmtmr_ioport = fadt->xpm_tmr_blk.address;
/*
* "X" fields are optional extensions to the original V1.0
* fields, so we must selectively expand V1.0 fields if the
* corresponding X field is zero.
*/
- if (!pmtmr_ioport)
- pmtmr_ioport = fadt->V1_pm_tmr_blk;
+ if (!acpi_pmtmr_ioport)
+ acpi_pmtmr_ioport = fadt->V1_pm_tmr_blk;
} else {
/* FADT rev. 1 */
- pmtmr_ioport = fadt->V1_pm_tmr_blk;
+ acpi_pmtmr_ioport = fadt->V1_pm_tmr_blk;
}
- if (pmtmr_ioport)
- printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n",
- pmtmr_ioport);
+
+ if (acpi_pmtmr_ioport)
+ printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n", acpi_pmtmr_ioport);
+
#endif
return 0;
}

2005-11-12 04:51:44

by john stultz

[permalink] [raw]
Subject: [PATCH 10/13] Time: i386 Conversion - part 6: Remove Old Code

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the final of four, removes the old
timers/timer_opts infrastructure.

It applies on top of my timeofday-arch-i386-part5 patch. This patch is
the last in the timeofday-arch-i386 patchset, so you should be able to
build and boot a kernel after it has been applied.

Note that this patch does not provide any i386 clocksources, so you will
only have the jiffies clocksource. To get full replacements for the code
being removed here, the following timeofday-clocks-i386 patch will need
to be applied.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

Makefile | 9 -
common.c | 88 ------------
timer.c | 75 ----------
timer_cyclone.c | 259 -------------------------------------
timer_hpet.c | 217 -------------------------------
timer_none.c | 39 -----
timer_pit.c | 163 -----------------------
timer_pm.c | 268 --------------------------------------
timer_tsc.c | 388 --------------------------------------------------------
9 files changed, 1506 deletions(-)

linux-2.6.14_timeofday-arch-i386-part6_B10.patch
============================================
diff --git a/arch/i386/kernel/timers/Makefile b/arch/i386/kernel/timers/Makefile
deleted file mode 100644
index 8fa12be..0000000
--- a/arch/i386/kernel/timers/Makefile
+++ /dev/null
@@ -1,9 +0,0 @@
-#
-# Makefile for x86 timers
-#
-
-obj-y := timer.o timer_none.o timer_tsc.o timer_pit.o common.o
-
-obj-$(CONFIG_X86_CYCLONE_TIMER) += timer_cyclone.o
-obj-$(CONFIG_HPET_TIMER) += timer_hpet.o
-obj-$(CONFIG_X86_PM_TIMER) += timer_pm.o
diff --git a/arch/i386/kernel/timers/common.c b/arch/i386/kernel/timers/common.c
deleted file mode 100644
index 535f4d8..0000000
--- a/arch/i386/kernel/timers/common.c
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Common functions used across the timers go here
- */
-
-#include <linux/init.h>
-#include <linux/timex.h>
-#include <linux/errno.h>
-#include <linux/jiffies.h>
-#include <linux/module.h>
-
-#include <asm/io.h>
-#include <asm/timer.h>
-#include <asm/hpet.h>
-
-#include "mach_timer.h"
-
-#ifdef CONFIG_HPET_TIMER
-/* ------ Calibrate the TSC using HPET -------
- * Return 2^32 * (1 / (TSC clocks per usec)) for getting the CPU freq.
- * Second output is parameter 1 (when non NULL)
- * Set 2^32 * (1 / (tsc per HPET clk)) for delay_hpet().
- * calibrate_tsc() calibrates the processor TSC by comparing
- * it to the HPET timer of known frequency.
- * Too much 64-bit arithmetic here to do this cleanly in C
- */
-#define CALIBRATE_CNT_HPET (5 * hpet_tick)
-#define CALIBRATE_TIME_HPET (5 * KERNEL_TICK_USEC)
-
-unsigned long __devinit calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr)
-{
- unsigned long tsc_startlow, tsc_starthigh;
- unsigned long tsc_endlow, tsc_endhigh;
- unsigned long hpet_start, hpet_end;
- unsigned long result, remain;
-
- hpet_start = hpet_readl(HPET_COUNTER);
- rdtsc(tsc_startlow, tsc_starthigh);
- do {
- hpet_end = hpet_readl(HPET_COUNTER);
- } while ((hpet_end - hpet_start) < CALIBRATE_CNT_HPET);
- rdtsc(tsc_endlow, tsc_endhigh);
-
- /* 64-bit subtract - gcc just messes up with long longs */
- __asm__("subl %2,%0\n\t"
- "sbbl %3,%1"
- :"=a" (tsc_endlow), "=d" (tsc_endhigh)
- :"g" (tsc_startlow), "g" (tsc_starthigh),
- "0" (tsc_endlow), "1" (tsc_endhigh));
-
- /* Error: ECPUTOOFAST */
- if (tsc_endhigh)
- goto bad_calibration;
-
- /* Error: ECPUTOOSLOW */
- if (tsc_endlow <= CALIBRATE_TIME_HPET)
- goto bad_calibration;
-
- ASM_DIV64_REG(result, remain, tsc_endlow, 0, CALIBRATE_TIME_HPET);
- if (remain > (tsc_endlow >> 1))
- result++; /* rounding the result */
-
- if (tsc_hpet_quotient_ptr) {
- unsigned long tsc_hpet_quotient;
-
- ASM_DIV64_REG(tsc_hpet_quotient, remain, tsc_endlow, 0,
- CALIBRATE_CNT_HPET);
- if (remain > (tsc_endlow >> 1))
- tsc_hpet_quotient++; /* rounding the result */
- *tsc_hpet_quotient_ptr = tsc_hpet_quotient;
- }
-
- return result;
-bad_calibration:
- /*
- * the CPU was so fast/slow that the quotient wouldn't fit in
- * 32 bits..
- */
- return 0;
-}
-#endif
-
-
-unsigned long read_timer_tsc(void)
-{
- unsigned long retval;
- rdtscl(retval);
- return retval;
-}
diff --git a/arch/i386/kernel/timers/timer.c b/arch/i386/kernel/timers/timer.c
deleted file mode 100644
index 7e39ed8..0000000
--- a/arch/i386/kernel/timers/timer.c
+++ /dev/null
@@ -1,75 +0,0 @@
-#include <linux/init.h>
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <asm/timer.h>
-
-#ifdef CONFIG_HPET_TIMER
-/*
- * HPET memory read is slower than tsc reads, but is more dependable as it
- * always runs at constant frequency and reduces complexity due to
- * cpufreq. So, we prefer HPET timer to tsc based one. Also, we cannot use
- * timer_pit when HPET is active. So, we default to timer_tsc.
- */
-#endif
-/* list of timers, ordered by preference, NULL terminated */
-static struct init_timer_opts* __initdata timers[] = {
-#ifdef CONFIG_X86_CYCLONE_TIMER
- &timer_cyclone_init,
-#endif
-#ifdef CONFIG_HPET_TIMER
- &timer_hpet_init,
-#endif
-#ifdef CONFIG_X86_PM_TIMER
- &timer_pmtmr_init,
-#endif
- &timer_tsc_init,
- &timer_pit_init,
- NULL,
-};
-
-static char clock_override[10] __initdata;
-
-static int __init clock_setup(char* str)
-{
- if (str)
- strlcpy(clock_override, str, sizeof(clock_override));
- return 1;
-}
-__setup("clock=", clock_setup);
-
-
-/* The chosen timesource has been found to be bad.
- * Fall back to a known good timesource (the PIT)
- */
-void clock_fallback(void)
-{
- cur_timer = &timer_pit;
-}
-
-/* iterates through the list of timers, returning the first
- * one that initializes successfully.
- */
-struct timer_opts* __init select_timer(void)
-{
- int i = 0;
-
- /* find most preferred working timer */
- while (timers[i]) {
- if (timers[i]->init)
- if (timers[i]->init(clock_override) == 0)
- return timers[i]->opts;
- ++i;
- }
-
- panic("select_timer: Cannot find a suitable timer\n");
- return NULL;
-}
-
-int read_current_timer(unsigned long *timer_val)
-{
- if (cur_timer->read_timer) {
- *timer_val = cur_timer->read_timer();
- return 0;
- }
- return -1;
-}
diff --git a/arch/i386/kernel/timers/timer_cyclone.c b/arch/i386/kernel/timers/timer_cyclone.c
deleted file mode 100644
index 13892a6..0000000
--- a/arch/i386/kernel/timers/timer_cyclone.c
+++ /dev/null
@@ -1,259 +0,0 @@
-/* Cyclone-timer:
- * This code implements timer_ops for the cyclone counter found
- * on IBM x440, x360, and other Summit based systems.
- *
- * Copyright (C) 2002 IBM, John Stultz ([email protected])
- */
-
-
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/timex.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/jiffies.h>
-
-#include <asm/timer.h>
-#include <asm/io.h>
-#include <asm/pgtable.h>
-#include <asm/fixmap.h>
-#include <asm/i8253.h>
-
-#include "io_ports.h"
-
-/* Number of usecs that the last interrupt was delayed */
-static int delay_at_last_interrupt;
-
-#define CYCLONE_CBAR_ADDR 0xFEB00CD0
-#define CYCLONE_PMCC_OFFSET 0x51A0
-#define CYCLONE_MPMC_OFFSET 0x51D0
-#define CYCLONE_MPCS_OFFSET 0x51A8
-#define CYCLONE_TIMER_FREQ 100000000
-#define CYCLONE_TIMER_MASK (((u64)1<<40)-1) /* 40 bit mask */
-int use_cyclone = 0;
-
-static u32* volatile cyclone_timer; /* Cyclone MPMC0 register */
-static u32 last_cyclone_low;
-static u32 last_cyclone_high;
-static unsigned long long monotonic_base;
-static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
-
-/* helper macro to atomically read both cyclone counter registers */
-#define read_cyclone_counter(low,high) \
- do{ \
- high = cyclone_timer[1]; low = cyclone_timer[0]; \
- } while (high != cyclone_timer[1]);
-
-
-static void mark_offset_cyclone(void)
-{
- unsigned long lost, delay;
- unsigned long delta = last_cyclone_low;
- int count;
- unsigned long long this_offset, last_offset;
-
- write_seqlock(&monotonic_lock);
- last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
-
- spin_lock(&i8253_lock);
- read_cyclone_counter(last_cyclone_low,last_cyclone_high);
-
- /* read values for delay_at_last_interrupt */
- outb_p(0x00, 0x43); /* latch the count ASAP */
-
- count = inb_p(0x40); /* read the latched count */
- count |= inb(0x40) << 8;
-
- /*
- * VIA686a test code... reset the latch if count > max + 1
- * from timer_pit.c - cjb
- */
- if (count > LATCH) {
- outb_p(0x34, PIT_MODE);
- outb_p(LATCH & 0xff, PIT_CH0);
- outb(LATCH >> 8, PIT_CH0);
- count = LATCH - 1;
- }
- spin_unlock(&i8253_lock);
-
- /* lost tick compensation */
- delta = last_cyclone_low - delta;
- delta /= (CYCLONE_TIMER_FREQ/1000000);
- delta += delay_at_last_interrupt;
- lost = delta/(1000000/HZ);
- delay = delta%(1000000/HZ);
- if (lost >= 2)
- jiffies_64 += lost-1;
-
- /* update the monotonic base value */
- this_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
- monotonic_base += (this_offset - last_offset) & CYCLONE_TIMER_MASK;
- write_sequnlock(&monotonic_lock);
-
- /* calculate delay_at_last_interrupt */
- count = ((LATCH-1) - count) * TICK_SIZE;
- delay_at_last_interrupt = (count + LATCH/2) / LATCH;
-
-
- /* catch corner case where tick rollover occured
- * between cyclone and pit reads (as noted when
- * usec delta is > 90% # of usecs/tick)
- */
- if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
- jiffies_64++;
-}
-
-static unsigned long get_offset_cyclone(void)
-{
- u32 offset;
-
- if(!cyclone_timer)
- return delay_at_last_interrupt;
-
- /* Read the cyclone timer */
- offset = cyclone_timer[0];
-
- /* .. relative to previous jiffy */
- offset = offset - last_cyclone_low;
-
- /* convert cyclone ticks to microseconds */
- /* XXX slow, can we speed this up? */
- offset = offset/(CYCLONE_TIMER_FREQ/1000000);
-
- /* our adjusted time offset in microseconds */
- return delay_at_last_interrupt + offset;
-}
-
-static unsigned long long monotonic_clock_cyclone(void)
-{
- u32 now_low, now_high;
- unsigned long long last_offset, this_offset, base;
- unsigned long long ret;
- unsigned seq;
-
- /* atomically read monotonic base & last_offset */
- do {
- seq = read_seqbegin(&monotonic_lock);
- last_offset = ((unsigned long long)last_cyclone_high<<32)|last_cyclone_low;
- base = monotonic_base;
- } while (read_seqretry(&monotonic_lock, seq));
-
-
- /* Read the cyclone counter */
- read_cyclone_counter(now_low,now_high);
- this_offset = ((unsigned long long)now_high<<32)|now_low;
-
- /* convert to nanoseconds */
- ret = base + ((this_offset - last_offset)&CYCLONE_TIMER_MASK);
- return ret * (1000000000 / CYCLONE_TIMER_FREQ);
-}
-
-static int __init init_cyclone(char* override)
-{
- u32* reg;
- u32 base; /* saved cyclone base address */
- u32 pageaddr; /* page that contains cyclone_timer register */
- u32 offset; /* offset from pageaddr to cyclone_timer register */
- int i;
-
- /* check clock override */
- if (override[0] && strncmp(override,"cyclone",7))
- return -ENODEV;
-
- /*make sure we're on a summit box*/
- if(!use_cyclone) return -ENODEV;
-
- printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
-
- /* find base address */
- pageaddr = (CYCLONE_CBAR_ADDR)&PAGE_MASK;
- offset = (CYCLONE_CBAR_ADDR)&(~PAGE_MASK);
- set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
- reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
- if(!reg){
- printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
- return -ENODEV;
- }
- base = *reg;
- if(!base){
- printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
- return -ENODEV;
- }
-
- /* setup PMCC */
- pageaddr = (base + CYCLONE_PMCC_OFFSET)&PAGE_MASK;
- offset = (base + CYCLONE_PMCC_OFFSET)&(~PAGE_MASK);
- set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
- reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
- if(!reg){
- printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
- return -ENODEV;
- }
- reg[0] = 0x00000001;
-
- /* setup MPCS */
- pageaddr = (base + CYCLONE_MPCS_OFFSET)&PAGE_MASK;
- offset = (base + CYCLONE_MPCS_OFFSET)&(~PAGE_MASK);
- set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
- reg = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
- if(!reg){
- printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
- return -ENODEV;
- }
- reg[0] = 0x00000001;
-
- /* map in cyclone_timer */
- pageaddr = (base + CYCLONE_MPMC_OFFSET)&PAGE_MASK;
- offset = (base + CYCLONE_MPMC_OFFSET)&(~PAGE_MASK);
- set_fixmap_nocache(FIX_CYCLONE_TIMER, pageaddr);
- cyclone_timer = (u32*)(fix_to_virt(FIX_CYCLONE_TIMER) + offset);
- if(!cyclone_timer){
- printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
- return -ENODEV;
- }
-
- /*quick test to make sure its ticking*/
- for(i=0; i<3; i++){
- u32 old = cyclone_timer[0];
- int stall = 100;
- while(stall--) barrier();
- if(cyclone_timer[0] == old){
- printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
- cyclone_timer = 0;
- return -ENODEV;
- }
- }
-
- init_cpu_khz();
-
- /* Everything looks good! */
- return 0;
-}
-
-
-static void delay_cyclone(unsigned long loops)
-{
- unsigned long bclock, now;
- if(!cyclone_timer)
- return;
- bclock = cyclone_timer[0];
- do {
- rep_nop();
- now = cyclone_timer[0];
- } while ((now-bclock) < loops);
-}
-/************************************************************/
-
-/* cyclone timer_opts struct */
-static struct timer_opts timer_cyclone = {
- .name = "cyclone",
- .mark_offset = mark_offset_cyclone,
- .get_offset = get_offset_cyclone,
- .monotonic_clock = monotonic_clock_cyclone,
- .delay = delay_cyclone,
-};
-
-struct init_timer_opts __initdata timer_cyclone_init = {
- .init = init_cyclone,
- .opts = &timer_cyclone,
-};
diff --git a/arch/i386/kernel/timers/timer_hpet.c b/arch/i386/kernel/timers/timer_hpet.c
deleted file mode 100644
index be24272..0000000
--- a/arch/i386/kernel/timers/timer_hpet.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * This code largely moved from arch/i386/kernel/time.c.
- * See comments there for proper credits.
- */
-
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/timex.h>
-#include <linux/errno.h>
-#include <linux/string.h>
-#include <linux/jiffies.h>
-
-#include <asm/timer.h>
-#include <asm/io.h>
-#include <asm/processor.h>
-
-#include "io_ports.h"
-#include "mach_timer.h"
-#include <asm/hpet.h>
-
-static unsigned long hpet_usec_quotient __read_mostly; /* convert hpet clks to usec */
-static unsigned long tsc_hpet_quotient __read_mostly; /* convert tsc to hpet clks */
-static unsigned long hpet_last; /* hpet counter value at last tick*/
-static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
-static unsigned long last_tsc_high; /* msb 32 bits of Time Stamp Counter */
-static unsigned long long monotonic_base;
-static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
-
-/* convert from cycles(64bits) => nanoseconds (64bits)
- * basic equation:
- * ns = cycles / (freq / ns_per_sec)
- * ns = cycles * (ns_per_sec / freq)
- * ns = cycles * (10^9 / (cpu_khz * 10^3))
- * ns = cycles * (10^6 / cpu_khz)
- *
- * Then we use scaling math (suggested by [email protected]) to get:
- * ns = cycles * (10^6 * SC / cpu_khz) / SC
- * ns = cycles * cyc2ns_scale / SC
- *
- * And since SC is a constant power of two, we can convert the div
- * into a shift.
- *
- * We can use khz divisor instead of mhz to keep a better percision, since
- * cyc2ns_scale is limited to 10^6 * 2^10, which fits in 32 bits.
- * ([email protected])
- *
- * [email protected] "math is hard, lets go shopping!"
- */
-static unsigned long cyc2ns_scale;
-#define CYC2NS_SCALE_FACTOR 10 /* 2^10, carefully chosen */
-
-static inline void set_cyc2ns_scale(unsigned long cpu_khz)
-{
- cyc2ns_scale = (1000000 << CYC2NS_SCALE_FACTOR)/cpu_khz;
-}
-
-static inline unsigned long long cycles_2_ns(unsigned long long cyc)
-{
- return (cyc * cyc2ns_scale) >> CYC2NS_SCALE_FACTOR;
-}
-
-static unsigned long long monotonic_clock_hpet(void)
-{
- unsigned long long last_offset, this_offset, base;
- unsigned seq;
-
- /* atomically read monotonic base & last_offset */
- do {
- seq = read_seqbegin(&monotonic_lock);
- last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
- base = monotonic_base;
- } while (read_seqretry(&monotonic_lock, seq));
-
- /* Read the Time Stamp Counter */
- rdtscll(this_offset);
-
- /* return the value in ns */
- return base + cycles_2_ns(this_offset - last_offset);
-}
-
-static unsigned long get_offset_hpet(void)
-{
- register unsigned long eax, edx;
-
- eax = hpet_readl(HPET_COUNTER);
- eax -= hpet_last; /* hpet delta */
- eax = min(hpet_tick, eax);
- /*
- * Time offset = (hpet delta) * ( usecs per HPET clock )
- * = (hpet delta) * ( usecs per tick / HPET clocks per tick)
- * = (hpet delta) * ( hpet_usec_quotient ) / (2^32)
- *
- * Where,
- * hpet_usec_quotient = (2^32 * usecs per tick)/HPET clocks per tick
- *
- * Using a mull instead of a divl saves some cycles in critical path.
- */
- ASM_MUL64_REG(eax, edx, hpet_usec_quotient, eax);
-
- /* our adjusted time offset in microseconds */
- return edx;
-}
-
-static void mark_offset_hpet(void)
-{
- unsigned long long this_offset, last_offset;
- unsigned long offset;
-
- write_seqlock(&monotonic_lock);
- last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
- rdtsc(last_tsc_low, last_tsc_high);
-
- if (hpet_use_timer)
- offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
- else
- offset = hpet_readl(HPET_COUNTER);
- if (unlikely(((offset - hpet_last) >= (2*hpet_tick)) && (hpet_last != 0))) {
- int lost_ticks = ((offset - hpet_last) / hpet_tick) - 1;
- jiffies_64 += lost_ticks;
- }
- hpet_last = offset;
-
- /* update the monotonic base value */
- 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);
-}
-
-static void delay_hpet(unsigned long loops)
-{
- unsigned long hpet_start, hpet_end;
- unsigned long eax;
-
- /* loops is the number of cpu cycles. Convert it to hpet clocks */
- ASM_MUL64_REG(eax, loops, tsc_hpet_quotient, loops);
-
- hpet_start = hpet_readl(HPET_COUNTER);
- do {
- rep_nop();
- hpet_end = hpet_readl(HPET_COUNTER);
- } while ((hpet_end - hpet_start) < (loops));
-}
-
-static struct timer_opts timer_hpet;
-
-static int __init init_hpet(char* override)
-{
- unsigned long result, remain;
-
- /* check clock override */
- if (override[0] && strncmp(override,"hpet",4))
- return -ENODEV;
-
- if (!is_hpet_enabled())
- return -ENODEV;
-
- printk("Using HPET for gettimeofday\n");
- if (cpu_has_tsc) {
- unsigned long tsc_quotient = calibrate_tsc_hpet(&tsc_hpet_quotient);
- if (tsc_quotient) {
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000;
- ASM_DIV64_REG(cpu_khz, edx, tsc_quotient,
- eax, edx);
- printk("Detected %u.%03u MHz processor.\n",
- cpu_khz / 1000, cpu_khz % 1000);
- }
- set_cyc2ns_scale(cpu_khz);
- }
- /* set this only when cpu_has_tsc */
- timer_hpet.read_timer = read_timer_tsc;
- }
-
- /*
- * Math to calculate hpet to usec multiplier
- * Look for the comments at get_offset_hpet()
- */
- ASM_DIV64_REG(result, remain, hpet_tick, 0, KERNEL_TICK_USEC);
- if (remain > (hpet_tick >> 1))
- result++; /* rounding the result */
- hpet_usec_quotient = result;
-
- 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 */
-static struct timer_opts timer_hpet __read_mostly = {
- .name = "hpet",
- .mark_offset = mark_offset_hpet,
- .get_offset = get_offset_hpet,
- .monotonic_clock = monotonic_clock_hpet,
- .delay = delay_hpet,
- .resume = hpet_resume,
-};
-
-struct init_timer_opts __initdata timer_hpet_init = {
- .init = init_hpet,
- .opts = &timer_hpet,
-};
diff --git a/arch/i386/kernel/timers/timer_none.c b/arch/i386/kernel/timers/timer_none.c
deleted file mode 100644
index 4ea2f41..0000000
--- a/arch/i386/kernel/timers/timer_none.c
+++ /dev/null
@@ -1,39 +0,0 @@
-#include <linux/init.h>
-#include <asm/timer.h>
-
-static void mark_offset_none(void)
-{
- /* nothing needed */
-}
-
-static unsigned long get_offset_none(void)
-{
- return 0;
-}
-
-static unsigned long long monotonic_clock_none(void)
-{
- return 0;
-}
-
-static void delay_none(unsigned long loops)
-{
- int d0;
- __asm__ __volatile__(
- "\tjmp 1f\n"
- ".align 16\n"
- "1:\tjmp 2f\n"
- ".align 16\n"
- "2:\tdecl %0\n\tjns 2b"
- :"=&a" (d0)
- :"0" (loops));
-}
-
-/* none timer_opts struct */
-struct timer_opts timer_none = {
- .name = "none",
- .mark_offset = mark_offset_none,
- .get_offset = get_offset_none,
- .monotonic_clock = monotonic_clock_none,
- .delay = delay_none,
-};
diff --git a/arch/i386/kernel/timers/timer_pit.c b/arch/i386/kernel/timers/timer_pit.c
deleted file mode 100644
index 4676576..0000000
--- a/arch/i386/kernel/timers/timer_pit.c
+++ /dev/null
@@ -1,163 +0,0 @@
-/*
- * This code largely moved from arch/i386/kernel/time.c.
- * See comments there for proper credits.
- */
-
-#include <linux/spinlock.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/sysdev.h>
-#include <linux/timex.h>
-#include <asm/delay.h>
-#include <asm/mpspec.h>
-#include <asm/timer.h>
-#include <asm/smp.h>
-#include <asm/io.h>
-#include <asm/arch_hooks.h>
-#include <asm/i8253.h>
-
-#include "do_timer.h"
-#include "io_ports.h"
-
-static int count_p; /* counter in get_offset_pit() */
-
-static int __init init_pit(char* override)
-{
- /* check clock override */
- if (override[0] && strncmp(override,"pit",3))
- printk(KERN_ERR "Warning: clock= override failed. Defaulting to PIT\n");
-
- count_p = LATCH;
- return 0;
-}
-
-static void mark_offset_pit(void)
-{
- /* nothing needed */
-}
-
-static unsigned long long monotonic_clock_pit(void)
-{
- return 0;
-}
-
-static void delay_pit(unsigned long loops)
-{
- int d0;
- __asm__ __volatile__(
- "\tjmp 1f\n"
- ".align 16\n"
- "1:\tjmp 2f\n"
- ".align 16\n"
- "2:\tdecl %0\n\tjns 2b"
- :"=&a" (d0)
- :"0" (loops));
-}
-
-
-/* This function must be called with xtime_lock held.
- * It was inspired by Steve McCanne's microtime-i386 for BSD. -- jrs
- *
- * However, the pc-audio speaker driver changes the divisor so that
- * it gets interrupted rather more often - it loads 64 into the
- * counter rather than 11932! This has an adverse impact on
- * do_gettimeoffset() -- it stops working! What is also not
- * good is that the interval that our timer function gets called
- * is no longer 10.0002 ms, but 9.9767 ms. To get around this
- * would require using a different timing source. Maybe someone
- * could use the RTC - I know that this can interrupt at frequencies
- * ranging from 8192Hz to 2Hz. If I had the energy, I'd somehow fix
- * it so that at startup, the timer code in sched.c would select
- * using either the RTC or the 8253 timer. The decision would be
- * based on whether there was any other device around that needed
- * to trample on the 8253. I'd set up the RTC to interrupt at 1024 Hz,
- * and then do some jiggery to have a version of do_timer that
- * advanced the clock by 1/1024 s. Every time that reached over 1/100
- * of a second, then do all the old code. If the time was kept correct
- * then do_gettimeoffset could just return 0 - there is no low order
- * divider that can be accessed.
- *
- * Ideally, you would be able to use the RTC for the speaker driver,
- * but it appears that the speaker driver really needs interrupt more
- * often than every 120 us or so.
- *
- * Anyway, this needs more thought.... pjsg (1993-08-28)
- *
- * If you are really that interested, you should be reading
- * comp.protocols.time.ntp!
- */
-
-static unsigned long get_offset_pit(void)
-{
- int count;
- unsigned long flags;
- static unsigned long jiffies_p = 0;
-
- /*
- * cache volatile jiffies temporarily; we have xtime_lock.
- */
- unsigned long jiffies_t;
-
- spin_lock_irqsave(&i8253_lock, flags);
- /* timer count may underflow right here */
- outb_p(0x00, PIT_MODE); /* latch the count ASAP */
-
- count = inb_p(PIT_CH0); /* read the latched count */
-
- /*
- * We do this guaranteed double memory access instead of a _p
- * postfix in the previous port access. Wheee, hackady hack
- */
- jiffies_t = jiffies;
-
- count |= inb_p(PIT_CH0) << 8;
-
- /* VIA686a test code... reset the latch if count > max + 1 */
- if (count > LATCH) {
- outb_p(0x34, PIT_MODE);
- outb_p(LATCH & 0xff, PIT_CH0);
- outb(LATCH >> 8, PIT_CH0);
- count = LATCH - 1;
- }
-
- /*
- * avoiding timer inconsistencies (they are rare, but they happen)...
- * there are two kinds of problems that must be avoided here:
- * 1. the timer counter underflows
- * 2. hardware problem with the timer, not giving us continuous time,
- * the counter does small "jumps" upwards on some Pentium systems,
- * (see c't 95/10 page 335 for Neptun bug.)
- */
-
- if( jiffies_t == jiffies_p ) {
- if( count > count_p ) {
- /* the nutcase */
- count = do_timer_overflow(count);
- }
- } else
- jiffies_p = jiffies_t;
-
- count_p = count;
-
- spin_unlock_irqrestore(&i8253_lock, flags);
-
- count = ((LATCH-1) - count) * TICK_SIZE;
- count = (count + LATCH/2) / LATCH;
-
- return count;
-}
-
-
-/* tsc timer_opts struct */
-struct timer_opts timer_pit = {
- .name = "pit",
- .mark_offset = mark_offset_pit,
- .get_offset = get_offset_pit,
- .monotonic_clock = monotonic_clock_pit,
- .delay = delay_pit,
-};
-
-struct init_timer_opts __initdata timer_pit_init = {
- .init = init_pit,
- .opts = &timer_pit,
-};
diff --git a/arch/i386/kernel/timers/timer_pm.c b/arch/i386/kernel/timers/timer_pm.c
deleted file mode 100644
index 264edaa..0000000
--- a/arch/i386/kernel/timers/timer_pm.c
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * (C) Dominik Brodowski <[email protected]> 2003
- *
- * Driver to use the Power Management Timer (PMTMR) available in some
- * southbridges as primary timing source for the Linux kernel.
- *
- * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
- * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
- *
- * This file is licensed under the GPL v2.
- */
-
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/device.h>
-#include <linux/init.h>
-#include <asm/types.h>
-#include <asm/timer.h>
-#include <asm/smp.h>
-#include <asm/io.h>
-#include <asm/arch_hooks.h>
-
-#include <linux/timex.h>
-#include "mach_timer.h"
-
-/* Number of PMTMR ticks expected during calibration run */
-#define PMTMR_TICKS_PER_SEC 3579545
-#define PMTMR_EXPECTED_RATE \
- ((CALIBRATE_LATCH * (PMTMR_TICKS_PER_SEC >> 10)) / (CLOCK_TICK_RATE>>10))
-
-
-/* The I/O port the PMTMR resides at.
- * The location is detected during setup_arch(),
- * in arch/i386/acpi/boot.c */
-u32 pmtmr_ioport = 0;
-
-
-/* value of the Power timer at last timer interrupt */
-static u32 offset_tick;
-static u32 offset_delay;
-
-static unsigned long long monotonic_base;
-static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
-
-#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
-
-/*helper function to safely read acpi pm timesource*/
-static inline u32 read_pmtmr(void)
-{
- u32 v1=0,v2=0,v3=0;
- /* It has been reported that because of various broken
- * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM time
- * source is not latched, so you must read it multiple
- * times to insure a safe value is read.
- */
- do {
- v1 = inl(pmtmr_ioport);
- v2 = inl(pmtmr_ioport);
- v3 = inl(pmtmr_ioport);
- } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
- || (v3 > v1 && v3 < v2));
-
- /* mask the output to 24 bits */
- return v2 & ACPI_PM_MASK;
-}
-
-
-/*
- * Some boards have the PMTMR running way too fast. We check
- * the PMTMR rate against PIT channel 2 to catch these cases.
- */
-static int verify_pmtmr_rate(void)
-{
- u32 value1, value2;
- unsigned long count, delta;
-
- mach_prepare_counter();
- value1 = read_pmtmr();
- mach_countup(&count);
- value2 = read_pmtmr();
- delta = (value2 - value1) & ACPI_PM_MASK;
-
- /* Check that the PMTMR delta is within 5% of what we expect */
- if (delta < (PMTMR_EXPECTED_RATE * 19) / 20 ||
- delta > (PMTMR_EXPECTED_RATE * 21) / 20) {
- printk(KERN_INFO "PM-Timer running at invalid rate: %lu%% of normal - aborting.\n", 100UL * delta / PMTMR_EXPECTED_RATE);
- return -1;
- }
-
- return 0;
-}
-
-
-static int init_pmtmr(char* override)
-{
- u32 value1, value2;
- unsigned int i;
-
- if (override[0] && strncmp(override,"pmtmr",5))
- return -ENODEV;
-
- if (!pmtmr_ioport)
- return -ENODEV;
-
- /* we use the TSC for delay_pmtmr, so make sure it exists */
- if (!cpu_has_tsc)
- return -ENODEV;
-
- /* "verify" this timing source */
- value1 = read_pmtmr();
- for (i = 0; i < 10000; i++) {
- value2 = read_pmtmr();
- if (value2 == value1)
- continue;
- if (value2 > value1)
- goto pm_good;
- if ((value2 < value1) && ((value2) < 0xFFF))
- goto pm_good;
- printk(KERN_INFO "PM-Timer had inconsistent results: 0x%#x, 0x%#x - aborting.\n", value1, value2);
- return -EINVAL;
- }
- printk(KERN_INFO "PM-Timer had no reasonable result: 0x%#x - aborting.\n", value1);
- return -ENODEV;
-
-pm_good:
- if (verify_pmtmr_rate() != 0)
- return -ENODEV;
-
- init_cpu_khz();
- return 0;
-}
-
-static inline u32 cyc2us(u32 cycles)
-{
- /* The Power Management Timer ticks at 3.579545 ticks per microsecond.
- * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%]
- *
- * Even with HZ = 100, delta is at maximum 35796 ticks, so it can
- * easily be multiplied with 286 (=0x11E) without having to fear
- * u32 overflows.
- */
- cycles *= 286;
- return (cycles >> 10);
-}
-
-/*
- * this gets called during each timer interrupt
- * - Called while holding the writer xtime_lock
- */
-static void mark_offset_pmtmr(void)
-{
- u32 lost, delta, last_offset;
- static int first_run = 1;
- last_offset = offset_tick;
-
- write_seqlock(&monotonic_lock);
-
- offset_tick = read_pmtmr();
-
- /* calculate tick interval */
- delta = (offset_tick - last_offset) & ACPI_PM_MASK;
-
- /* convert to usecs */
- delta = cyc2us(delta);
-
- /* update the monotonic base value */
- monotonic_base += delta * NSEC_PER_USEC;
- write_sequnlock(&monotonic_lock);
-
- /* convert to ticks */
- delta += offset_delay;
- lost = delta / (USEC_PER_SEC / HZ);
- offset_delay = delta % (USEC_PER_SEC / HZ);
-
-
- /* compensate for lost ticks */
- if (lost >= 2)
- jiffies_64 += lost - 1;
-
- /* don't calculate delay for first run,
- or if we've got less then a tick */
- if (first_run || (lost < 1)) {
- first_run = 0;
- offset_delay = 0;
- }
-}
-
-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)
-{
- u32 last_offset, this_offset;
- unsigned long long base, ret;
- unsigned seq;
-
-
- /* atomically read monotonic base & last_offset */
- do {
- seq = read_seqbegin(&monotonic_lock);
- last_offset = offset_tick;
- base = monotonic_base;
- } while (read_seqretry(&monotonic_lock, seq));
-
- /* Read the pmtmr */
- this_offset = read_pmtmr();
-
- /* convert to nanoseconds */
- ret = (this_offset - last_offset) & ACPI_PM_MASK;
- ret = base + (cyc2us(ret) * NSEC_PER_USEC);
- return ret;
-}
-
-static void delay_pmtmr(unsigned long loops)
-{
- unsigned long bclock, now;
-
- rdtscl(bclock);
- do
- {
- rep_nop();
- rdtscl(now);
- } while ((now-bclock) < loops);
-}
-
-
-/*
- * get the offset (in microseconds) from the last call to mark_offset()
- * - Called holding a reader xtime_lock
- */
-static unsigned long get_offset_pmtmr(void)
-{
- u32 now, offset, delta = 0;
-
- offset = offset_tick;
- now = read_pmtmr();
- delta = (now - offset)&ACPI_PM_MASK;
-
- return (unsigned long) offset_delay + cyc2us(delta);
-}
-
-
-/* acpi timer_opts struct */
-static struct timer_opts timer_pmtmr = {
- .name = "pmtmr",
- .mark_offset = mark_offset_pmtmr,
- .get_offset = get_offset_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 = {
- .init = init_pmtmr,
- .opts = &timer_pmtmr,
-};
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Dominik Brodowski <[email protected]>");
-MODULE_DESCRIPTION("Power Management Timer (PMTMR) as primary timing source for x86");
diff --git a/arch/i386/kernel/timers/timer_tsc.c b/arch/i386/kernel/timers/timer_tsc.c
deleted file mode 100644
index 93ec4c9..0000000
--- a/arch/i386/kernel/timers/timer_tsc.c
+++ /dev/null
@@ -1,388 +0,0 @@
-/*
- * This code largely moved from arch/i386/kernel/time.c.
- * See comments there for proper credits.
- *
- * 2004-06-25 Jesper Juhl
- * moved mark_offset_tsc below cpufreq_delayed_get to avoid gcc 3.4
- * failing to inline.
- */
-
-#include <linux/spinlock.h>
-#include <linux/init.h>
-#include <linux/timex.h>
-#include <linux/errno.h>
-#include <linux/cpufreq.h>
-#include <linux/string.h>
-#include <linux/jiffies.h>
-
-#include <asm/timer.h>
-#include <asm/io.h>
-/* processor.h for distable_tsc flag */
-#include <asm/processor.h>
-
-#include "io_ports.h"
-#include "mach_timer.h"
-
-#include <asm/hpet.h>
-#include <asm/i8253.h>
-
-#ifdef CONFIG_HPET_TIMER
-static unsigned long hpet_usec_quotient;
-static unsigned long hpet_last;
-static struct timer_opts timer_tsc;
-#endif
-
-static int use_tsc;
-/* Number of usecs that the last interrupt was delayed */
-static int delay_at_last_interrupt;
-
-static unsigned long last_tsc_low; /* lsb 32 bits of Time Stamp Counter */
-static unsigned long last_tsc_high; /* msb 32 bits of Time Stamp Counter */
-static unsigned long long monotonic_base;
-static seqlock_t monotonic_lock = SEQLOCK_UNLOCKED;
-
-static int count2; /* counter for mark_offset_tsc() */
-
-/* Cached *multiplier* to convert TSC counts to microseconds.
- * (see the equation below).
- * Equal to 2^32 * (1 / (clocks per usec) ).
- * Initialized in time_init.
- */
-static unsigned long fast_gettimeoffset_quotient;
-
-static unsigned long get_offset_tsc(void)
-{
- register unsigned long eax, edx;
-
- /* Read the Time Stamp Counter */
-
- rdtsc(eax,edx);
-
- /* .. relative to previous jiffy (32 bits is enough) */
- eax -= last_tsc_low; /* tsc_low delta */
-
- /*
- * Time offset = (tsc_low delta) * fast_gettimeoffset_quotient
- * = (tsc_low delta) * (usecs_per_clock)
- * = (tsc_low delta) * (usecs_per_jiffy / clocks_per_jiffy)
- *
- * Using a mull instead of a divl saves up to 31 clock cycles
- * in the critical path.
- */
-
- __asm__("mull %2"
- :"=a" (eax), "=d" (edx)
- :"rm" (fast_gettimeoffset_quotient),
- "0" (eax));
-
- /* our adjusted time offset in microseconds */
- return delay_at_last_interrupt + edx;
-}
-
-static unsigned long long monotonic_clock_tsc(void)
-{
- unsigned long long last_offset, this_offset, base;
- unsigned seq;
-
- /* atomically read monotonic base & last_offset */
- do {
- seq = read_seqbegin(&monotonic_lock);
- last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
- base = monotonic_base;
- } while (read_seqretry(&monotonic_lock, seq));
-
- /* Read the Time Stamp Counter */
- rdtscll(this_offset);
-
- /* return the value in ns */
- return base + cycles_2_ns(this_offset - last_offset);
-}
-
-
-static void delay_tsc(unsigned long loops)
-{
- unsigned long bclock, now;
-
- rdtscl(bclock);
- do
- {
- rep_nop();
- rdtscl(now);
- } while ((now-bclock) < loops);
-}
-
-#ifdef CONFIG_HPET_TIMER
-static void mark_offset_tsc_hpet(void)
-{
- unsigned long long this_offset, last_offset;
- unsigned long offset, temp, hpet_current;
-
- write_seqlock(&monotonic_lock);
- last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
- /*
- * It is important that these two operations happen almost at
- * the same time. We do the RDTSC stuff first, since it's
- * faster. To avoid any inconsistencies, we need interrupts
- * disabled locally.
- */
- /*
- * Interrupts are just disabled locally since the timer irq
- * has the SA_INTERRUPT flag set. -arca
- */
- /* read Pentium cycle counter */
-
- hpet_current = hpet_readl(HPET_COUNTER);
- rdtsc(last_tsc_low, last_tsc_high);
-
- /* lost tick compensation */
- offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
- if (unlikely(((offset - hpet_last) > hpet_tick) && (hpet_last != 0))) {
- int lost_ticks = (offset - hpet_last) / hpet_tick;
- jiffies_64 += lost_ticks;
- }
- hpet_last = hpet_current;
-
- /* update the monotonic base value */
- 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);
-
- /* calculate delay_at_last_interrupt */
- /*
- * Time offset = (hpet delta) * ( usecs per HPET clock )
- * = (hpet delta) * ( usecs per tick / HPET clocks per tick)
- * = (hpet delta) * ( hpet_usec_quotient ) / (2^32)
- * Where,
- * hpet_usec_quotient = (2^32 * usecs per tick)/HPET clocks per tick
- */
- delay_at_last_interrupt = hpet_current - offset;
- ASM_MUL64_REG(temp, delay_at_last_interrupt,
- hpet_usec_quotient, delay_at_last_interrupt);
-}
-#endif
-
-
-
-static void mark_offset_tsc(void)
-{
- unsigned long lost,delay;
- unsigned long delta = last_tsc_low;
- int count;
- int countmp;
- static int count1 = 0;
- unsigned long long this_offset, last_offset;
- static int lost_count = 0;
-
- write_seqlock(&monotonic_lock);
- last_offset = ((unsigned long long)last_tsc_high<<32)|last_tsc_low;
- /*
- * It is important that these two operations happen almost at
- * the same time. We do the RDTSC stuff first, since it's
- * faster. To avoid any inconsistencies, we need interrupts
- * disabled locally.
- */
-
- /*
- * Interrupts are just disabled locally since the timer irq
- * has the SA_INTERRUPT flag set. -arca
- */
-
- /* read Pentium cycle counter */
-
- rdtsc(last_tsc_low, last_tsc_high);
-
- spin_lock(&i8253_lock);
- outb_p(0x00, PIT_MODE); /* latch the count ASAP */
-
- count = inb_p(PIT_CH0); /* read the latched count */
- count |= inb(PIT_CH0) << 8;
-
- /*
- * VIA686a test code... reset the latch if count > max + 1
- * from timer_pit.c - cjb
- */
- if (count > LATCH) {
- outb_p(0x34, PIT_MODE);
- outb_p(LATCH & 0xff, PIT_CH0);
- outb(LATCH >> 8, PIT_CH0);
- count = LATCH - 1;
- }
-
- spin_unlock(&i8253_lock);
-
- if (pit_latch_buggy) {
- /* get center value of last 3 time lutch */
- if ((count2 >= count && count >= count1)
- || (count1 >= count && count >= count2)) {
- count2 = count1; count1 = count;
- } else if ((count1 >= count2 && count2 >= count)
- || (count >= count2 && count2 >= count1)) {
- countmp = count;count = count2;
- count2 = count1;count1 = countmp;
- } else {
- count2 = count1; count1 = count; count = count1;
- }
- }
-
- /* lost tick compensation */
- delta = last_tsc_low - delta;
- {
- register unsigned long eax, edx;
- eax = delta;
- __asm__("mull %2"
- :"=a" (eax), "=d" (edx)
- :"rm" (fast_gettimeoffset_quotient),
- "0" (eax));
- delta = edx;
- }
- delta += delay_at_last_interrupt;
- lost = delta/(1000000/HZ);
- delay = delta%(1000000/HZ);
- if (lost >= 2) {
- jiffies_64 += lost-1;
-
- /* sanity check to ensure we're not always losing ticks */
- if (lost_count++ > 100) {
- printk(KERN_WARNING "Losing too many ticks!\n");
- printk(KERN_WARNING "TSC cannot be used as a timesource. \n");
- printk(KERN_WARNING "Possible reasons for this are:\n");
- printk(KERN_WARNING " You're running with Speedstep,\n");
- printk(KERN_WARNING " You don't have DMA enabled for your hard disk (see hdparm),\n");
- printk(KERN_WARNING " Incorrect TSC synchronization on an SMP system (see dmesg).\n");
- printk(KERN_WARNING "Falling back to a sane timesource now.\n");
-
- clock_fallback();
- }
- /* ... but give the TSC a fair chance */
- if (lost_count > 25)
- cpufreq_delayed_get();
- } else
- lost_count = 0;
- /* update the monotonic base value */
- 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);
-
- /* calculate delay_at_last_interrupt */
- count = ((LATCH-1) - count) * TICK_SIZE;
- delay_at_last_interrupt = (count + LATCH/2) / LATCH;
-
- /* catch corner case where tick rollover occured
- * between tsc and pit reads (as noted when
- * usec delta is > 90% # of usecs/tick)
- */
- if (lost && abs(delay - delay_at_last_interrupt) > (900000/HZ))
- jiffies_64++;
-}
-
-static int __init init_tsc(char* override)
-{
-
- /* check clock override */
- if (override[0] && strncmp(override,"tsc",3)) {
-#ifdef CONFIG_HPET_TIMER
- if (is_hpet_enabled()) {
- printk(KERN_ERR "Warning: clock= override failed. Defaulting to tsc\n");
- } else
-#endif
- {
- return -ENODEV;
- }
- }
-
- /*
- * If we have APM enabled or the CPU clock speed is variable
- * (CPU stops clock on HLT or slows clock to save power)
- * then the TSC timestamps may diverge by up to 1 jiffy from
- * 'real time' but nothing will break.
- * The most frequent case is that the CPU is "woken" from a halt
- * state by the timer interrupt itself, so we get 0 error. In the
- * rare cases where a driver would "wake" the CPU and request a
- * timestamp, the maximum error is < 1 jiffy. But timestamps are
- * still perfectly ordered.
- * Note that the TSC counter will be reset if APM suspends
- * to disk; this won't break the kernel, though, 'cuz we're
- * smart. See arch/i386/kernel/apm.c.
- */
- /*
- * Firstly we have to do a CPU check for chips with
- * a potentially buggy TSC. At this point we haven't run
- * the ident/bugs checks so we must run this hook as it
- * may turn off the TSC flag.
- *
- * NOTE: this doesn't yet handle SMP 486 machines where only
- * some CPU's have a TSC. Thats never worked and nobody has
- * moaned if you have the only one in the world - you fix it!
- */
-
- count2 = LATCH; /* initialize counter for mark_offset_tsc() */
-
- if (cpu_has_tsc) {
- unsigned long tsc_quotient;
-#ifdef CONFIG_HPET_TIMER
- if (is_hpet_enabled() && hpet_use_timer) {
- unsigned long result, remain;
- printk("Using TSC for gettimeofday\n");
- tsc_quotient = calibrate_tsc_hpet(NULL);
- timer_tsc.mark_offset = &mark_offset_tsc_hpet;
- /*
- * Math to calculate hpet to usec multiplier
- * Look for the comments at get_offset_tsc_hpet()
- */
- ASM_DIV64_REG(result, remain, hpet_tick,
- 0, KERNEL_TICK_USEC);
- if (remain > (hpet_tick >> 1))
- result++; /* rounding the result */
-
- hpet_usec_quotient = result;
- } else
-#endif
- {
- tsc_quotient = calibrate_tsc();
- }
-
- if (tsc_quotient) {
- fast_gettimeoffset_quotient = tsc_quotient;
- use_tsc = 1;
- /*
- * We could be more selective here I suspect
- * and just enable this for the next intel chips ?
- */
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000;
- __asm__("divl %2"
- :"=a" (cpu_khz), "=d" (edx)
- :"r" (tsc_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %u.%03u MHz processor.\n",
- cpu_khz / 1000, cpu_khz % 1000);
- }
- set_cyc2ns_scale(cpu_khz);
- return 0;
- }
- }
- return -ENODEV;
-}
-
-
-
-/************************************************************/
-
-/* tsc timer_opts struct */
-static struct timer_opts timer_tsc = {
- .name = "tsc",
- .mark_offset = mark_offset_tsc,
- .get_offset = get_offset_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 = {
- .init = init_tsc,
- .opts = &timer_tsc,
-};

2005-11-12 04:51:44

by john stultz

[permalink] [raw]
Subject: [PATCH 9/13] Time: i386 Conversion - part 5: Enable Generic Timekeeping

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the fifth of six, converts the i386 arch
to use the generic timeofday subsystem.

It applies on top of my timeofday-arch-i386-part4 patch. This patch is
the last in the timeofday-arch-i386 patchset, so you should be able to
build and boot a kernel after it has been applied.

Note that this patch does not provide any i386 clocksource, so you will
only have the jiffies clocksource. To get full replacements for the code
being removed here, the following timeofday-clocks-i386 patch will need
to be applied.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/i386/Kconfig | 4
arch/i386/kernel/Makefile | 1
arch/i386/kernel/time.c | 219 ++++++-------------------------------------
arch/i386/kernel/tsc.c | 12 --
arch/i386/lib/delay.c | 48 +++++++++
include/asm-i386/delay.h | 2
include/asm-i386/timeofday.h | 4
include/asm-i386/timer.h | 57 -----------
8 files changed, 92 insertions(+), 255 deletions(-)

linux-2.6.14_timeofday-arch-i386-part5_B10.patch
============================================
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index dbf90ad..8408da2 100644
--- a/arch/i386/Kconfig
+++ b/arch/i386/Kconfig
@@ -14,6 +14,10 @@ config X86_32
486, 586, Pentiums, and various instruction-set-compatible chips by
AMD, Cyrix, and others.

+config GENERIC_TIME
+ bool
+ default y
+
config SEMAPHORE_SLEEPERS
bool
default y
diff --git a/arch/i386/kernel/Makefile b/arch/i386/kernel/Makefile
index 4c4e1e5..5d1d9c9 100644
--- a/arch/i386/kernel/Makefile
+++ b/arch/i386/kernel/Makefile
@@ -10,7 +10,6 @@ obj-y := process.o semaphore.o signal.o
doublefault.o quirks.o i8237.o i8253.o tsc.o

obj-y += cpu/
-obj-y += timers/
obj-$(CONFIG_ACPI) += acpi/
obj-$(CONFIG_X86_BIOS_REBOOT) += reboot.o
obj-$(CONFIG_MCA) += mca.o
diff --git a/arch/i386/kernel/time.c b/arch/i386/kernel/time.c
index 5a9f253..3336f43 100644
--- a/arch/i386/kernel/time.c
+++ b/arch/i386/kernel/time.c
@@ -56,6 +56,7 @@
#include <asm/uaccess.h>
#include <asm/processor.h>
#include <asm/timer.h>
+#include <asm/timeofday.h>

#include "mach_time.h"

@@ -82,8 +83,6 @@ extern unsigned long wall_jiffies;
DEFINE_SPINLOCK(rtc_lock);
EXPORT_SYMBOL(rtc_lock);

-struct timer_opts *cur_timer __read_mostly = &timer_none;
-
/*
* This is a special lock that is owned by the CPU and holds the index
* register we are working with. It is required for NMI access to the
@@ -113,99 +112,19 @@ void rtc_cmos_write(unsigned char val, u
}
EXPORT_SYMBOL(rtc_cmos_write);

-/*
- * This version of gettimeofday has microsecond resolution
- * and better than microsecond precision on fast x86 machines with TSC.
- */
-void do_gettimeofday(struct timeval *tv)
-{
- unsigned long seq;
- unsigned long usec, sec;
- unsigned long max_ntp_tick;
-
- do {
- unsigned long lost;
-
- seq = read_seqbegin(&xtime_lock);
-
- usec = cur_timer->get_offset();
- lost = jiffies - wall_jiffies;
-
- /*
- * If time_adjust is negative then NTP is slowing the clock
- * so make sure not to go into next possible interval.
- * Better to lose some accuracy than have time go backwards..
- */
- if (unlikely(time_adjust < 0)) {
- max_ntp_tick = (USEC_PER_SEC / HZ) - tickadj;
- usec = min(usec, max_ntp_tick);
-
- if (lost)
- usec += lost * max_ntp_tick;
- }
- else if (unlikely(lost))
- usec += lost * (USEC_PER_SEC / HZ);
-
- sec = xtime.tv_sec;
- usec += (xtime.tv_nsec / 1000);
- } while (read_seqretry(&xtime_lock, seq));
-
- while (usec >= 1000000) {
- usec -= 1000000;
- sec++;
- }
-
- tv->tv_sec = sec;
- tv->tv_usec = usec;
-}
-
-EXPORT_SYMBOL(do_gettimeofday);
-
-int do_settimeofday(struct timespec *tv)
-{
- time_t wtm_sec, sec = tv->tv_sec;
- long wtm_nsec, nsec = tv->tv_nsec;
-
- if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
- return -EINVAL;
-
- write_seqlock_irq(&xtime_lock);
- /*
- * This is revolting. We need to set "xtime" correctly. However, the
- * value in this location is the value at the most recent update of
- * wall time. Discover what correction gettimeofday() would have
- * made, and then undo it!
- */
- nsec -= cur_timer->get_offset() * NSEC_PER_USEC;
- nsec -= (jiffies - wall_jiffies) * TICK_NSEC;
-
- wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
- wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
-
- set_normalized_timespec(&xtime, sec, nsec);
- set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
-
- ntp_clear();
- write_sequnlock_irq(&xtime_lock);
- clock_was_set();
- return 0;
-}
-
-EXPORT_SYMBOL(do_settimeofday);
-
static int set_rtc_mmss(unsigned long nowtime)
{
int retval;
-
- WARN_ON(irqs_disabled());
+ unsigned long flags;

/* gets recalled with irq locally disabled */
- spin_lock_irq(&rtc_lock);
+ /* XXX - does irqsave resolve this? -johnstul */
+ spin_lock_irqsave(&rtc_lock, flags);
if (efi_enabled)
retval = efi_set_rtc_mmss(nowtime);
else
retval = mach_set_rtc_mmss(nowtime);
- spin_unlock_irq(&rtc_lock);
+ spin_unlock_irqrestore(&rtc_lock, flags);

return retval;
}
@@ -213,16 +132,6 @@ static int set_rtc_mmss(unsigned long no

int timer_ack;

-/* monotonic_clock(): returns # of nanoseconds passed since time_init()
- * Note: This function is required to return accurate
- * time even in the absence of multiple timer ticks.
- */
-unsigned long long monotonic_clock(void)
-{
- return cur_timer->monotonic_clock();
-}
-EXPORT_SYMBOL(monotonic_clock);
-
#if defined(CONFIG_SMP) && defined(CONFIG_FRAME_POINTER)
unsigned long profile_pc(struct pt_regs *regs)
{
@@ -237,11 +146,21 @@ EXPORT_SYMBOL(profile_pc);
#endif

/*
- * timer_interrupt() needs to keep up the real-time clock,
- * as well as call the "do_timer()" routine every clocktick
+ * This is the same as the above, except we _also_ save the current
+ * Time Stamp Counter value at the time of the timer interrupt, so that
+ * we later on can estimate the time of day more exactly.
*/
-static inline void do_timer_interrupt(int irq, struct pt_regs *regs)
+irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
+ /*
+ * 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
+ * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
+ * the irq version of write_lock because as just said we have irq
+ * locally disabled. -arca
+ */
+ write_seqlock(&xtime_lock);
+
#ifdef CONFIG_X86_IO_APIC
if (timer_ack) {
/*
@@ -274,27 +193,6 @@ static inline void do_timer_interrupt(in
irq = inb_p( 0x61 ); /* read the current state */
outb_p( irq|0x80, 0x61 ); /* reset the IRQ */
}
-}
-
-/*
- * This is the same as the above, except we _also_ save the current
- * Time Stamp Counter value at the time of the timer interrupt, so that
- * we later on can estimate the time of day more exactly.
- */
-irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
-{
- /*
- * 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
- * CPU. We need to avoid to SMP race with it. NOTE: we don' t need
- * the irq version of write_lock because as just said we have irq
- * locally disabled. -arca
- */
- write_seqlock(&xtime_lock);
-
- cur_timer->mark_offset();
-
- do_timer_interrupt(irq, regs);

write_sequnlock(&xtime_lock);
return IRQ_HANDLED;
@@ -318,58 +216,37 @@ unsigned long get_cmos_time(void)
}
EXPORT_SYMBOL(get_cmos_time);

-static void sync_cmos_clock(unsigned long dummy);
-
-static DEFINE_TIMER(sync_cmos_timer, sync_cmos_clock, 0, 0);
-
-static void sync_cmos_clock(unsigned long dummy)
+/* arch specific timeofday hooks */
+nsec_t read_persistent_clock(void)
{
- struct timeval now, next;
- int fail = 1;
+ return (nsec_t)get_cmos_time() * NSEC_PER_SEC;
+}

+void sync_persistent_clock(struct timespec ts)
+{
+ static unsigned long last_rtc_update;
/*
* If we have an externally synchronized Linux clock, then update
* CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
* called as close as possible to 500 ms before the new second starts.
- * This code is run on a timer. If the clock is set, that timer
- * may not expire at the correct time. Thus, we adjust...
*/
- if (!ntp_synced())
- /*
- * Not synced, exit, do not restart a timer (if one is
- * running, let it run out).
- */
+ if (ts.tv_sec <= last_rtc_update + 660)
return;

- do_gettimeofday(&now);
- if (now.tv_usec >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 &&
- now.tv_usec <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2)
- fail = set_rtc_mmss(now.tv_sec);
-
- next.tv_usec = USEC_AFTER - now.tv_usec;
- if (next.tv_usec <= 0)
- next.tv_usec += USEC_PER_SEC;
-
- if (!fail)
- next.tv_sec = 659;
- else
- next.tv_sec = 0;
-
- if (next.tv_usec >= USEC_PER_SEC) {
- next.tv_sec++;
- next.tv_usec -= USEC_PER_SEC;
+ if((ts.tv_nsec / 1000) >= USEC_AFTER - ((unsigned) TICK_SIZE) / 2 &&
+ (ts.tv_nsec / 1000) <= USEC_BEFORE + ((unsigned) TICK_SIZE) / 2) {
+ /* horrible...FIXME */
+ if (set_rtc_mmss(ts.tv_sec) == 0)
+ last_rtc_update = ts.tv_sec;
+ else
+ last_rtc_update = ts.tv_sec - 600; /* do it again in 60 s */
}
- mod_timer(&sync_cmos_timer, jiffies + timeval_to_jiffies(&next));
}

-void notify_arch_cmos_timer(void)
-{
- mod_timer(&sync_cmos_timer, jiffies + 1);
-}
+

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)
{
/*
@@ -378,16 +255,11 @@ 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;
}

static int timer_resume(struct sys_device *dev)
{
- unsigned long flags;
unsigned long sec;
unsigned long sleep_length;

@@ -397,16 +269,8 @@ static int timer_resume(struct sys_devic
#endif
sec = get_cmos_time() + clock_cmos_diff;
sleep_length = (get_cmos_time() - sleep_start) * HZ;
- write_seqlock_irqsave(&xtime_lock, flags);
- xtime.tv_sec = sec;
- xtime.tv_nsec = 0;
- 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;
touch_softlockup_watchdog();
return 0;
}
@@ -439,17 +303,10 @@ extern void (*late_time_init)(void);
/* Duplicate of time_init() below, with hpet_enable part added */
static void __init hpet_time_init(void)
{
- xtime.tv_sec = get_cmos_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
-
if ((hpet_enable() >= 0) && hpet_use_timer) {
printk("Using HPET for base-timer\n");
}

- cur_timer = select_timer();
- printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);

time_init_hook();
}
@@ -467,13 +324,5 @@ void __init time_init(void)
return;
}
#endif
- xtime.tv_sec = get_cmos_time();
- xtime.tv_nsec = (INITIAL_JIFFIES % HZ) * (NSEC_PER_SEC / HZ);
- set_normalized_timespec(&wall_to_monotonic,
- -xtime.tv_sec, -xtime.tv_nsec);
-
- cur_timer = select_timer();
- printk(KERN_INFO "Using %s for high-res timesource\n",cur_timer->name);
-
time_init_hook();
}
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c
index de26f6e..5608c21 100644
--- a/arch/i386/kernel/tsc.c
+++ b/arch/i386/kernel/tsc.c
@@ -8,6 +8,7 @@
#include <linux/cpufreq.h>
#include <linux/jiffies.h>
#include <asm/tsc.h>
+#include <asm/delay.h>
#include <asm/io.h>
#include "mach_timer.h"

@@ -36,16 +37,6 @@ static int __init tsc_setup(char *str)
#endif
__setup("notsc", tsc_setup);

-
-int read_current_timer(unsigned long *timer_val)
-{
- if (!tsc_disable && cpu_khz) {
- rdtscl(*timer_val);
- return 0;
- }
- return -1;
-}
-
/* Code to mark and check if the TSC is unstable
* due to cpufreq or due to unsynced TSCs
*/
@@ -210,6 +201,7 @@ void tsc_init(void)
(unsigned long)cpu_khz % 1000);

set_cyc2ns_scale(cpu_khz);
+ use_tsc_delay();
}


diff --git a/arch/i386/lib/delay.c b/arch/i386/lib/delay.c
index c49a6ac..6785340 100644
--- a/arch/i386/lib/delay.c
+++ b/arch/i386/lib/delay.c
@@ -13,6 +13,7 @@
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/delay.h>
+#include <linux/timeofday.h>
#include <linux/module.h>
#include <asm/processor.h>
#include <asm/delay.h>
@@ -22,11 +23,54 @@
#include <asm/smp.h>
#endif

-extern struct timer_opts* timer;
+/* simple loop based delay */
+static void delay_loop(unsigned long loops)
+{
+ int d0;
+ __asm__ __volatile__(
+ "\tjmp 1f\n"
+ ".align 16\n"
+ "1:\tjmp 2f\n"
+ ".align 16\n"
+ "2:\tdecl %0\n\tjns 2b"
+ :"=&a" (d0)
+ :"0" (loops));
+}
+
+/* TSC based delay */
+static void delay_tsc(unsigned long loops)
+{
+ unsigned long bclock, now;
+ rdtscl(bclock);
+ do {
+ rep_nop();
+ rdtscl(now);
+ } while ((now-bclock) < loops);
+}
+
+/* Since we calibrate only once at boot, this
+ * function should be set once at boot and not changed
+ */
+static void (*delay_fn)(unsigned long) = delay_loop;
+
+void use_tsc_delay(void)
+{
+ delay_fn = delay_tsc;
+}
+
+int read_current_timer(unsigned long *timer_val)
+{
+ if (delay_fn == delay_tsc) {
+ rdtscl(*timer_val);
+ return 0;
+ }
+ return -1;
+}
+

void __delay(unsigned long loops)
{
- cur_timer->delay(loops);
+ delay_fn(loops);
}

inline void __const_udelay(unsigned long xloops)
diff --git a/include/asm-i386/delay.h b/include/asm-i386/delay.h
index 456db85..b1c7650 100644
--- a/include/asm-i386/delay.h
+++ b/include/asm-i386/delay.h
@@ -23,4 +23,6 @@ extern void __delay(unsigned long loops)
((n) > 20000 ? __bad_ndelay() : __const_udelay((n) * 5ul)) : \
__ndelay(n))

+void use_tsc_delay(void);
+
#endif /* defined(_I386_DELAY_H) */
diff --git a/include/asm-i386/timeofday.h b/include/asm-i386/timeofday.h
new file mode 100644
index 0000000..315edf9
--- /dev/null
+++ b/include/asm-i386/timeofday.h
@@ -0,0 +1,4 @@
+#ifndef _ASM_I386_TIMEOFDAY_H
+#define _ASM_I386_TIMEOFDAY_H
+#include <asm-generic/timeofday.h>
+#endif
diff --git a/include/asm-i386/timer.h b/include/asm-i386/timer.h
index aed1643..d0ebd05 100644
--- a/include/asm-i386/timer.h
+++ b/include/asm-i386/timer.h
@@ -3,68 +3,11 @@
#include <linux/init.h>
#include <linux/pm.h>

-/**
- * struct timer_ops - used to define a timer source
- *
- * @name: name of the timer.
- * @init: Probes and initializes the timer. Takes clock= override
- * string as an argument. Returns 0 on success, anything else
- * on failure.
- * @mark_offset: called by the timer interrupt.
- * @get_offset: called by gettimeofday(). Returns the number of microseconds
- * since the last timer interupt.
- * @monotonic_clock: returns the number of nanoseconds since the init of the
- * timer.
- * @delay: delays this many clock cycles.
- */
-struct timer_opts {
- char* name;
- void (*mark_offset)(void);
- unsigned long (*get_offset)(void);
- 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 {
- int (*init)(char *override);
- struct timer_opts *opts;
-};
-
#define TICK_SIZE (tick_nsec / 1000)
-
-extern struct timer_opts* __init select_timer(void);
-extern void clock_fallback(void);
void setup_pit_timer(void);
-
/* Modifiers for buggy PIT handling */
-
extern int pit_latch_buggy;
-
-extern struct timer_opts *cur_timer;
extern int timer_ack;
-
-/* list of externed timers */
-extern struct timer_opts timer_none;
-extern struct timer_opts timer_pit;
-extern struct init_timer_opts timer_pit_init;
-extern struct init_timer_opts timer_tsc_init;
-#ifdef CONFIG_X86_CYCLONE_TIMER
-extern struct init_timer_opts timer_cyclone_init;
-#endif
-
-extern unsigned long calibrate_tsc(void);
-extern unsigned long read_timer_tsc(void);
-extern void init_cpu_khz(void);
extern int recalibrate_cpu_khz(void);
-#ifdef CONFIG_HPET_TIMER
-extern struct init_timer_opts timer_hpet_init;
-extern unsigned long calibrate_tsc_hpet(unsigned long *tsc_hpet_quotient_ptr);
-#endif

-#ifdef CONFIG_X86_PM_TIMER
-extern struct init_timer_opts timer_pmtmr_init;
-#endif
#endif

2005-11-12 04:50:15

by john stultz

[permalink] [raw]
Subject: [PATCH 11/13] Time: x86-64 Conversion to Generic Timekeeping

All,
This patch converts the x86-64 arch to use the generic timeofday
infrastructure. It applies on top of my timeofday-core patch. This is a
full conversion, so most of this patch is subtractions removing the
existing arch specific time keeping code. This patch does not provide
any x86-64 clocksourcs, so using this patch alone on top of the
timeofday-core patch will only give you the jiffies clocksource. To get
full replacements for the code being removed here, the following
timeofday-clocks-i386 patch (x86-64 shares the same clocksources as
i386) will need to be applied.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/x86_64/kernel/pmtimer.c | 101 -------
b/arch/i386/kernel/acpi/boot.c | 9
b/arch/x86_64/Kconfig | 8
b/arch/x86_64/kernel/Makefile | 1
b/arch/x86_64/kernel/time.c | 527 ++++++++++++++-----------------------
b/arch/x86_64/kernel/vmlinux.lds.S | 12
b/arch/x86_64/kernel/vsyscall.c | 126 ++++++--
b/include/asm-generic/div64.h | 9
b/include/asm-x86_64/hpet.h | 3
b/include/asm-x86_64/timeofday.h | 4
b/include/asm-x86_64/timex.h | 2
b/include/asm-x86_64/vsyscall.h | 8
12 files changed, 353 insertions(+), 457 deletions(-)

linux-2.6.14_timeofday-arch-x86-64_B10.patch
============================================
diff --git a/arch/i386/kernel/acpi/boot.c b/arch/i386/kernel/acpi/boot.c
index bb9ecdc..972d66f 100644
--- a/arch/i386/kernel/acpi/boot.c
+++ b/arch/i386/kernel/acpi/boot.c
@@ -570,7 +570,7 @@ static int __init acpi_parse_sbf(unsigne
}

#ifdef CONFIG_HPET_TIMER
-
+#include <asm/hpet.h>
static int __init acpi_parse_hpet(unsigned long phys, unsigned long size)
{
struct acpi_table_hpet *hpet_tbl;
@@ -592,6 +592,7 @@ static int __init acpi_parse_hpet(unsign
#ifdef CONFIG_X86_64
vxtime.hpet_address = hpet_tbl->addr.addrl |
((long)hpet_tbl->addr.addrh << 32);
+ hpet_address = vxtime.hpet_address;

printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
hpet_tbl->id, vxtime.hpet_address);
@@ -600,10 +601,10 @@ static int __init acpi_parse_hpet(unsign
extern unsigned long hpet_address;

hpet_address = hpet_tbl->addr.addrl;
- printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
- hpet_tbl->id, hpet_address);
}
-#endif /* X86 */
+#endif /* X86 */
+ printk(KERN_INFO PREFIX "HPET id: %#x base: %#lx\n",
+ hpet_tbl->id, hpet_address);

return 0;
}
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig
index 4cce2f6..19b113e 100644
--- a/arch/x86_64/Kconfig
+++ b/arch/x86_64/Kconfig
@@ -24,6 +24,14 @@ config X86
bool
default y

+config GENERIC_TIME
+ bool
+ default y
+
+config GENERIC_TIME_VSYSCALL
+ bool
+ default y
+
config SEMAPHORE_SLEEPERS
bool
default y
diff --git a/arch/x86_64/kernel/Makefile b/arch/x86_64/kernel/Makefile
index 14328ca..0dd7b86 100644
--- a/arch/x86_64/kernel/Makefile
+++ b/arch/x86_64/kernel/Makefile
@@ -28,7 +28,6 @@ obj-$(CONFIG_EARLY_PRINTK) += early_prin
obj-$(CONFIG_GART_IOMMU) += pci-gart.o aperture.o
obj-$(CONFIG_DUMMY_IOMMU) += pci-nommu.o pci-dma.o
obj-$(CONFIG_KPROBES) += kprobes.o
-obj-$(CONFIG_X86_PM_TIMER) += pmtimer.o

obj-$(CONFIG_MODULES) += module.o

diff --git a/arch/x86_64/kernel/pmtimer.c b/arch/x86_64/kernel/pmtimer.c
deleted file mode 100644
index feb5f10..0000000
--- a/arch/x86_64/kernel/pmtimer.c
+++ /dev/null
@@ -1,101 +0,0 @@
-/* Ported over from i386 by AK, original copyright was:
- *
- * (C) Dominik Brodowski <[email protected]> 2003
- *
- * Driver to use the Power Management Timer (PMTMR) available in some
- * southbridges as primary timing source for the Linux kernel.
- *
- * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
- * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
- *
- * This file is licensed under the GPL v2.
- *
- * Dropped all the hardware bug workarounds for now. Hopefully they
- * are not needed on 64bit chipsets.
- */
-
-#include <linux/jiffies.h>
-#include <linux/kernel.h>
-#include <linux/time.h>
-#include <linux/init.h>
-#include <linux/cpumask.h>
-#include <asm/io.h>
-#include <asm/proto.h>
-#include <asm/msr.h>
-#include <asm/vsyscall.h>
-
-/* The I/O port the PMTMR resides at.
- * The location is detected during setup_arch(),
- * in arch/i386/kernel/acpi/boot.c */
-u32 pmtmr_ioport;
-
-/* value of the Power timer at last timer interrupt */
-static u32 offset_delay;
-static u32 last_pmtmr_tick;
-
-#define ACPI_PM_MASK 0xFFFFFF /* limit it to 24 bits */
-
-static inline u32 cyc2us(u32 cycles)
-{
- /* The Power Management Timer ticks at 3.579545 ticks per microsecond.
- * 1 / PM_TIMER_FREQUENCY == 0.27936511 =~ 286/1024 [error: 0.024%]
- *
- * Even with HZ = 100, delta is at maximum 35796 ticks, so it can
- * easily be multiplied with 286 (=0x11E) without having to fear
- * u32 overflows.
- */
- cycles *= 286;
- return (cycles >> 10);
-}
-
-int pmtimer_mark_offset(void)
-{
- static int first_run = 1;
- unsigned long tsc;
- u32 lost;
-
- u32 tick = inl(pmtmr_ioport);
- u32 delta;
-
- delta = cyc2us((tick - last_pmtmr_tick) & ACPI_PM_MASK);
-
- last_pmtmr_tick = tick;
- monotonic_base += delta * NSEC_PER_USEC;
-
- delta += offset_delay;
-
- lost = delta / (USEC_PER_SEC / HZ);
- offset_delay = delta % (USEC_PER_SEC / HZ);
-
- rdtscll(tsc);
- vxtime.last_tsc = tsc - offset_delay * cpu_khz;
-
- /* don't calculate delay for first run,
- or if we've got less then a tick */
- if (first_run || (lost < 1)) {
- first_run = 0;
- offset_delay = 0;
- }
-
- return lost - 1;
-}
-
-unsigned int do_gettimeoffset_pm(void)
-{
- u32 now, offset, delta = 0;
-
- offset = last_pmtmr_tick;
- now = inl(pmtmr_ioport);
- delta = (now - offset) & ACPI_PM_MASK;
-
- return offset_delay + cyc2us(delta);
-}
-
-
-static int __init nopmtimer_setup(char *s)
-{
- pmtmr_ioport = 0;
- return 0;
-}
-
-__setup("nopmtimer", nopmtimer_setup);
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 4b80b4a..3a44861 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -38,13 +38,11 @@
#include <asm/sections.h>
#include <linux/cpufreq.h>
#include <linux/hpet.h>
+#include <linux/timeofday.h>
#ifdef CONFIG_X86_LOCAL_APIC
#include <asm/apic.h>
#endif

-#ifdef CONFIG_CPU_FREQ
-static void cpufreq_delayed_get(void);
-#endif
extern void i8254_timer_resume(void);
extern int using_apic_timer;

@@ -56,7 +54,9 @@ static int notsc __initdata = 0;

#undef HPET_HACK_ENABLE_DANGEROUS

-unsigned int cpu_khz; /* TSC clocks / usec, not used here */
+unsigned int cpu_khz; /* CPU clocks / usec, not used here */
+unsigned int tsc_khz; /* TSC clocks / usec, not used here */
+unsigned long hpet_address;
static unsigned long hpet_period; /* fsecs / HPET clock */
unsigned long hpet_tick; /* HPET clocks / interrupt */
static int hpet_use_timer;
@@ -79,112 +79,6 @@ static inline void rdtscll_sync(unsigned
rdtscll(*tsc);
}

-static inline unsigned long fixed_hpet_tick(void)
-{
- return hpet_tick ? hpet_tick : 1;
-}
-
-/*
- * do_gettimeoffset() returns microseconds since last timer interrupt was
- * triggered by hardware. A memory read of HPET is slower than a register read
- * of TSC, but much more reliable. It's also synchronized to the timer
- * interrupt. Note that do_gettimeoffset() may return more than hpet_tick, if a
- * timer interrupt has happened already, but vxtime.trigger wasn't updated yet.
- * This is not a problem, because jiffies hasn't updated either. They are bound
- * together by xtime_lock.
- */
-
-static inline unsigned int do_gettimeoffset_tsc(void)
-{
- unsigned long t;
- unsigned long x;
- rdtscll_sync(&t);
- if (t < vxtime.last_tsc) t = vxtime.last_tsc; /* hack */
- x = ((t - vxtime.last_tsc) * vxtime.tsc_quot) >> 32;
- return x;
-}
-
-static inline unsigned int do_gettimeoffset_hpet(void)
-{
- /* cap counter read to one tick to avoid inconsistencies */
- unsigned long counter = hpet_readl(HPET_COUNTER) - vxtime.last;
- return (min(counter,hpet_tick) * vxtime.quot) >> 32;
-}
-
-unsigned int (*do_gettimeoffset)(void) = do_gettimeoffset_tsc;
-
-/*
- * This version of gettimeofday() has microsecond resolution and better than
- * microsecond precision, as we're using at least a 10 MHz (usually 14.31818
- * MHz) HPET timer.
- */
-
-void do_gettimeofday(struct timeval *tv)
-{
- unsigned long seq, t;
- unsigned int sec, usec;
-
- do {
- seq = read_seqbegin(&xtime_lock);
-
- sec = xtime.tv_sec;
- usec = xtime.tv_nsec / 1000;
-
- /* i386 does some correction here to keep the clock
- monotonous even when ntpd is fixing drift.
- But they didn't work for me, there is a non monotonic
- clock anyways with ntp.
- I dropped all corrections now until a real solution can
- be found. Note when you fix it here you need to do the same
- in arch/x86_64/kernel/vsyscall.c and export all needed
- variables in vmlinux.lds. -AK */
-
- t = (jiffies - wall_jiffies) * (1000000L / HZ) +
- do_gettimeoffset();
- usec += t;
-
- } while (read_seqretry(&xtime_lock, seq));
-
- tv->tv_sec = sec + usec / 1000000;
- tv->tv_usec = usec % 1000000;
-}
-
-EXPORT_SYMBOL(do_gettimeofday);
-
-/*
- * settimeofday() first undoes the correction that gettimeofday would do
- * on the time, and then saves it. This is ugly, but has been like this for
- * ages already.
- */
-
-int do_settimeofday(struct timespec *tv)
-{
- time_t wtm_sec, sec = tv->tv_sec;
- long wtm_nsec, nsec = tv->tv_nsec;
-
- if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
- return -EINVAL;
-
- write_seqlock_irq(&xtime_lock);
-
- nsec -= do_gettimeoffset() * 1000 +
- (jiffies - wall_jiffies) * (NSEC_PER_SEC/HZ);
-
- wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
- wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
-
- set_normalized_timespec(&xtime, sec, nsec);
- set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
-
- ntp_clear();
-
- write_sequnlock_irq(&xtime_lock);
- clock_was_set();
- return 0;
-}
-
-EXPORT_SYMBOL(do_settimeofday);
-
unsigned long profile_pc(struct pt_regs *regs)
{
unsigned long pc = instruction_pointer(regs);
@@ -284,90 +178,8 @@ static void set_rtc_mmss(unsigned long n
spin_unlock(&rtc_lock);
}

-
-/* monotonic_clock(): returns # of nanoseconds passed since time_init()
- * Note: This function is required to return accurate
- * time even in the absence of multiple timer ticks.
- */
-unsigned long long monotonic_clock(void)
-{
- unsigned long seq;
- u32 last_offset, this_offset, offset;
- unsigned long long base;
-
- if (vxtime.mode == VXTIME_HPET) {
- do {
- seq = read_seqbegin(&xtime_lock);
-
- last_offset = vxtime.last;
- base = monotonic_base;
- this_offset = hpet_readl(HPET_COUNTER);
-
- } while (read_seqretry(&xtime_lock, seq));
- offset = (this_offset - last_offset);
- offset *=(NSEC_PER_SEC/HZ)/fixed_hpet_tick();
- return base + offset;
- }else{
- do {
- seq = read_seqbegin(&xtime_lock);
-
- last_offset = vxtime.last_tsc;
- base = monotonic_base;
- } while (read_seqretry(&xtime_lock, seq));
- sync_core();
- rdtscll(this_offset);
- offset = (this_offset - last_offset)*1000/cpu_khz;
- return base + offset;
- }
-
-
-}
-EXPORT_SYMBOL(monotonic_clock);
-
-static noinline void handle_lost_ticks(int lost, struct pt_regs *regs)
-{
- static long lost_count;
- static int warned;
-
- if (report_lost_ticks) {
- printk(KERN_WARNING "time.c: Lost %d timer "
- "tick(s)! ", lost);
- print_symbol("rip %s)\n", regs->rip);
- }
-
- if (lost_count == 1000 && !warned) {
- printk(KERN_WARNING
- "warning: many lost ticks.\n"
- KERN_WARNING "Your time source seems to be instable or "
- "some driver is hogging interupts\n");
- print_symbol("rip %s\n", regs->rip);
- if (vxtime.mode == VXTIME_TSC && vxtime.hpet_address) {
- printk(KERN_WARNING "Falling back to HPET\n");
- vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
- vxtime.mode = VXTIME_HPET;
- do_gettimeoffset = do_gettimeoffset_hpet;
- }
- /* else should fall back to PIT, but code missing. */
- warned = 1;
- } else
- lost_count++;
-
-#ifdef CONFIG_CPU_FREQ
- /* In some cases the CPU can change frequency without us noticing
- (like going into thermal throttle)
- Give cpufreq a change to catch up. */
- if ((lost_count+1) % 25 == 0) {
- cpufreq_delayed_get();
- }
-#endif
-}
-
static irqreturn_t timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- static unsigned long rtc_update = 0;
- unsigned long tsc;
- int delay, offset = 0, lost = 0;
-
/*
* Here we are in the timer irq handler. We have irqs locally disabled (so we
* don't need spin_lock_irqsave()) but we don't know if the timer_bh is running
@@ -377,67 +189,6 @@ static irqreturn_t timer_interrupt(int i

write_seqlock(&xtime_lock);

- if (vxtime.hpet_address)
- offset = hpet_readl(HPET_COUNTER);
-
- if (hpet_use_timer) {
- /* if we're using the hpet timer functionality,
- * we can more accurately know the counter value
- * when the timer interrupt occured.
- */
- offset = hpet_readl(HPET_T0_CMP) - hpet_tick;
- delay = hpet_readl(HPET_COUNTER) - offset;
- } else {
- spin_lock(&i8253_lock);
- outb_p(0x00, 0x43);
- delay = inb_p(0x40);
- delay |= inb(0x40) << 8;
- spin_unlock(&i8253_lock);
- delay = LATCH - 1 - delay;
- }
-
- rdtscll_sync(&tsc);
-
- if (vxtime.mode == VXTIME_HPET) {
- if (offset - vxtime.last > hpet_tick) {
- lost = (offset - vxtime.last) / fixed_hpet_tick() - 1;
- }
-
- monotonic_base += (offset - vxtime.last)*(NSEC_PER_SEC/HZ) /
- fixed_hpet_tick();
-
- vxtime.last = offset;
-#ifdef CONFIG_X86_PM_TIMER
- } else if (vxtime.mode == VXTIME_PMTMR) {
- lost = pmtimer_mark_offset();
-#endif
- } else {
- offset = (((tsc - vxtime.last_tsc) *
- vxtime.tsc_quot) >> 32) - (USEC_PER_SEC / HZ);
-
- if (offset < 0)
- offset = 0;
-
- if (offset > (USEC_PER_SEC / HZ)) {
- lost = offset / (USEC_PER_SEC / HZ);
- offset %= (USEC_PER_SEC / HZ);
- }
-
- monotonic_base += (tsc - vxtime.last_tsc)*1000000/cpu_khz ;
-
- vxtime.last_tsc = tsc - vxtime.quot * delay / vxtime.tsc_quot;
-
- if ((((tsc - vxtime.last_tsc) *
- vxtime.tsc_quot) >> 32) < offset)
- vxtime.last_tsc = tsc -
- (((long) offset << 32) / vxtime.tsc_quot) - 1;
- }
-
- if (lost > 0) {
- handle_lost_ticks(lost, regs);
- jiffies += lost;
- }
-
/*
* Do the timer stuff.
*/
@@ -460,20 +211,6 @@ static irqreturn_t timer_interrupt(int i
smp_local_timer_interrupt(regs);
#endif

-/*
- * If we have an externally synchronized Linux clock, then update CMOS clock
- * accordingly every ~11 minutes. set_rtc_mmss() will be called in the jiffy
- * closest to exactly 500 ms before the next second. If the update fails, we
- * don't care, as it'll be updated on the next turn, and the problem (time way
- * off) isn't likely to go away much sooner anyway.
- */
-
- if (ntp_synced() && xtime.tv_sec > rtc_update &&
- abs(xtime.tv_nsec - 500000000) <= tick_nsec / 2) {
- set_rtc_mmss(xtime.tv_sec);
- rtc_update = xtime.tv_sec + 660;
- }
-
write_sequnlock(&xtime_lock);

return IRQ_HANDLED;
@@ -514,6 +251,29 @@ unsigned long long sched_clock(void)
return cycles_2_ns(a);
}

+/* Code to compensate for TSC C3 stalls */
+static u64 tsc_c3_offset;
+static int tsc_unstable;
+static inline int check_tsc_unstable(void)
+{
+ return tsc_unstable;
+}
+static inline void mark_tsc_unstable(void)
+{
+ tsc_unstable = 1;;
+}
+
+void tsc_c3_compensate(unsigned long nsecs)
+{
+ u64 cycles = ((u64)nsecs * tsc_khz)/1000000;
+ tsc_c3_offset += cycles;
+}
+
+static inline u64 tsc_read_c3_time(void)
+{
+ return tsc_c3_offset;
+}
+
unsigned long get_cmos_time(void)
{
unsigned int timeout, year, mon, day, hour, min, sec;
@@ -574,6 +334,30 @@ unsigned long get_cmos_time(void)
return mktime(year, mon, day, hour, min, sec);
}

+/* arch specific timeofday hooks */
+u64 read_persistent_clock(void)
+{
+ return (u64)get_cmos_time() * NSEC_PER_SEC;
+}
+
+void sync_persistent_clock(struct timespec ts)
+{
+ static unsigned long rtc_update = 0;
+ /*
+ * If we have an externally synchronized Linux clock, then update
+ * CMOS clock accordingly every ~11 minutes. set_rtc_mmss() will
+ * be called in the jiffy closest to exactly 500 ms before the
+ * next second. If the update fails, we don't care, as it'll be
+ * updated on the next turn, and the problem (time way off) isn't
+ * likely to go away much sooner anyway.
+ */
+ if (ts.tv_sec > rtc_update &&
+ abs(ts.tv_nsec - 500000000) <= tick_nsec / 2) {
+ set_rtc_mmss(xtime.tv_sec);
+ rtc_update = xtime.tv_sec + 660;
+ }
+}
+
#ifdef CONFIG_CPU_FREQ

/* Frequency scaling support. Adjust the TSC based timer when the cpu frequency
@@ -601,23 +385,6 @@ static void handle_cpufreq_delayed_get(v
cpufreq_delayed_issched = 0;
}

-/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
- * to verify the CPU frequency the timing core thinks the CPU is running
- * at is still correct.
- */
-static void cpufreq_delayed_get(void)
-{
- static int warned;
- if (cpufreq_init && !cpufreq_delayed_issched) {
- cpufreq_delayed_issched = 1;
- if (!warned) {
- warned = 1;
- printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");
- }
- schedule_work(&cpufreq_delayed_get_work);
- }
-}
-
static unsigned int ref_freq = 0;
static unsigned long loops_per_jiffy_ref = 0;

@@ -652,8 +419,11 @@ static int time_cpufreq_notifier(struct
cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);

cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
- if (!(freq->flags & CPUFREQ_CONST_LOOPS))
+ if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
vxtime.tsc_quot = (1000L << 32) / cpu_khz;
+ tsc_khz = cpu_khz;
+ }
+
}

set_cyc2ns_scale(cpu_khz_ref);
@@ -917,18 +687,12 @@ void __init time_init(void)
if (hpet_use_timer) {
cpu_khz = hpet_calibrate_tsc();
timename = "HPET";
-#ifdef CONFIG_X86_PM_TIMER
- } else if (pmtmr_ioport) {
- vxtime_hz = PM_TIMER_FREQUENCY;
- timename = "PM";
- pit_init();
- cpu_khz = pit_calibrate_tsc();
-#endif
} else {
pit_init();
cpu_khz = pit_calibrate_tsc();
timename = "PIT";
}
+ tsc_khz = cpu_khz;

printk(KERN_INFO "time.c: Using %ld.%06ld MHz %s timer.\n",
vxtime_hz / 1000000, vxtime_hz % 1000000, timename);
@@ -970,31 +734,8 @@ static __init int unsynchronized_tsc(voi
*/
void __init time_init_gtod(void)
{
- char *timetype;
-
if (unsynchronized_tsc())
- notsc = 1;
- if (vxtime.hpet_address && notsc) {
- timetype = hpet_use_timer ? "HPET" : "PIT/HPET";
- vxtime.last = hpet_readl(HPET_T0_CMP) - hpet_tick;
- vxtime.mode = VXTIME_HPET;
- do_gettimeoffset = do_gettimeoffset_hpet;
-#ifdef CONFIG_X86_PM_TIMER
- /* Using PM for gettimeofday is quite slow, but we have no other
- choice because the TSC is too unreliable on some systems. */
- } else if (pmtmr_ioport && !vxtime.hpet_address && notsc) {
- timetype = "PM";
- do_gettimeoffset = do_gettimeoffset_pm;
- vxtime.mode = VXTIME_PMTMR;
- sysctl_vsyscall = 0;
- printk(KERN_INFO "Disabling vsyscall due to use of PM timer\n");
-#endif
- } else {
- timetype = hpet_use_timer ? "HPET/TSC" : "PIT/TSC";
- vxtime.mode = VXTIME_TSC;
- }
-
- printk(KERN_INFO "time.c: Using %s based timekeeping.\n", timetype);
+ mark_tsc_unstable();
}

__setup("report_lost_ticks", time_setup);
@@ -1017,7 +758,6 @@ static int timer_suspend(struct sys_devi

static int timer_resume(struct sys_device *dev)
{
- unsigned long flags;
unsigned long sec;
unsigned long ctime = get_cmos_time();
unsigned long sleep_length = (ctime - sleep_start) * HZ;
@@ -1028,10 +768,6 @@ static int timer_resume(struct sys_devic
i8254_timer_resume();

sec = ctime + clock_cmos_diff;
- write_seqlock_irqsave(&xtime_lock,flags);
- xtime.tv_sec = sec;
- xtime.tv_nsec = 0;
- write_sequnlock_irqrestore(&xtime_lock,flags);
jiffies += sleep_length;
wall_jiffies += sleep_length;
touch_softlockup_watchdog();
@@ -1308,3 +1044,152 @@ static int __init notsc_setup(char *s)
__setup("notsc", notsc_setup);


+/* Clock source code */
+#include <linux/clocksource.h>
+#include <asm/vsyscall.h>
+
+static unsigned long current_tsc_khz = 0;
+static int tsc_update_callback(void);
+
+static cycle_t read_tsc(void)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret;
+}
+
+static cycle_t __vsyscall_fn vread_tsc(void* unused)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret;
+}
+
+static cycle_t read_tsc_c3(void)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret + tsc_read_c3_time();
+}
+
+static struct clocksource clocksource_tsc = {
+ .name = "tsc",
+ .rating = 300,
+ .read = read_tsc,
+ .vread = vread_tsc,
+ .mask = (cycle_t)-1,
+ .mult = 0, /* to be set */
+ .shift = 22,
+ .update_callback = tsc_update_callback,
+ .is_continuous = 1,
+};
+
+
+static int tsc_update_callback(void)
+{
+ int change = 0;
+ /* check to see if we should switch to the safe clocksource */
+ if (tsc_read_c3_time() &&
+ strncmp(clocksource_tsc.name, "c3tsc", 5)) {
+ printk("Falling back to C3 safe TSC\n");
+ clocksource_tsc.read = read_tsc_c3;
+ clocksource_tsc.vread = 0;
+ clocksource_tsc.name = "c3tsc";
+ change = 1;
+ }
+
+ if (clocksource_tsc.rating != 50 && check_tsc_unstable()) {
+ clocksource_tsc.rating = 50;
+ reselect_clocksource();
+ change = 1;
+ }
+ /* only update if tsc_khz has changed */
+ if (current_tsc_khz != tsc_khz){
+ current_tsc_khz = tsc_khz;
+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+ clocksource_tsc.shift);
+ change = 1;
+ }
+ return change;
+}
+
+static int __init init_tsc_clocksource(void)
+{
+ if (!notsc && tsc_khz) {
+ current_tsc_khz = tsc_khz;
+ clocksource_tsc.mult = clocksource_khz2mult(current_tsc_khz,
+ clocksource_tsc.shift);
+ register_clocksource(&clocksource_tsc);
+ }
+ return 0;
+}
+
+module_init(init_tsc_clocksource);
+
+
+#define HPET_MASK (0xFFFFFFFF)
+#define HPET_SHIFT 22
+
+/* FSEC = 10^-15 NSEC = 10^-9 */
+#define FSEC_PER_NSEC 1000000
+
+
+static void *hpet_ptr;
+
+static cycle_t read_hpet(void)
+{
+ return (cycle_t)readl(hpet_ptr);
+}
+
+static cycle_t __vsyscall_fn vread_hpet(void* ptr)
+{
+ return (cycle_t)readl((void *)fix_to_virt(VSYSCALL_HPET) + 0xf0);
+}
+
+struct clocksource clocksource_hpet = {
+ .name = "hpet",
+ .rating = 250,
+ .read = read_hpet,
+ .vread = vread_hpet,
+ .mask = (cycle_t)HPET_MASK,
+ .mult = 0, /* set below */
+ .shift = HPET_SHIFT,
+ .is_continuous = 1,
+};
+
+static int __init init_hpet_clocksource(void)
+{
+ unsigned long hpet_period;
+ void __iomem* hpet_base;
+ u64 tmp;
+
+ if (!hpet_address)
+ return -ENODEV;
+
+ /* calculate the hpet address */
+ hpet_base =
+ (void __iomem*)ioremap_nocache(hpet_address, HPET_MMAP_SIZE);
+ hpet_ptr = hpet_base + HPET_COUNTER;
+
+ /* calculate the frequency */
+ hpet_period = readl(hpet_base + HPET_PERIOD);
+
+
+ /* hpet period is in femto seconds per cycle
+ * so we need to convert this to ns/cyc units
+ * aproximated by mult/2^shift
+ *
+ * fsec/cyc * 1nsec/1000000fsec = nsec/cyc = mult/2^shift
+ * fsec/cyc * 1ns/1000000fsec * 2^shift = mult
+ * fsec/cyc * 2^shift * 1nsec/1000000fsec = mult
+ * (fsec/cyc << shift)/1000000 = mult
+ * (hpet_period << shift)/FSEC_PER_NSEC = mult
+ */
+ tmp = (u64)hpet_period << HPET_SHIFT;
+ do_div(tmp, FSEC_PER_NSEC);
+ clocksource_hpet.mult = (u32)tmp;
+
+ register_clocksource(&clocksource_hpet);
+ return 0;
+}
+module_init(init_hpet_clocksource);
diff --git a/arch/x86_64/kernel/vmlinux.lds.S b/arch/x86_64/kernel/vmlinux.lds.S
index 6dd642c..7559b94 100644
--- a/arch/x86_64/kernel/vmlinux.lds.S
+++ b/arch/x86_64/kernel/vmlinux.lds.S
@@ -99,6 +99,18 @@ SECTIONS
.jiffies : AT(VLOAD(.jiffies)) { *(.jiffies) }
jiffies = VVIRT(.jiffies);

+ .vsyscall_fn : AT(VLOAD(.vsyscall_fn)) { *(.vsyscall_fn) }
+ .vsyscall_data : AT(VLOAD(.vsyscall_data)) { *(.vsyscall_data) }
+
+ .vsyscall_gtod_data : AT(VLOAD(.vsyscall_gtod_data)) { *(.vsyscall_gtod_data) }
+ vsyscall_gtod_data = VVIRT(.vsyscall_gtod_data);
+
+ .vsyscall_gtod_lock : AT(VLOAD(.vsyscall_gtod_lock)) { *(.vsyscall_gtod_lock) }
+ vsyscall_gtod_lock = VVIRT(.vsyscall_gtod_lock);
+
+ .vsyscall_fn : AT(VLOAD(.vsyscall_fn)) { *(.vsyscall_fn) }
+ .vsyscall_data : AT(VLOAD(.vsyscall_data)) { *(.vsyscall_data) }
+
.vsyscall_1 ADDR(.vsyscall_0) + 1024: AT(VLOAD(.vsyscall_1)) { *(.vsyscall_1) }
.vsyscall_2 ADDR(.vsyscall_0) + 2048: AT(VLOAD(.vsyscall_2)) { *(.vsyscall_2) }
.vsyscall_3 ADDR(.vsyscall_0) + 3072: AT(VLOAD(.vsyscall_3)) { *(.vsyscall_3) }
diff --git a/arch/x86_64/kernel/vsyscall.c b/arch/x86_64/kernel/vsyscall.c
index 70a0bd1..5b5d337 100644
--- a/arch/x86_64/kernel/vsyscall.c
+++ b/arch/x86_64/kernel/vsyscall.c
@@ -19,6 +19,8 @@
* want per guest time just set the kernel.vsyscall64 sysctl to 0.
*/

+#include <linux/timeofday.h>
+#include <linux/clocksource.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -40,6 +42,21 @@
int __sysctl_vsyscall __section_sysctl_vsyscall = 1;
seqlock_t __xtime_lock __section_xtime_lock = SEQLOCK_UNLOCKED;

+
+struct vsyscall_gtod_data_t {
+ struct timeval wall_time_tv;
+ struct timezone sys_tz;
+ cycle_t offset_base;
+ struct clocksource clock;
+};
+
+extern struct vsyscall_gtod_data_t vsyscall_gtod_data;
+struct vsyscall_gtod_data_t __vsyscall_gtod_data __section_vsyscall_gtod_data;
+
+extern seqlock_t vsyscall_gtod_lock;
+seqlock_t __vsyscall_gtod_lock __section_vsyscall_gtod_lock = SEQLOCK_UNLOCKED;
+
+
#include <asm/unistd.h>

static force_inline void timeval_normalize(struct timeval * tv)
@@ -53,40 +70,56 @@ static force_inline void timeval_normali
}
}

-static force_inline void do_vgettimeofday(struct timeval * tv)
+/* XXX - this is ugly. gettimeofday() has a label in it so we can't
+ call it twice.
+ */
+static force_inline int syscall_gtod(struct timeval *tv, struct timezone *tz)
{
- long sequence, t;
- unsigned long sec, usec;
-
+ int ret;
+ asm volatile("syscall"
+ : "=a" (ret)
+ : "0" (__NR_gettimeofday),"D" (tv),"S" (tz) : __syscall_clobber );
+ return ret;
+}
+static force_inline void do_vgettimeofday(struct timeval* tv)
+{
+ cycle_t now, cycle_delta;
+ nsec_t nsec_delta;
+ unsigned long seq;
do {
- sequence = read_seqbegin(&__xtime_lock);
-
- sec = __xtime.tv_sec;
- usec = (__xtime.tv_nsec / 1000) +
- (__jiffies - __wall_jiffies) * (1000000 / HZ);
-
- if (__vxtime.mode != VXTIME_HPET) {
- sync_core();
- rdtscll(t);
- if (t < __vxtime.last_tsc)
- t = __vxtime.last_tsc;
- usec += ((t - __vxtime.last_tsc) *
- __vxtime.tsc_quot) >> 32;
- /* See comment in x86_64 do_gettimeofday. */
- } else {
- usec += ((readl((void *)fix_to_virt(VSYSCALL_HPET) + 0xf0) -
- __vxtime.last) * __vxtime.quot) >> 32;
+ seq = read_seqbegin(&__vsyscall_gtod_lock);
+
+ if (!__vsyscall_gtod_data.clock.vread) {
+ syscall_gtod(tv, NULL);
+ return;
}
- } while (read_seqretry(&__xtime_lock, sequence));

- tv->tv_sec = sec + usec / 1000000;
- tv->tv_usec = usec % 1000000;
+ /* read the timeosurce and calc cycle_delta */
+ now = __vsyscall_gtod_data.clock.vread(
+ __vsyscall_gtod_data.clock.vdata);
+
+ cycle_delta = (now - __vsyscall_gtod_data.offset_base)
+ & __vsyscall_gtod_data.clock.mask;
+
+ /* convert cycles to nsecs */
+ nsec_delta = cycle_delta * __vsyscall_gtod_data.clock.mult;
+ nsec_delta = nsec_delta >> __vsyscall_gtod_data.clock.shift;
+
+ /* add nsec offset to wall_time_tv */
+ *tv = __vsyscall_gtod_data.wall_time_tv;
+ do_div(nsec_delta, NSEC_PER_USEC);
+ tv->tv_usec += (unsigned long) nsec_delta;
+ while (tv->tv_usec > USEC_PER_SEC) {
+ tv->tv_sec += 1;
+ tv->tv_usec -= USEC_PER_SEC;
+ }
+ } while (read_seqretry(&__vsyscall_gtod_lock, seq));
}

/* RED-PEN may want to readd seq locking, but then the variable should be write-once. */
static force_inline void do_get_tz(struct timezone * tz)
{
- *tz = __sys_tz;
+ *tz = __vsyscall_gtod_data.sys_tz;
}

static force_inline int gettimeofday(struct timeval *tv, struct timezone *tz)
@@ -122,11 +155,13 @@ int __vsyscall(0) vgettimeofday(struct t
* unlikely */
time_t __vsyscall(1) vtime(time_t *t)
{
+ struct timeval tv;
if (unlikely(!__sysctl_vsyscall))
return time_syscall(t);
- else if (t)
- *t = __xtime.tv_sec;
- return __xtime.tv_sec;
+ vgettimeofday(&tv, 0);
+ if (t)
+ *t = tv.tv_sec;
+ return tv.tv_sec;
}

long __vsyscall(2) venosys_0(void)
@@ -139,6 +174,40 @@ long __vsyscall(3) venosys_1(void)
return -ENOSYS;
}

+struct clocksource* curr_clock;
+
+void arch_update_vsyscall_gtod(struct timespec wall_time, cycle_t offset_base,
+ struct clocksource *clock, int ntp_adj)
+{
+ unsigned long flags;
+
+ write_seqlock_irqsave(&vsyscall_gtod_lock, flags);
+
+ /* XXX - hackitty hack hack. this is terrible! */
+ if (curr_clock != clock) {
+ curr_clock = clock;
+ }
+
+ /* save off wall time as timeval */
+ vsyscall_gtod_data.wall_time_tv.tv_sec = wall_time.tv_sec;
+ vsyscall_gtod_data.wall_time_tv.tv_usec = wall_time.tv_nsec/1000;
+
+ /* save offset_base */
+ vsyscall_gtod_data.offset_base = offset_base;
+
+ /* copy current clocksource */
+ vsyscall_gtod_data.clock = *clock;
+
+ /* apply ntp adjustment to clocksource mult */
+ vsyscall_gtod_data.clock.mult += ntp_adj;
+
+ /* save off current timezone */
+ vsyscall_gtod_data.sys_tz = sys_tz;
+
+ write_sequnlock_irqrestore(&vsyscall_gtod_lock, flags);
+
+}
+
#ifdef CONFIG_SYSCTL

#define SYSCALL 0x050f
@@ -217,6 +286,7 @@ static int __init vsyscall_init(void)
BUG_ON((unsigned long) &vtime != VSYSCALL_ADDR(__NR_vtime));
BUG_ON((VSYSCALL_ADDR(0) != __fix_to_virt(VSYSCALL_FIRST_PAGE)));
map_vsyscall();
+ sysctl_vsyscall = 1;
#ifdef CONFIG_SYSCTL
register_sysctl_table(kernel_root_table2, 0);
#endif
diff --git a/include/asm-generic/div64.h b/include/asm-generic/div64.h
index 8f4e319..910a0fe 100644
--- a/include/asm-generic/div64.h
+++ b/include/asm-generic/div64.h
@@ -55,4 +55,13 @@ extern uint32_t __div64_32(uint64_t *div

#endif /* BITS_PER_LONG */

+#ifndef div_long_long_rem
+#define div_long_long_rem(dividend,divisor,remainder) \
+({ \
+ u64 result = dividend; \
+ *remainder = do_div(result,divisor); \
+ result; \
+})
+#endif
+
#endif /* _ASM_GENERIC_DIV64_H */
diff --git a/include/asm-x86_64/hpet.h b/include/asm-x86_64/hpet.h
index a3877f5..c8420bb 100644
--- a/include/asm-x86_64/hpet.h
+++ b/include/asm-x86_64/hpet.h
@@ -1,6 +1,6 @@
#ifndef _ASM_X8664_HPET_H
#define _ASM_X8664_HPET_H 1
-
+#include <asm/fixmap.h>
/*
* Documentation on HPET can be found at:
* http://www.intel.com/ial/home/sp/pcmmspec.htm
@@ -44,6 +44,7 @@
#define HPET_TN_SETVAL 0x040
#define HPET_TN_32BIT 0x100

+extern unsigned long hpet_address; /* hpet memory map physical address */
extern int is_hpet_enabled(void);
extern int hpet_rtc_timer_init(void);
extern int oem_force_hpet_timer(void);
diff --git a/include/asm-x86_64/timeofday.h b/include/asm-x86_64/timeofday.h
new file mode 100644
index 0000000..b344c0e
--- /dev/null
+++ b/include/asm-x86_64/timeofday.h
@@ -0,0 +1,4 @@
+#ifndef _ASM_X86_64_TIMEOFDAY_H
+#define _ASM_X86_64_TIMEOFDAY_H
+#include <asm-generic/timeofday.h>
+#endif
diff --git a/include/asm-x86_64/timex.h b/include/asm-x86_64/timex.h
index f971f45..a2da183 100644
--- a/include/asm-x86_64/timex.h
+++ b/include/asm-x86_64/timex.h
@@ -24,6 +24,8 @@ static inline cycles_t get_cycles (void)
}

extern unsigned int cpu_khz;
+extern unsigned int tsc_khz;
+extern void tsc_c3_compensate(unsigned long usecs);

extern int read_current_timer(unsigned long *timer_value);
#define ARCH_HAS_READ_CURRENT_TIMER 1
diff --git a/include/asm-x86_64/vsyscall.h b/include/asm-x86_64/vsyscall.h
index 438a3f5..2fe714c 100644
--- a/include/asm-x86_64/vsyscall.h
+++ b/include/asm-x86_64/vsyscall.h
@@ -14,7 +14,7 @@ enum vsyscall_num {
#define VSYSCALL_ADDR(vsyscall_nr) (VSYSCALL_START+VSYSCALL_SIZE*(vsyscall_nr))

#ifdef __KERNEL__
-
+/* XXX - All of these are unused w/ CONFIG_GENERIC_TIME and should be removed */
#define __section_vxtime __attribute__ ((unused, __section__ (".vxtime"), aligned(16)))
#define __section_wall_jiffies __attribute__ ((unused, __section__ (".wall_jiffies"), aligned(16)))
#define __section_jiffies __attribute__ ((unused, __section__ (".jiffies"), aligned(16)))
@@ -23,6 +23,12 @@ enum vsyscall_num {
#define __section_xtime __attribute__ ((unused, __section__ (".xtime"), aligned(16)))
#define __section_xtime_lock __attribute__ ((unused, __section__ (".xtime_lock"), aligned(16)))

+/* Definitions for CONFIG_GENERIC_TIME definitions */
+#define __section_vsyscall_gtod_data __attribute__ ((unused, __section__ (".vsyscall_gtod_data"),aligned(16)))
+#define __section_vsyscall_gtod_lock __attribute__ ((unused, __section__ (".vsyscall_gtod_lock"),aligned(16)))
+#define __vsyscall_fn __attribute__ ((unused,__section__(".vsyscall_fn")))
+#define __vsyscall_data __attribute__ ((unused,__section__(".vsyscall_data")))
+
#define VXTIME_TSC 1
#define VXTIME_HPET 2
#define VXTIME_PMTMR 3

2005-11-12 04:52:38

by john stultz

[permalink] [raw]
Subject: [PATCH 13/13] Time: Generic Timekeeping Paraniod Debug Patch

All,

This patch provides paranoid checking of the timekeeping code. It is
not intended to be submitted to mainline, but to allow developers and
testers using the timeofday patches to better find or rule-out potential
timekeeping problems.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/i386/Kconfig | 3 +
arch/i386/kernel/tsc.c | 41 ++++++++++++++++
arch/x86_64/Kconfig | 3 +
arch/x86_64/kernel/time.c | 49 +++++++++++++++++++
kernel/time/timeofday.c | 114 +++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 209 insertions(+), 1 deletion(-)

linux-2.6.14_timeofday-paranoid-debug_B10.patch
============================================
diff --git a/arch/i386/Kconfig b/arch/i386/Kconfig
index 8408da2..716414e 100644
--- a/arch/i386/Kconfig
+++ b/arch/i386/Kconfig
@@ -18,6 +18,9 @@ config GENERIC_TIME
bool
default y

+config PARANOID_GENERIC_TIME
+ bool "Paraniod Timekeeping Checks"
+
config SEMAPHORE_SLEEPERS
bool
default y
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c
index 3f9d1e8..73b2a03 100644
--- a/arch/i386/kernel/tsc.c
+++ b/arch/i386/kernel/tsc.c
@@ -313,6 +313,46 @@ core_initcall(cpufreq_tsc);
static unsigned long current_tsc_khz = 0;
static int tsc_update_callback(void);

+#ifdef CONFIG_PARANOID_GENERIC_TIME
+/* This will hurt performance! */
+DEFINE_SPINLOCK(checktsc_lock);
+cycle_t last_tsc;
+
+static cycle_t read_tsc(void)
+{
+ cycle_t ret;
+ unsigned long flags;
+ spin_lock_irqsave(&checktsc_lock, flags);
+
+ rdtscll(ret);
+
+ if(ret < last_tsc)
+ printk("read_tsc: ACK! TSC went backward! Unsynced TSCs?\n");
+ last_tsc = ret;
+
+ spin_unlock_irqrestore(&checktsc_lock, flags);
+ return ret;
+}
+
+static cycle_t read_tsc_c3(void)
+{
+ cycle_t ret;
+ unsigned long flags;
+ spin_lock_irqsave(&checktsc_lock, flags);
+
+ rdtscll(ret);
+ ret += tsc_read_c3_time();
+
+ if(ret < last_tsc)
+ printk("read_tsc_c3: ACK! TSC went backward! Unsynced TSCs?\n");
+ last_tsc = ret;
+
+ spin_unlock_irqrestore(&checktsc_lock, flags);
+ return ret;
+}
+
+#else /* CONFIG_PARANOID_GENERIC_TIME */
+
static cycle_t read_tsc(void)
{
cycle_t ret;
@@ -327,6 +367,7 @@ static cycle_t read_tsc_c3(void)
return ret + tsc_read_c3_time();
}

+#endif /* CONFIG_PARANOID_GENERIC_TIME */

static struct clocksource clocksource_tsc = {
.name = "tsc",
diff --git a/arch/x86_64/Kconfig b/arch/x86_64/Kconfig
index 19b113e..4d2be52 100644
--- a/arch/x86_64/Kconfig
+++ b/arch/x86_64/Kconfig
@@ -32,6 +32,9 @@ config GENERIC_TIME_VSYSCALL
bool
default y

+config PARANOID_GENERIC_TIME
+ bool "Paraniod Timekeeping Checks"
+
config SEMAPHORE_SLEEPERS
bool
default y
diff --git a/arch/x86_64/kernel/time.c b/arch/x86_64/kernel/time.c
index 3a44861..376aa80 100644
--- a/arch/x86_64/kernel/time.c
+++ b/arch/x86_64/kernel/time.c
@@ -1051,6 +1051,53 @@ __setup("notsc", notsc_setup);
static unsigned long current_tsc_khz = 0;
static int tsc_update_callback(void);

+#ifdef CONFIG_PARANOID_GENERIC_TIME
+/* This will hurt performance! */
+DEFINE_SPINLOCK(checktsc_lock);
+cycle_t last_tsc;
+
+static cycle_t read_tsc(void)
+{
+ cycle_t ret;
+ unsigned long flags;
+ spin_lock_irqsave(&checktsc_lock, flags);
+
+ rdtscll(ret);
+
+ if(ret < last_tsc)
+ printk("read_tsc: ACK! TSC went backward! Unsynced TSCs?\n");
+ last_tsc = ret;
+
+ spin_unlock_irqrestore(&checktsc_lock, flags);
+ return ret;
+}
+
+static cycle_t __vsyscall_fn vread_tsc(void* unused)
+{
+ cycle_t ret;
+ rdtscll(ret);
+ return ret;
+}
+
+static cycle_t read_tsc_c3(void)
+{
+ cycle_t ret;
+ unsigned long flags;
+ spin_lock_irqsave(&checktsc_lock, flags);
+
+ rdtscll(ret);
+ ret += tsc_read_c3_time();
+
+ if(ret < last_tsc)
+ printk("read_tsc_c3: ACK! TSC went backward! Unsynced TSCs?\n");
+ last_tsc = ret;
+
+ spin_unlock_irqrestore(&checktsc_lock, flags);
+ return ret;
+}
+
+#else /* CONFIG_PARANOID_GENERIC_TIME */
+
static cycle_t read_tsc(void)
{
cycle_t ret;
@@ -1072,6 +1119,8 @@ static cycle_t read_tsc_c3(void)
return ret + tsc_read_c3_time();
}

+#endif /* CONFIG_PARANOID_GENERIC_TIME */
+
static struct clocksource clocksource_tsc = {
.name = "tsc",
.rating = 300,
diff --git a/kernel/time/timeofday.c b/kernel/time/timeofday.c
index 42ec5fc..ce3fd87 100644
--- a/kernel/time/timeofday.c
+++ b/kernel/time/timeofday.c
@@ -120,6 +120,108 @@ static nsec_t suspend_start;
*/
struct ktimer timeofday_timer;

+
+#ifdef CONFIG_PARANOID_GENERIC_TIME
+/* This will hurt performance! */
+DEFINE_SPINLOCK(check_monotonic_lock);
+ktime_t last_monotonic_ktime;
+
+ktime_t get_check_value(void)
+{
+ ktime_t ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&check_monotonic_lock, flags);
+ ret = last_monotonic_ktime;
+ spin_unlock_irqrestore(&check_monotonic_lock, flags);
+ return ret;
+}
+
+void check_monotonic_clock(ktime_t prev, ktime_t now)
+{
+ unsigned long flags;
+
+ /* check for monotonic inconsistencies */
+ if(ktime_cmp(now, <, prev)) {
+ static int warn_count = 10;
+ if (warn_count > 0) {
+ warn_count--;
+ printk("check_monotonic_clock: monotonic inconsistency"
+ " detected!\n");
+ printk(" from %16Lx (%llu) to %16Lx (%llu).\n",
+ ktime_to_ns(prev),
+ ktime_to_ns(prev),
+ ktime_to_ns(now),
+ ktime_to_ns(now));
+ WARN_ON(1);
+ }
+ }
+ spin_lock_irqsave(&check_monotonic_lock, flags);
+ last_monotonic_ktime = now;
+ spin_unlock_irqrestore(&check_monotonic_lock, flags);
+}
+/* timespec version */
+#define check_monotonic_clock_ts(prev, now) \
+ check_monotonic_clock(prev, timespec_to_ktime(now))
+
+/* Call holding atleast a readlock on system_time_lock */
+void verify_timekeeping_state(void)
+{
+ static int warn_count = 10;
+ if (warn_count <= 0)
+ return;
+ /* insure all the timespec and ktime values are consistent */
+ if (ktime_cmp(system_time, !=, timespec_to_ktime(mono_time_ts))) {
+ printk("verify_timekeeping_state: system_time != mono_time_ts\n");
+ warn_count--;
+ }
+
+ if (ktime_cmp(ktime_add(system_time, wall_time_offset), !=,
+ timespec_to_ktime(wall_time_ts))) {
+ printk("verify_timekeeping_state: system_time + wall_time_offset "
+ "!= wall_time_ts\n");
+ warn_count--;
+ }
+
+ if (ktime_cmp(wall_time_offset, !=,
+ timespec_to_ktime(monotonic_time_offset_ts))) {
+ printk("verify_timekeeping_state: wall_time_offset "
+ "!= monotonic_time_offset_ts\n");
+ warn_count--;
+ }
+}
+
+void check_periodic_interval(cycle_t now)
+{
+ static cycle_t last;
+ cycle_t delta;
+ nsec_t ns_offset;
+ if (last != 0 && now != 0) {
+ delta = (now - last)& clock->mask;
+
+ ns_offset = cyc2ns(clock, ntp_adj, delta);
+
+ if(ns_offset > (nsec_t)2*PERIODIC_INTERVAL_MS *1000000) {
+ static int warn_count = 10;
+ if (warn_count > 0) {
+ warn_count--;
+ printk("check_periodic_interval: Long interval! %llu.\n",
+ ns_offset);
+ printk(" Something may be blocking interrupts.\n");
+ }
+ }
+ }
+ last = now;
+}
+
+#else /* CONFIG_PARANOID_GENERIC_TIME */
+#define get_check_value(void) ktime_set(0,0) /* XXX can we optimize this out? */
+#define check_monotonic_clock(x,y) {}
+#define check_monotonic_clock_ts(x,ts)
+#define verify_timekeeping_state()
+#define check_periodic_interval(x)
+#endif /* CONFIG_PARANOID_GENERIC_TIME */
+
/**
* update_legacy_time_values - sync legacy time values
*
@@ -184,8 +286,12 @@ static inline nsec_t __get_nsec_offset(v
*/
static inline ktime_t __get_monotonic_clock(void)
{
+ ktime_t ret;
+ ktime_t check = get_check_value();
nsec_t offset = __get_nsec_offset();
- return ktime_add_ns(system_time, offset);
+ ret = ktime_add_ns(system_time, offset);
+ check_monotonic_clock(check,ret);
+ return ret;
}

/**
@@ -269,6 +375,7 @@ void get_monotonic_clock_ts(struct times
{
unsigned long seq;
nsec_t offset;
+ ktime_t check = get_check_value();

do {
seq = read_seqbegin(&system_time_lock);
@@ -278,6 +385,7 @@ void get_monotonic_clock_ts(struct times
} while (read_seqretry(&system_time_lock, seq));

timespec_add_ns(ts, offset);
+ check_monotonic_clock_ts(check, *ts);
}

/**
@@ -518,6 +626,7 @@ static void timeofday_periodic_hook(void

/* read time source & calc time since last call*/
cycle_now = read_clocksource(clock);
+ check_periodic_interval(cycle_now);
cycle_delta = (cycle_now - cycle_last) & clock->mask;

delta_nsec = cyc2ns_fixed_rem(ts_interval, &cycle_delta, &remainder);
@@ -562,6 +671,7 @@ static void timeofday_periodic_hook(void
ntp_adj = 0;
remainder = 0;
something_changed = 1;
+ check_periodic_interval(0);
}

/* now is a safe time, so allow clocksource to adjust
@@ -609,6 +719,8 @@ static void timeofday_periodic_hook(void

update_legacy_time_values();

+ verify_timekeeping_state();
+
write_sequnlock_irqrestore(&system_time_lock, flags);

/* Set us up to go off on the next interval */

2005-11-12 04:53:10

by john stultz

[permalink] [raw]
Subject: [PATCH 7/13] Time: i386 Conversion - part 3: Rework TSC Support

All,
The conversion of i386 to use the generic timeofday subsystem has been
split into 6 parts. This patch, the third of six, reworks some of the
code in the new tsc.c file, adding some new interfaces and hooks to use
these new interfaces appropriately.

It applies on top of my timeofday-arch-i386-part2 patch. This patch is
part the timeofday-arch-i386 patchset, so without the following parts it
is not expected to compile.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

arch/i386/kernel/setup.c | 1
arch/i386/kernel/tsc.c | 197 ++++++++++++++--------------
drivers/acpi/processor_idle.c | 6
include/asm-i386/mach-default/mach_timer.h | 4
include/asm-i386/mach-summit/mach_mpparse.h | 3
include/asm-i386/tsc.h | 4
6 files changed, 122 insertions(+), 93 deletions(-)

linux-2.6.14_timeofday-arch-i386-part3_B10.patch
============================================
diff --git a/arch/i386/kernel/setup.c b/arch/i386/kernel/setup.c
index fdfcb0c..28ab317 100644
--- a/arch/i386/kernel/setup.c
+++ b/arch/i386/kernel/setup.c
@@ -1620,6 +1620,7 @@ void __init setup_arch(char **cmdline_p)
conswitchp = &dummy_con;
#endif
#endif
+ tsc_init();
}

#include "setup_arch_post.h"
diff --git a/arch/i386/kernel/tsc.c b/arch/i386/kernel/tsc.c
index c562292..de26f6e 100644
--- a/arch/i386/kernel/tsc.c
+++ b/arch/i386/kernel/tsc.c
@@ -5,11 +5,18 @@
*/

#include <linux/init.h>
-#include <linux/timex.h>
#include <linux/cpufreq.h>
+#include <linux/jiffies.h>
+#include <asm/tsc.h>
#include <asm/io.h>
#include "mach_timer.h"

+/* On some systems the TSC frequency does not
+ * change with the cpu frequency. So we need
+ * an extra value to store the TSC freq
+ */
+unsigned int tsc_khz;
+
int tsc_disable __initdata = 0;
#ifndef CONFIG_X86_TSC
/* disable flag for tsc. Takes effect by clearing the TSC cpu flag
@@ -32,15 +39,46 @@ __setup("notsc", tsc_setup);

int read_current_timer(unsigned long *timer_val)
{
- if (cur_timer->read_timer) {
- *timer_val = cur_timer->read_timer();
+ if (!tsc_disable && cpu_khz) {
+ rdtscl(*timer_val);
return 0;
}
return -1;
}

+/* Code to mark and check if the TSC is unstable
+ * due to cpufreq or due to unsynced TSCs
+ */
+static int tsc_unstable;
+static inline int check_tsc_unstable(void)
+{
+ return tsc_unstable;
+}
+
+void mark_tsc_unstable(void)
+{
+ tsc_unstable = 1;
+}
+
+/* Code to compensate for C3 stalls */
+static u64 tsc_c3_offset;
+void tsc_c3_compensate(unsigned long nsecs)
+{
+ /* this could def be optimized */
+ u64 cycles = ((u64)nsecs * tsc_khz);
+ do_div(cycles, 1000000);
+ tsc_c3_offset += cycles;
+}
+
+EXPORT_SYMBOL_GPL(tsc_c3_compensate);

-/* convert from cycles(64bits) => nanoseconds (64bits)
+static inline u64 tsc_read_c3_time(void)
+{
+ return tsc_c3_offset;
+}
+
+/* Accellerators for sched_clock()
+ * convert from cycles(64bits) => nanoseconds (64bits)
* basic equation:
* ns = cycles / (freq / ns_per_sec)
* ns = cycles * (ns_per_sec / freq)
@@ -85,76 +123,54 @@ unsigned long long sched_clock(void)
* synchronized across all CPUs.
*/
#ifndef CONFIG_NUMA
- if (!use_tsc)
+ if (!cpu_khz || check_tsc_unstable())
#endif
/* no locking but a rare wrong value is not a big deal */
- return jiffies_64 * (1000000000 / HZ);
+ return (jiffies_64 - INITIAL_JIFFIES) * (1000000000 / HZ);

/* Read the Time Stamp Counter */
rdtscll(this_offset);
+ this_offset += tsc_read_c3_time();

/* return the value in ns */
return cycles_2_ns(this_offset);
}

-/* ------ Calibrate the TSC -------
- * Return 2^32 * (1 / (TSC clocks per usec)) for do_fast_gettimeoffset().
- * Too much 64-bit arithmetic here to do this cleanly in C, and for
- * accuracy's sake we want to keep the overhead on the CTC speaker (channel 2)
- * output busy loop as low as possible. We avoid reading the CTC registers
- * directly because of the awkward 8-bit access mechanism of the 82C54
- * device.
- */
-
-#define CALIBRATE_TIME (5 * 1000020/HZ)

-unsigned long calibrate_tsc(void)
+static unsigned long calculate_cpu_khz(void)
{
- mach_prepare_counter();
-
- {
- unsigned long startlow, starthigh;
- unsigned long endlow, endhigh;
- unsigned long count;
-
- rdtsc(startlow,starthigh);
+ unsigned long long start, end;
+ unsigned long count;
+ u64 delta64;
+ int i;
+ /* run 3 times to ensure the cache is warm */
+ for(i=0; i<3; i++) {
+ mach_prepare_counter();
+ rdtscll(start);
mach_countup(&count);
- rdtsc(endlow,endhigh);
-
-
- /* Error: ECTCNEVERSET */
- if (count <= 1)
- goto bad_ctc;
-
- /* 64-bit subtract - gcc just messes up with long longs */
- __asm__("subl %2,%0\n\t"
- "sbbl %3,%1"
- :"=a" (endlow), "=d" (endhigh)
- :"g" (startlow), "g" (starthigh),
- "0" (endlow), "1" (endhigh));
-
- /* Error: ECPUTOOFAST */
- if (endhigh)
- goto bad_ctc;
-
- /* Error: ECPUTOOSLOW */
- if (endlow <= CALIBRATE_TIME)
- goto bad_ctc;
-
- __asm__("divl %2"
- :"=a" (endlow), "=d" (endhigh)
- :"r" (endlow), "0" (0), "1" (CALIBRATE_TIME));
-
- return endlow;
+ rdtscll(end);
}
-
- /*
+ /* Error: ECTCNEVERSET
* The CTC wasn't reliable: we got a hit on the very first read,
* or the CPU was so fast/slow that the quotient wouldn't fit in
* 32 bits..
*/
-bad_ctc:
- return 0;
+ if (count <= 1)
+ return 0;
+
+ delta64 = end - start;
+
+ /* cpu freq too fast */
+ if(delta64 > (1ULL<<32))
+ return 0;
+ /* cpu freq too slow */
+ if (delta64 <= CALIBRATE_TIME_MSEC)
+ return 0;
+
+ delta64 += CALIBRATE_TIME_MSEC/2; /* round for do_div */
+ do_div(delta64,CALIBRATE_TIME_MSEC);
+
+ return (unsigned long)delta64;
}

int recalibrate_cpu_khz(void)
@@ -163,11 +179,11 @@ int recalibrate_cpu_khz(void)
unsigned long cpu_khz_old = cpu_khz;

if (cpu_has_tsc) {
- init_cpu_khz();
+ cpu_khz = calculate_cpu_khz();
+ tsc_khz = cpu_khz;
cpu_data[0].loops_per_jiffy =
- cpufreq_scale(cpu_data[0].loops_per_jiffy,
- cpu_khz_old,
- cpu_khz);
+ cpufreq_scale(cpu_data[0].loops_per_jiffy,
+ cpu_khz_old, cpu_khz);
return 0;
} else
return -ENODEV;
@@ -178,25 +194,22 @@ int recalibrate_cpu_khz(void)
EXPORT_SYMBOL(recalibrate_cpu_khz);


-/* calculate cpu_khz */
-void init_cpu_khz(void)
+void tsc_init(void)
{
- if (cpu_has_tsc) {
- unsigned long tsc_quotient = calibrate_tsc();
- if (tsc_quotient) {
- /* report CPU clock rate in Hz.
- * The formula is (10^6 * 2^32) / (2^32 * 1 / (clocks/us)) =
- * clock/second. Our precision is about 100 ppm.
- */
- { unsigned long eax=0, edx=1000;
- __asm__("divl %2"
- :"=a" (cpu_khz), "=d" (edx)
- :"r" (tsc_quotient),
- "0" (eax), "1" (edx));
- printk("Detected %lu.%03lu MHz processor.\n", cpu_khz / 1000, cpu_khz % 1000);
- }
- }
- }
+ if(!cpu_has_tsc || tsc_disable)
+ return;
+
+ cpu_khz = calculate_cpu_khz();
+ tsc_khz = cpu_khz;
+
+ if (!cpu_khz)
+ return;
+
+ printk("Detected %lu.%03lu MHz processor.\n",
+ (unsigned long)cpu_khz / 1000,
+ (unsigned long)cpu_khz % 1000);
+
+ set_cyc2ns_scale(cpu_khz);
}


@@ -216,15 +229,15 @@ static void handle_cpufreq_delayed_get(v
cpufreq_delayed_issched = 0;
}

-/* if we notice lost ticks, schedule a call to cpufreq_get() as it tries
+/* if we notice cpufreq oddness, schedule a call to cpufreq_get() as it tries
* to verify the CPU frequency the timing core thinks the CPU is running
* at is still correct.
*/
-void cpufreq_delayed_get(void)
+static inline void cpufreq_delayed_get(void)
{
if (cpufreq_init && !cpufreq_delayed_issched) {
cpufreq_delayed_issched = 1;
- printk(KERN_DEBUG "Losing some ticks... checking if CPU frequency changed.\n");
+ printk(KERN_DEBUG "Checking if CPU frequency changed.\n");
schedule_work(&cpufreq_delayed_get_work);
}
}
@@ -237,13 +250,11 @@ static unsigned int ref_freq = 0;
static unsigned long loops_per_jiffy_ref = 0;

#ifndef CONFIG_SMP
-static unsigned long fast_gettimeoffset_ref = 0;
static unsigned long cpu_khz_ref = 0;
#endif

-static int
-time_cpufreq_notifier(struct notifier_block *nb, unsigned long val,
- void *data)
+static int time_cpufreq_notifier(struct notifier_block *nb,
+ unsigned long val, void *data)
{
struct cpufreq_freqs *freq = data;

@@ -253,7 +264,6 @@ time_cpufreq_notifier(struct notifier_bl
ref_freq = freq->old;
loops_per_jiffy_ref = cpu_data[freq->cpu].loops_per_jiffy;
#ifndef CONFIG_SMP
- fast_gettimeoffset_ref = fast_gettimeoffset_quotient;
cpu_khz_ref = cpu_khz;
#endif
}
@@ -263,16 +273,20 @@ time_cpufreq_notifier(struct notifier_bl
(val == CPUFREQ_RESUMECHANGE)) {
if (!(freq->flags & CPUFREQ_CONST_LOOPS))
cpu_data[freq->cpu].loops_per_jiffy = cpufreq_scale(loops_per_jiffy_ref, ref_freq, freq->new);
+
+ if (cpu_khz) {
#ifndef CONFIG_SMP
- if (cpu_khz)
cpu_khz = cpufreq_scale(cpu_khz_ref, ref_freq, freq->new);
- if (use_tsc) {
+#endif
if (!(freq->flags & CPUFREQ_CONST_LOOPS)) {
- fast_gettimeoffset_quotient = cpufreq_scale(fast_gettimeoffset_ref, freq->new, ref_freq);
+ tsc_khz = cpu_khz;
set_cyc2ns_scale(cpu_khz);
+ /* TSC based sched_clock turns
+ * to junk w/ cpufreq
+ */
+ mark_tsc_unstable();
}
}
-#endif
}

if (val != CPUFREQ_RESUMECHANGE)
@@ -294,10 +308,9 @@ static int __init cpufreq_tsc(void)
CPUFREQ_TRANSITION_NOTIFIER);
if (!ret)
cpufreq_init = 1;
+
return ret;
}
core_initcall(cpufreq_tsc);

-#else /* CONFIG_CPU_FREQ */
-void cpufreq_delayed_get(void) { return; }
#endif
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 573b6a9..47e9c8d 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -181,6 +181,7 @@ static void acpi_safe_halt(void)
}

static atomic_t c3_cpu_count;
+extern void tsc_c3_compensate(unsigned long nsecs);

static void acpi_processor_idle(void)
{
@@ -354,6 +355,11 @@ static void acpi_processor_idle(void)
ACPI_MTX_DO_NOT_LOCK);
}

+#ifdef CONFIG_GENERIC_TIME
+ /* compensate for TSC pause */
+ tsc_c3_compensate((u32)(((u64)((t2-t1)&0xFFFFFF)*286070)>>10));
+#endif
+
/* Re-enable interrupts */
local_irq_enable();
/* Compute time (ticks) that we were actually asleep */
diff --git a/include/asm-i386/mach-default/mach_timer.h b/include/asm-i386/mach-default/mach_timer.h
index 4b9703b..807992f 100644
--- a/include/asm-i386/mach-default/mach_timer.h
+++ b/include/asm-i386/mach-default/mach_timer.h
@@ -15,7 +15,9 @@
#ifndef _MACH_TIMER_H
#define _MACH_TIMER_H

-#define CALIBRATE_LATCH (5 * LATCH)
+#define CALIBRATE_TIME_MSEC 30 /* 30 msecs */
+#define CALIBRATE_LATCH \
+ ((CLOCK_TICK_RATE * CALIBRATE_TIME_MSEC + 1000/2)/1000)

static inline void mach_prepare_counter(void)
{
diff --git a/include/asm-i386/mach-summit/mach_mpparse.h b/include/asm-i386/mach-summit/mach_mpparse.h
index 1cce2b9..9426839 100644
--- a/include/asm-i386/mach-summit/mach_mpparse.h
+++ b/include/asm-i386/mach-summit/mach_mpparse.h
@@ -2,6 +2,7 @@
#define __ASM_MACH_MPPARSE_H

#include <mach_apic.h>
+#include <asm/tsc.h>

extern int use_cyclone;

@@ -29,6 +30,7 @@ static inline int mps_oem_check(struct m
(!strncmp(productid, "VIGIL SMP", 9)
|| !strncmp(productid, "EXA", 3)
|| !strncmp(productid, "RUTHLESS SMP", 12))){
+ mark_tsc_unstable();
use_cyclone = 1; /*enable cyclone-timer*/
setup_summit();
return 1;
@@ -42,6 +44,7 @@ static inline int acpi_madt_oem_check(ch
if (!strncmp(oem_id, "IBM", 3) &&
(!strncmp(oem_table_id, "SERVIGIL", 8)
|| !strncmp(oem_table_id, "EXA", 3))){
+ mark_tsc_unstable();
use_cyclone = 1; /*enable cyclone-timer*/
setup_summit();
return 1;
diff --git a/include/asm-i386/tsc.h b/include/asm-i386/tsc.h
index 86288f2..49f0112 100644
--- a/include/asm-i386/tsc.h
+++ b/include/asm-i386/tsc.h
@@ -41,4 +41,8 @@ static inline cycles_t get_cycles (void)
}

extern unsigned int cpu_khz;
+extern unsigned int tsc_khz;
+extern void tsc_init(void);
+void tsc_c3_compensate(unsigned long usecs);
+extern void mark_tsc_unstable(void);
#endif

2005-11-12 04:49:42

by john stultz

[permalink] [raw]
Subject: [PATCH 2/13] Time: Reduced NTP Rework (part 2)

All,
Here is the second of two patches which try to minimize my ntp rework
patches.

This patch further changes the interrupt time NTP code, breaking out the
leapsecond processing and introduces an accessor to a shifted ppm
adjustment value. For correctness, I've also introduced a new lock, the
ntp_lock, which protects the NTP state machine when accessing it from my
timekeeping code (which does not use the xtime_lock).

Again, this patch should not affect the existing behavior, but just
separate the logical functionality so it can be re-used by my timeofday
patches.

thanks
-john

Signed-off-by: John Stultz <[email protected]>

include/linux/timex.h | 24 +++++++++
kernel/time.c | 25 +++++----
kernel/timer.c | 133 ++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 168 insertions(+), 14 deletions(-)

linux-2.6.14_timeofday-ntp-part2_B10.patch
============================================
diff --git a/include/linux/timex.h b/include/linux/timex.h
index 04a4a8c..10d8c71 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -260,6 +260,7 @@ extern long pps_calcnt; /* calibration
extern long pps_errcnt; /* calibration errors */
extern long pps_stbcnt; /* stability limit exceeded */

+extern seqlock_t ntp_lock;
/**
* ntp_clear - Clears the NTP state variables
*
@@ -267,10 +268,14 @@ extern long pps_stbcnt; /* stability li
*/
static inline void ntp_clear(void)
{
+ unsigned long flags;
+ write_seqlock_irqsave(&ntp_lock, flags);
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
+ write_sequnlock_irqrestore(&ntp_lock, flags);
+
}

/**
@@ -282,6 +287,25 @@ static inline int ntp_synced(void)
return !(time_status & STA_UNSYNC);
}

+/**
+ * ntp_get_ppm_adjustment - Returns Shifted PPM adjustment
+ *
+ */
+long ntp_get_ppm_adjustment(void);
+
+/**
+ * ntp_advance - Advances the NTP state machine by interval_ns
+ *
+ */
+void ntp_advance(unsigned long interval_ns);
+
+/**
+ * ntp_leapsecond - NTP leapsecond processing code.
+ *
+ */
+int ntp_leapsecond(struct timespec now);
+
+
/* Required to safely shift negative values */
#define shift_right(x, s) ({ \
__typeof__(x) __x = (x); \
diff --git a/kernel/time.c b/kernel/time.c
index 099dee8..c589b71 100644
--- a/kernel/time.c
+++ b/kernel/time.c
@@ -234,7 +234,9 @@ int do_adjtimex(struct timex *txc)
{
long ltemp, mtemp, save_adjust;
int result;
-
+ unsigned long flags;
+ struct timespec now_ts;
+ unsigned long seq;
/* In order to modify anything, you gotta be super-user! */
if (txc->modes && !capable(CAP_SYS_TIME))
return -EPERM;
@@ -257,7 +259,13 @@ int do_adjtimex(struct timex *txc)
txc->tick > 1100000/USER_HZ)
return -EINVAL;

- write_seqlock_irq(&xtime_lock);
+ do { /* save off current xtime */
+ seq = read_seqbegin(&xtime_lock);
+ now_ts = xtime;
+ } while (read_seqretry(&xtime_lock, seq));
+
+ write_seqlock_irqsave(&ntp_lock, flags);
+
result = time_state; /* mostly `TIME_OK' */

/* Save for later - semantics of adjtime is to return old value */
@@ -334,9 +342,9 @@ int do_adjtimex(struct timex *txc)
*/

if (time_status & STA_FREQHOLD || time_reftime == 0)
- time_reftime = xtime.tv_sec;
- mtemp = xtime.tv_sec - time_reftime;
- time_reftime = xtime.tv_sec;
+ time_reftime = now_ts.tv_sec;
+ mtemp = now_ts.tv_sec - time_reftime;
+ time_reftime = now_ts.tv_sec;
if (time_status & STA_FLL) {
if (mtemp >= MINSEC) {
ltemp = (time_offset / mtemp) << (SHIFT_USEC -
@@ -395,7 +403,7 @@ leave: if ((time_status & (STA_UNSYNC|ST
txc->calcnt = pps_calcnt;
txc->errcnt = pps_errcnt;
txc->stbcnt = pps_stbcnt;
- write_sequnlock_irq(&xtime_lock);
+ write_sequnlock_irqrestore(&ntp_lock, flags);
do_gettimeofday(&txc->time);
notify_arch_cmos_timer();
return(result);
@@ -512,10 +520,7 @@ int do_settimeofday (struct timespec *tv
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);

- time_adjust = 0; /* stop active adjtime() */
- time_status |= STA_UNSYNC;
- time_maxerror = NTP_PHASE_LIMIT;
- time_esterror = NTP_PHASE_LIMIT;
+ ntp_clear();
time_interpolator_reset();
}
write_sequnlock_irq(&xtime_lock);
diff --git a/kernel/timer.c b/kernel/timer.c
index 30a1979..f0c986b 100644
--- a/kernel/timer.c
+++ b/kernel/timer.c
@@ -582,7 +582,6 @@ long time_tolerance = MAXFREQ; /* frequ
long time_precision = 1; /* clock precision (us) */
long time_maxerror = NTP_PHASE_LIMIT; /* maximum error (us) */
long time_esterror = NTP_PHASE_LIMIT; /* estimated error (us) */
-static long time_phase; /* phase offset (scaled us) */
long time_freq = (((NSEC_PER_SEC + HZ/2) % HZ - HZ/2) << SHIFT_USEC) / NSEC_PER_USEC;
/* frequency offset (scaled ppm)*/
static long time_adj; /* tick adjust (scaled 1 / HZ) */
@@ -591,6 +590,87 @@ long time_adjust;
long time_next_adjust;
long time_adjust_step; /* per tick time_adjust step */

+long total_sppm; /* shifted ppm sum of all NTP adjustments */
+long offset_adj_ppm;
+long tick_adj_ppm;
+long singleshot_adj_ppm;
+
+#define MAX_SINGLESHOT_ADJ 500 /* (ppm) */
+#define SEC_PER_DAY 86400
+#define END_OF_DAY(x) (x + SEC_PER_DAY - (x % SEC_PER_DAY) - 1)
+
+/* NTP lock, protects NTP state machine */
+seqlock_t ntp_lock = SEQLOCK_UNLOCKED;
+
+/**
+ * ntp_leapsecond - NTP leapsecond processing code.
+ * now: the current time
+ *
+ * Returns the number of seconds (-1, 0, or 1) that
+ * should be added to the current time to properly
+ * adjust for leapseconds.
+ */
+
+int ntp_leapsecond(struct timespec now)
+{
+ unsigned long flags;
+ /*
+ * Leap second processing. If in leap-insert state at
+ * the end of the day, the system clock is set back one
+ * second; if in leap-delete state, the system clock is
+ * set ahead one second.
+ */
+ static time_t leaptime = 0;
+ int ret = 0;
+
+ write_seqlock_irqsave(&ntp_lock, flags);
+ switch (time_state) {
+
+ case TIME_OK:
+ if (time_status & STA_INS) {
+ time_state = TIME_INS;
+ leaptime = END_OF_DAY(now.tv_sec);
+ } else if (time_status & STA_DEL) {
+ time_state = TIME_DEL;
+ leaptime = END_OF_DAY(now.tv_sec);
+ }
+ break;
+
+ case TIME_INS:
+ /* Once we are at (or past) leaptime, insert the second */
+ if (now.tv_sec >= leaptime) {
+ time_state = TIME_OOP;
+ printk(KERN_NOTICE "Clock: inserting leap second 23:59:60 UTC\n");
+ ret = -1;
+ }
+ break;
+
+ case TIME_DEL:
+ /* Once we are at (or past) leaptime, delete the second */
+ if (now.tv_sec >= leaptime) {
+ time_state = TIME_WAIT;
+ printk(KERN_NOTICE "Clock: deleting leap second 23:59:59 UTC\n");
+ ret = 1;
+ }
+ break;
+
+ case TIME_OOP:
+ /* Wait for the end of the leap second*/
+ if (now.tv_sec > (leaptime + 1))
+ time_state = TIME_WAIT;
+ time_state = TIME_WAIT;
+ break;
+
+ case TIME_WAIT:
+ if (!(time_status & (STA_INS | STA_DEL)))
+ time_state = TIME_OK;
+ break;
+ }
+
+ write_sequnlock_irqrestore(&ntp_lock, flags);
+ return 0;
+}
+
/*
* this routine handles the overflow of the microsecond field
*
@@ -663,6 +743,13 @@ static void second_overflow(void)
time_state = TIME_OK;
}

+ /* Bump the maxerror field */
+ time_maxerror += time_tolerance >> SHIFT_USEC;
+ if ( time_maxerror > NTP_PHASE_LIMIT ) {
+ time_maxerror = NTP_PHASE_LIMIT;
+ time_status |= STA_UNSYNC;
+ }
+
/*
* Compute the phase adjustment for the next second. In PLL mode, the
* offset is reduced by a fixed factor times the time constant. In FLL
@@ -678,6 +765,13 @@ static void second_overflow(void)
time_offset -= ltemp;
time_adj = ltemp << (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);

+ offset_adj_ppm = shift_right(ltemp, SHIFT_UPDATE); /* ppm */
+
+ /* first calculate usec/user_tick offset */
+ tick_adj_ppm = ((USEC_PER_SEC + USER_HZ/2)/USER_HZ) - tick_usec;
+ /* multiply by user_hz to get usec/sec => ppm */
+ tick_adj_ppm *= USER_HZ;
+
/*
* Compute the frequency estimate and additional phase adjustment due
* to frequency error for the next second. When the PPS signal is
@@ -726,15 +820,25 @@ static void second_overflow(void)
#endif
}

+
/**
- * ntp_advance() - increments the NTP state machine
+ * ntp_get_ppm_adjustment - Returns Shifted PPM adjustment
*
- * Must be holding the xtime writelock when calling.
+ */
+long ntp_get_ppm_adjustment(void)
+{
+ return total_sppm;
+}
+
+/**
+ * ntp_advance() - increments the NTP state machine
*
*/
-static void ntp_advance(unsigned long interval_ns)
+void ntp_advance(unsigned long interval_ns)
{
static unsigned long interval_sum;
+ unsigned long flags;
+ write_seqlock_irqsave(&ntp_lock, flags);

/* increment the interval sum */
interval_sum += interval_ns;
@@ -761,6 +865,7 @@ static void ntp_advance(unsigned long in
}
interval_ns -= tick_nsec;
}
+ singleshot_adj_ppm = time_adjust_step*(1000000/HZ); /* usec/tick => ppm */

/* Changes by adjtime() do not take effect till next tick. */
if (time_next_adjust != 0) {
@@ -772,6 +877,15 @@ static void ntp_advance(unsigned long in
interval_sum -= NSEC_PER_SEC;
second_overflow();
}
+
+ /* calculate the total continuous ppm adjustment */
+ total_sppm = time_freq; /* already shifted by SHIFT_USEC */
+ total_sppm += offset_adj_ppm << SHIFT_USEC;
+ total_sppm += tick_adj_ppm << SHIFT_USEC;
+ total_sppm += singleshot_adj_ppm << SHIFT_USEC;
+
+ write_sequnlock_irqrestore(&ntp_lock, flags);
+
}

/*
@@ -784,6 +898,7 @@ static void ntp_advance(unsigned long in
static void update_wall_time(unsigned long ticks)
{
long delta_nsec;
+ static long time_phase; /* phase offset (scaled us) */

do {
ticks--;
@@ -807,8 +922,18 @@ static void update_wall_time(unsigned lo

xtime.tv_nsec += delta_nsec;
if (xtime.tv_nsec >= NSEC_PER_SEC) {
+ int leapsecond;
xtime.tv_nsec -= NSEC_PER_SEC;
xtime.tv_sec++;
+ /* process leapsecond */
+ leapsecond = ntp_leapsecond(xtime);
+ if (leapsecond) {
+ xtime.tv_sec += leapsecond;
+ wall_to_monotonic.tv_sec -= leapsecond;
+ /* Use of time interpolator for a gradual change of time */
+ time_interpolator_update(leapsecond*NSEC_PER_SEC);
+ clock_was_set();
+ }
}
ntp_advance(tick_nsec);
time_interpolator_update(delta_nsec);

2005-11-13 01:25:09

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

john stultz <[email protected]> writes:

> All,
> I had hoped to submit this to -mm today, but since Ingo pointed
> out an issue in the __delay code, I'm going to wait a week so the new fix
> can be better tested.

At least on x86-64 there is currently so much other timer related
development going on (per CPU TSC timers, no idle tick, 64bit HPET
etc.) that I don't want any x86-64 bits of that merged for the next
time. The other stuff needs to settle first.

I haven't read the patchset in full detail, but from a quick look
it's also not obvious too me in which way it is easier and cleaner
than the old setup. While the old code was quirky in parts the
new one seems to fall more in the overmodularization/too many
indirect callbacks trap.

It is also totally unclear how it will interact with vsyscall.

-Andi

2005-11-13 02:34:57

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Sun, 2005-11-13 at 02:24 +0100, Andi Kleen wrote:
> john stultz <[email protected]> writes:
>
> > All,
> > I had hoped to submit this to -mm today, but since Ingo pointed
> > out an issue in the __delay code, I'm going to wait a week so the new fix
> > can be better tested.
>
> At least on x86-64 there is currently so much other timer related
> development going on (per CPU TSC timers, no idle tick, 64bit HPET
> etc.) that I don't want any x86-64 bits of that merged for the next
> time. The other stuff needs to settle first.

Oh yes, I should have been more clear. I'm not planning on submitting
the x86-64 bits yet, just the i386. The x86-64 parts are there as an
example that this is useful outside of just i386, and ensures the
framework can support more complex features, like vsyscall for example
(Also just to give some scope, I have code for ppc that I run at home
and code that builds for ppc64, s390, arm, sparc, alpha and ia64, but
all of those will need much more work).

> I haven't read the patchset in full detail, but from a quick look
> it's also not obvious too me in which way it is easier and cleaner
> than the old setup. While the old code was quirky in parts the
> new one seems to fall more in the overmodularization/too many
> indirect callbacks trap.

I appreciate your time in looking at the code. I've added some
documentation in Documentation/timekeeping.txt, so let me know if
anything there needs further clarification.

However I don't feel it is overmodulation. The close linking between
timer interrupts and timekeeping is really problematic because any
change in one affects the other. Further, examples like the list above
(for per-cpu TSC management and 64bit HPET timkeeping) show that the
code could use some modulation so these features can be developed beside
the already working implementations without serious conflicts.

> It is also totally unclear how it will interact with vsyscall.

vsyscall is probably adds the most complexity to the infrastructure, but
I feel I've addressed it in a pretty clean way. And it is working fine
for x86-64 and I have an additional patch that enables it for i386.

Take a look at the arch_update_vsyscall() function in the x86-64 code
and look at the vread() function in the x86-64 tsc clocksource. These
bits could use some cleaning up (I haven't removed the legacy time
variables that are exported), but the foundation is there. Please let me
know if you have any comments or suggestions for further simplifying
that code.

If you need more explanation, I can send the i386 code out on Monday,
which might show a more clear example.

Again, I really appreciate your feedback, and once I move my attention
off of getting the i386 code to Andrew, the x86-64 is the next arch I'll
be focusing on. So suggest away and I'll try to get the code to your
liking.

thanks
-john

2005-11-13 07:32:34

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)


* Andi Kleen <[email protected]> wrote:

> At least on x86-64 there is currently so much other timer related
> development going on (per CPU TSC timers, no idle tick, 64bit HPET
> etc.) that I don't want any x86-64 bits of that merged for the next
> time. The other stuff needs to settle first.
>
> I haven't read the patchset in full detail, but from a quick look it's
> also not obvious too me in which way it is easier and cleaner than the
> old setup. While the old code was quirky in parts the new one seems to
> fall more in the overmodularization/too many indirect callbacks trap.

there are 3 "generic" components needed right now to clean up all time
related stuff: GTOD, ktimers and clockevents. [you know the first two,
and clockevents is new code from Thomas Gleixner that generalizes timer
interrupts and introduces one compact notion for 'clock chips'.]

what is the point? Ontop of these, a previously difficult feature, High
Resolution Timers became _massively_ simpler. All of these patches exist
together in the -rt tree, so it's not handwaving. The same will be the
case for idle ticks / dynamic ticks [we started with HRT because it is
so much harder than idle ticks]. So i do agree with you that GTOD needs
more work, but it also makes time related features all that much easier.

right now it's GTOD that needs the most work before it can be merged
upstream, so you picked the right one to criticise :-)

Ingo

2005-11-13 11:09:33

by Andi Kleen

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Sunday 13 November 2005 08:32, Ingo Molnar wrote:

> there are 3 "generic" components needed right now to clean up all time
> related stuff: GTOD, ktimers and clockevents. [you know the first two,
> and clockevents is new code from Thomas Gleixner that generalizes timer
> interrupts and introduces one compact notion for 'clock chips'.]

Both noidletick and the per cpu gettimeofday change significantly
how timer interrupts work. I hope your generalizations will be still
compatible to that. It's a bit dangerous to generalize
before things have their final shape.

Also vsyscalls make it all more difficult, because they don't map
very well to any kind of "timer drivers".

> what is the point? Ontop of these, a previously difficult feature, High
> Resolution Timers became _massively_ simpler. All of these patches exist
> together in the -rt tree, so it's not handwaving. The same will be the
> case for idle ticks / dynamic ticks [we started with HRT because it is
> so much harder than idle ticks]. So i do agree with you that GTOD needs
> more work, but it also makes time related features all that much easier.
>
> right now it's GTOD that needs the most work before it can be merged
> upstream, so you picked the right one to criticise :-)

My point was basically that there is a lot of feature work going on
on x86-64 in this area, and that has priority over any "cleanups" like this
from my side. If it has settled again later maybe it can be generalized,
or maybe not. I will only do it if it truly makes the code cleaner in the end,
just lots of indirect pointers by itself isn't necessarily something
that does this.

-Andi

2005-11-14 17:41:59

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Sun, 2005-11-13 at 11:53 +0100, Andi Kleen wrote:
> On Sunday 13 November 2005 08:32, Ingo Molnar wrote:
>
> > there are 3 "generic" components needed right now to clean up all time
> > related stuff: GTOD, ktimers and clockevents. [you know the first two,
> > and clockevents is new code from Thomas Gleixner that generalizes timer
> > interrupts and introduces one compact notion for 'clock chips'.]
>
> Both noidletick and the per cpu gettimeofday change significantly
> how timer interrupts work. I hope your generalizations will be still
> compatible to that. It's a bit dangerous to generalize
> before things have their final shape.

The separation of timekeeing from the timer tick is one of the core
design goals here. This allows timer ticks to be modulated without
affecting timekeeping, so I expect this should simplify noidletick. I
know you've mentioned it before, but I don't think I've seen the
noidletick work on lkml. Do you have a link to this work, so I could
make sure the meshing is as nice as I hope it to be?

> Also vsyscalls make it all more difficult, because they don't map
> very well to any kind of "timer drivers".

Again, it is a bit of complexity, but I feel I've resolved the issue
well. However, since it does require more care, specific feedback and
suggestions for improvements will be greatly welcomed.

> > what is the point? Ontop of these, a previously difficult feature, High
> > Resolution Timers became _massively_ simpler. All of these patches exist
> > together in the -rt tree, so it's not handwaving. The same will be the
> > case for idle ticks / dynamic ticks [we started with HRT because it is
> > so much harder than idle ticks]. So i do agree with you that GTOD needs
> > more work, but it also makes time related features all that much easier.
> >
> > right now it's GTOD that needs the most work before it can be merged
> > upstream, so you picked the right one to criticise :-)
>
> My point was basically that there is a lot of feature work going on
> on x86-64 in this area, and that has priority over any "cleanups" like this
> from my side. If it has settled again later maybe it can be generalized,
> or maybe not. I will only do it if it truly makes the code cleaner in the end,
> just lots of indirect pointers by itself isn't necessarily something
> that does this.

I disagree that this is just a cleanup. This work is focused on keeping
time correctly, and it fixes a number problems where timekeeping
inconsistencies have been seen by customers. Most specifically, those
dealing with late or lost timers interrupts. Its just that to fix these
issues much of the code had to be reworked, so I spent some time making
sure the new method was reusable by other arches and made other time
related features (HRT, dynmaic ticks, etc) simpler.

Again, I really look forward to any suggestions you have for the code.

thanks
-john

2005-11-14 18:23:53

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)


* Andi Kleen <[email protected]> wrote:

> My point was basically that there is a lot of feature work going on on
> x86-64 in this area, and that has priority over any "cleanups" like
> this from my side. [...]

i think we are in agreement mostly, but this approach of yours is just
... totally wrong. We are seeing an exponential increase in the 'cost'
of new time features because they are being built ontop of a naive base
that did not anticipate them. HRT was 'coming soon' for how many years -
eight?

just take a look at all the VFS work Al Viro has done and is doing. 90%
"cleanups", in preparation of a small 1000-line (or smaller) real
feature thing. If it were done the other way around, we'd still be at
5-10 poorly integrated filesystems and a poor VFS landscape - not at the
current 50+ filesystems supported by the best VFS on this planet ...

or just take a look at all the work Jens Axboe & co has done in the past
2 years, resurrecting the block IO code from the grave. Jens started at
the basics (replacing bhs with bio's), cleaning up the mess at its root.
Had they started adding pluggable IO schedulers and IO barriers as the
_first_ step, the block IO layer would still be a pain point, instead of
a poster-child.

i could go on with other examples. Networking. Firewall code. The MM.
Driver architecture - and more. x86_64 did get rid of lots of i386
legacies as well, so you are doing it too. Today's cleanliness is the
basis for tomorrow's features, not the other way around. New features
_always_ deform the code's internal structure, and if piled upon each
other without cleanups inbetween then they form a massive, hard to
change and hard to maintain chunk of spaghetti. The time code has been
long overdue for a massive cleanup.

the Linux CPU architecture code is currently where the VFS was 5 years
ago. Lots of consolidation work was done in the past 1-2 years, but both
i386 and x86_64 still have at least 30% of code bloat that does not
truly belong into architecture code. Now we have 25 main architectures,
and every unnecessary unit of complexity gets multiplied by 25!
Suggesting that generalization, common code and cleanups have a lower
priority than features is really ignoring history and common sense.

Ingo

2005-11-14 21:22:44

by Frank Sorenson

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

john stultz wrote:
> All,
> I had hoped to submit this to -mm today, but since Ingo pointed
> out an issue in the __delay code, I'm going to wait a week so the new fix
> can be better tested.

I replaced the TOD parts of the kthrt patchset with TOD B10. It seems
there is something wrong with 'c3tsc' and 'pit', though.

c3tsc appears to run fast:
14 Nov 12:02:13 offset: -0.00247 drift: -2502.0 ppm
14 Nov 12:03:14 offset: -0.145203 drift: -2342.5 ppm
14 Nov 12:04:14 offset: -0.329381 drift: -2700.10655738 ppm
14 Nov 12:05:14 offset: -0.532767 drift: -2927.46703297 ppm
14 Nov 12:06:15 offset: -0.638096 drift: -2626.04115226 ppm

and 'pit' seems to produce errors (system will not switch from pit to
another clocksource anymore):

[ 7.976858] Time: tsc clocksource has been installed.
[ 7.977855] Ktimers: Switched to high resolution mode CPU 0
[ 11.443804] Falling back to C3 safe TSC
[ 11.504408] check_periodic_interval: Long interval! 177494381.
[ 11.510560] Something may be blocking interrupts.
[ 20.223000] Time: acpi_pm clocksource has been installed.
[ 20.223000] Ktimers: Switched to high resolution mode CPU 0
[ 2990.834000] Time: c3tsc clocksource has been installed.
[ 2990.835000] Ktimers: Switched to high resolution mode CPU 0
[ 3289.320000] Time: pit clocksource has been installed.
[ 3289.365000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.365000] from 2fe277e9c61 (3290607557729) to
2fe2775a556 (3290606970198).
[ 3289.365000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.365000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.365000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.365000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.365000] [<c02d4b4b>] acpi_hw_low_level_write+0xc0/0xd1
[ 3289.365000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.365000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.365000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.365000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.365000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.365000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.365000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.365000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.365000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.367000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.367000] from 2fe27944675 (3290608977525) to
2fe279428fe (3290608969982).
[ 3289.367000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.367000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.367000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.367000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.367000] [<c02d4b4b>] acpi_hw_low_level_write+0xc0/0xd1
[ 3289.367000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.367000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.367000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.367000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.367000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.367000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.367000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.367000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.367000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.371000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.371000] from 2fe27da658d (3290613573005) to
2fe27d1304e (3290612969550).
[ 3289.371000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.371000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.371000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.371000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.371000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.371000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.371000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.371000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.371000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.371000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.371000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.371000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.371000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.371000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.423000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.423000] from 2fe2aeada9c (3290664983196) to
2fe2aea8f4f (3290664963919).
[ 3289.423000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.423000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.423000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.423000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.423000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.423000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.423000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.423000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.423000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.423000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.423000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.423000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.423000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.423000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.507000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.507000] from 2fe2ff461ee (3290749493742) to
2fe2fec28c7 (3290748954823).
[ 3289.507000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.507000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.507000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.507000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.507000] [<c02d4b4b>] acpi_hw_low_level_write+0xc0/0xd1
[ 3289.507000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.507000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.507000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.507000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.507000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.507000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.507000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.507000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.507000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.509000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.509000] from 2fe300aeaa3 (3290750970531) to
2fe300aac6f (3290750954607).
[ 3289.509000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.509000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.509000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.509000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.509000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.509000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.509000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.509000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.509000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.509000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.509000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.509000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.509000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.509000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.525000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.525000] from 2fe30ff1b84 (3290766973828) to
2fe30fec9aa (3290766952874).
[ 3289.525000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.525000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.525000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.525000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.525000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.525000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.525000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.525000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.525000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.525000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.525000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.525000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.525000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.525000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.541000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.541000] from 2fe31f4adfa (3290783067642) to
2fe31f2e6e6 (3290782951142).
[ 3289.541000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.541000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.541000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.541000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.541000] [<c02d4b4b>] acpi_hw_low_level_write+0xc0/0xd1
[ 3289.541000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.541000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.541000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.541000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.541000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.541000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.541000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.541000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.541000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.543000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.543000] from 2fe32117aec (3290784955116) to
2fe32116a8d (3290784950925).
[ 3289.543000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.543000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.543000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.543000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.543000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.543000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.543000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.543000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.543000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.543000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.543000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.543000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.543000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.543000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3289.545000] check_monotonic_clock: monotonic inconsistency detected!
[ 3289.545000] from 2fe322ffe93 (3290786954899) to
2fe322fee35 (3290786950709).
[ 3289.545000] Badness in check_monotonic_clock at
kernel/time/timeofday.c:156
[ 3289.545000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 3289.545000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 3289.545000] [<c013642b>] ktimer_interrupt+0x2b/0x2e0
[ 3289.545000] [<c02d4a7b>] acpi_hw_low_level_read+0xbb/0xcb
[ 3289.545000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.545000] [<c02e258f>] acpi_ut_status_exit+0x48/0x56
[ 3289.545000] [<c0137c48>] handle_nextevent_profile+0x8/0x20
[ 3289.545000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 3289.545000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 3289.545000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 3289.545000] [<c01010f2>] cpu_idle+0x42/0x60
[ 3289.545000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 3289.545000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 3695.183000] check_periodic_interval: Long interval! 106988521.
[ 3695.183000] Something may be blocking interrupts.
[ 3716.931000] check_periodic_interval: Long interval! 102989788.
[ 3716.931000] Something may be blocking interrupts.
[ 3731.137000] check_periodic_interval: Long interval! 101989057.
[ 3731.137000] Something may be blocking interrupts.
[ 3739.737000] check_periodic_interval: Long interval! 101000060.
[ 3739.737000] Something may be blocking interrupts.
[ 3741.268000] check_periodic_interval: Long interval! 106989359.
[ 3741.268000] Something may be blocking interrupts.
[ 3741.990000] check_periodic_interval: Long interval! 100990002.
[ 3741.990000] Something may be blocking interrupts.
[ 3772.714000] check_periodic_interval: Long interval! 104811889.
[ 3772.714000] Something may be blocking interrupts.
[ 3779.810000] check_periodic_interval: Long interval! 101989057.
[ 3779.810000] Something may be blocking interrupts.
[ 3781.311000] check_periodic_interval: Long interval! 109989037.
[ 3781.311000] Something may be blocking interrupts.




Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
[email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.7 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iD8DBQFDeP//aI0dwg4A47wRAkOZAKDTpjo/+xOdOmwvOXQX++IHMEytvQCgxfu+
ct9J4Rt7zIkwQx6a8FpWgwo=
=iVge
-----END PGP SIGNATURE-----

2005-11-14 21:38:48

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Mon, 2005-11-14 at 14:22 -0700, Frank Sorenson wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> john stultz wrote:
> > All,
> > I had hoped to submit this to -mm today, but since Ingo pointed
> > out an issue in the __delay code, I'm going to wait a week so the new fix
> > can be better tested.
>
> I replaced the TOD parts of the kthrt patchset with TOD B10. It seems
> there is something wrong with 'c3tsc' and 'pit', though.
>
> c3tsc appears to run fast:
> 14 Nov 12:02:13 offset: -0.00247 drift: -2502.0 ppm
> 14 Nov 12:03:14 offset: -0.145203 drift: -2342.5 ppm
> 14 Nov 12:04:14 offset: -0.329381 drift: -2700.10655738 ppm
> 14 Nov 12:05:14 offset: -0.532767 drift: -2927.46703297 ppm
> 14 Nov 12:06:15 offset: -0.638096 drift: -2626.04115226 ppm

Hmm... Not sure if this is mis-calibration or just bad-interaction w/
kthrt. Mind sending a dmesg to me?

> and 'pit' seems to produce errors (system will not switch from pit to
> another clocksource anymore):

Do the TOD patches have this issue by themselves, or is this only with
kthrt? I know I had some issues with non-continuous clocksources (pit,
jiffies) with the kthrt patch, where it wouldn't fall back to
non-high-res when the clocksource stopped supporting it.

thanks
-john


2005-11-14 21:53:55

by Frank Sorenson

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

[ 0.000000] Linux version 2.6.15-rc1-kthrt2-fs2 ([email protected]) (gcc version 4.0.1 20050727 (Red Hat 4.0.1-5)) #2 PREEMPT Sun Nov 13 18:41:34 MST 2005
[ 0.000000] BIOS-provided physical RAM map:
[ 0.000000] BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
[ 0.000000] BIOS-e820: 000000000009f000 - 00000000000a0000 (reserved)
[ 0.000000] BIOS-e820: 0000000000100000 - 000000005ffae000 (usable)
[ 0.000000] BIOS-e820: 000000005ffae000 - 0000000060000000 (reserved)
[ 0.000000] BIOS-e820: 00000000feda0000 - 00000000fee00000 (reserved)
[ 0.000000] BIOS-e820: 00000000ffb00000 - 0000000100000000 (reserved)
[ 0.000000] 639MB HIGHMEM available.
[ 0.000000] 896MB LOWMEM available.
[ 0.000000] On node 0 totalpages: 393134
[ 0.000000] DMA zone: 4096 pages, LIFO batch:2
[ 0.000000] Normal zone: 225280 pages, LIFO batch:64
[ 0.000000] HighMem zone: 163758 pages, LIFO batch:64
[ 0.000000] DMI 2.3 present.
[ 0.000000] ACPI: RSDP (v000 DELL ) @ 0x000fdf00
[ 0.000000] ACPI: RSDT (v001 DELL CPi R 0x27d50201 ASL 0x00000061) @ 0x5fff0000
[ 0.000000] ACPI: FADT (v001 DELL CPi R 0x27d50201 ASL 0x00000061) @ 0x5fff0400
[ 0.000000] ACPI: DSDT (v001 INT430 SYSFexxx 0x00001001 MSFT 0x0100000e) @ 0x00000000
[ 0.000000] ACPI: PM-Timer IO Port: 0x808
[ 0.000000] Allocating PCI resources starting at 70000000 (gap: 60000000:9eda0000)
[ 0.000000] Detected 1993.647 MHz processor.
[ 10.305106] Built 1 zonelists
[ 10.305108] Kernel command line: ro root=LABEL=/1 vga=794 nmi_watchdog=1 apic=verbose lapic psmouse.proto=exps acpi_sleep=s3_bios init=/sbin/tuxinit console=tty0 console=ttyUSB0,9600
[ 10.305320] Local APIC disabled by BIOS -- reenabling.
[ 10.305322] Found and enabled local APIC!
[ 10.305324] mapped APIC to ffffd000 (fee00000)
[ 10.305328] Initializing CPU#0
[ 10.305386] CPU 0 irqstacks, hard=c05e4000 soft=c05e3000
[ 10.305389] PID hash table entries: 4096 (order: 12, 65536 bytes)
[ 10.426439] Event source pit installed with caps set: 0f
[ 10.426468] Console: colour dummy device 80x25
[ 10.427104] Dentry cache hash table entries: 131072 (order: 7, 524288 bytes)
[ 10.427813] Inode-cache hash table entries: 65536 (order: 6, 262144 bytes)
[ 10.481089] Memory: 1550872k/1572536k available (3506k kernel code, 19564k reserved, 1260k data, 212k init, 655032k highmem)
[ 10.481109] Checking if this processor honours the WP bit even in supervisor mode... Ok.
[ 10.540282] Calibrating delay using timer specific routine.. 3988.30 BogoMIPS (lpj=1994153)
[ 10.540327] Mount-cache hash table entries: 512
[ 10.540452] CPU: After generic identify, caps: afe9fbbf 00000000 00000000 00000000 00000180 00000000 00000000
[ 10.540461] CPU: After vendor identify, caps: afe9fbbf 00000000 00000000 00000000 00000180 00000000 00000000
[ 10.540470] CPU: L1 I cache: 32K, L1 D cache: 32K
[ 10.540474] CPU: L2 cache: 2048K
[ 10.540476] CPU: After all inits, caps: afe9fbbf 00000000 00000000 00000040 00000180 00000000 00000000
[ 10.540482] Intel machine check architecture supported.
[ 10.540487] Intel machine check reporting enabled on CPU#0.
[ 10.540500] mtrr: v2.0 (20020519)
[ 10.540505] CPU: Intel(R) Pentium(R) M processor 2.00GHz stepping 06
[ 10.540510] Enabling fast FPU save and restore... done.
[ 10.540513] Enabling unmasked SIMD FPU exception support... done.
[ 10.540518] Checking 'hlt' instruction... OK.
[ 10.551138] tbxface-0109 [02] load_tables : ACPI Tables successfully acquired
[ 10.553386] Parsing all Control Methods:........................................................................................................................................................
[ 10.558219] Table [DSDT](id 0005) - 364 Objects with 60 Devices 152 Methods 5 Regions
[ 10.558225] ACPI Namespace successfully loaded at root c062e158
[ 10.558232] ACPI: setting ELCR to 0200 (from 0800)
[ 10.559935] evxfevnt-0091 [03] enable : Transition to ACPI mode successful
[ 10.560003] enabled ExtINT on CPU#0
[ 10.560008] Using local APIC timer interrupts.
[ 10.560011] calibrating APIC timer ...
[ 10.660108] ..... CPU clock speed is 1993.0218 MHz.
[ 10.660110] ..... host bus clock speed is 99.0660 MHz.
[ 10.660114] Event source pit new caps set: 05
[ 10.660117] Event source lapic installed with caps set: 0a
[ 10.660220] checking if image is initramfs... it is
[ 10.726911] Freeing initrd memory: 985k freed
[ 10.727021] DEV: registering device: ID = 'platform'
[ 10.727026] PM: Adding info for No Bus:platform
[ 10.727039] bus type 'platform' registered
[ 10.727041] Registering sysdev class '<NULL>'
[ 10.727215] NET: Registered protocol family 16
[ 10.727228] device class 'pci_bus': registering
[ 10.727234] bus type 'pci' registered
[ 10.727236] device class 'tty': registering
[ 10.727240] ACPI: bus type pci registered
[ 10.744176] PCI: PCI BIOS revision 2.10 entry at 0xfcf9e, last bus=2
[ 10.744184] PCI: Using configuration type 1
[ 10.744189] Registering sys device 'cpu0'
[ 10.744789] bus type 'usb' registered
[ 10.744793] device class 'usb_host': registering
[ 10.744800] device class 'usb': registering
[ 10.744804] bus usb: add driver usbfs
[ 10.745049] usbcore: registered new driver usbfs
[ 10.745056] device class 'usb_device': registering
[ 10.745064] bus usb: add driver hub
[ 10.745254] usbcore: registered new driver hub
[ 10.745264] bus usb: add driver usb
[ 10.745445] device class 'graphics': registering
[ 10.745450] ACPI: Subsystem revision 20050902
[ 10.746619] evgpeblk-0988 [06] ev_create_gpe_block : GPE 00 to 1F [_GPE] 4 regs on int 0x9
[ 10.746628] evgpeblk-0996 [06] ev_create_gpe_block : Found 7 Wake, Enabled 2 Runtime GPEs in this block
[ 10.753904] Completing Region/Field/Buffer/Package initialization:.............................................
[ 10.755836] Initialized 3/5 Regions 6/7 Fields 20/21 Buffers 16/25 Packages (373 nodes)
[ 10.755842] Executing all Device _STA and_INI methods:................................................................
[ 10.759907] 64 Devices found containing: 64 _STA, 3 _INI methods
[ 10.759919] ACPI: Interpreter enabled
[ 10.759922] ACPI: Using PIC for interrupt routing
[ 10.762706] ACPI: PCI Root Bridge [PCI0] (0000:00)
[ 10.762711] PCI: Probing PCI hardware (bus 00)
[ 10.762716] DEV: registering device: ID = 'pci0000:00'
[ 10.762721] PM: Adding info for No Bus:pci0000:00
[ 10.762924] ACPI: Assume root bridge [\_SB_.PCI0] bus is 0
[ 10.773298] CLASS: registering class device: ID = '0000:00'
[ 10.773304] class_hotplug - name = 0000:00
[ 10.773790] PCI quirk: region 0800-087f claimed by ICH4 ACPI/GPIO/TCO
[ 10.773795] PCI quirk: region 0880-08bf claimed by ICH4 GPIO
[ 10.773838] PCI: Ignoring BAR0-3 of IDE controller 0000:00:1f.1
[ 10.773929] CLASS: registering class device: ID = '0000:01'
[ 10.773934] class_hotplug - name = 0000:01
[ 10.774151] Boot video device is 0000:01:00.0
[ 10.774184] CLASS: registering class device: ID = '0000:02'
[ 10.774188] class_hotplug - name = 0000:02
[ 10.774582] PCI: Transparent bridge - 0000:00:1e.0
[ 10.774606] CLASS: registering class device: ID = '0000:03'
[ 10.774611] class_hotplug - name = 0000:03
[ 10.774811] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0._PRT]
[ 10.775302] DEV: registering device: ID = '0000:00:00.0'
[ 10.775479] PM: Adding info for pci:0000:00:00.0
[ 10.775483] bus pci: add device 0000:00:00.0
[ 10.776573] DEV: registering device: ID = '0000:00:01.0'
[ 10.776768] PM: Adding info for pci:0000:00:01.0
[ 10.776774] bus pci: add device 0000:00:01.0
[ 10.777862] DEV: registering device: ID = '0000:00:1d.0'
[ 10.777940] PM: Adding info for pci:0000:00:1d.0
[ 10.777943] bus pci: add device 0000:00:1d.0
[ 10.779027] DEV: registering device: ID = '0000:00:1d.1'
[ 10.779242] PM: Adding info for pci:0000:00:1d.1
[ 10.779249] bus pci: add device 0000:00:1d.1
[ 10.780339] DEV: registering device: ID = '0000:00:1d.2'
[ 10.780548] PM: Adding info for pci:0000:00:1d.2
[ 10.780551] bus pci: add device 0000:00:1d.2
[ 10.781646] DEV: registering device: ID = '0000:00:1d.7'
[ 10.781854] PM: Adding info for pci:0000:00:1d.7
[ 10.781857] bus pci: add device 0000:00:1d.7
[ 10.782955] DEV: registering device: ID = '0000:00:1e.0'
[ 10.783170] PM: Adding info for pci:0000:00:1e.0
[ 10.783175] bus pci: add device 0000:00:1e.0
[ 10.784268] DEV: registering device: ID = '0000:00:1f.0'
[ 10.784489] PM: Adding info for pci:0000:00:1f.0
[ 10.784494] bus pci: add device 0000:00:1f.0
[ 10.785594] DEV: registering device: ID = '0000:00:1f.1'
[ 10.785806] PM: Adding info for pci:0000:00:1f.1
[ 10.785810] bus pci: add device 0000:00:1f.1
[ 10.786898] DEV: registering device: ID = '0000:00:1f.5'
[ 10.787123] PM: Adding info for pci:0000:00:1f.5
[ 10.787131] bus pci: add device 0000:00:1f.5
[ 10.788220] DEV: registering device: ID = '0000:00:1f.6'
[ 10.788441] PM: Adding info for pci:0000:00:1f.6
[ 10.788445] bus pci: add device 0000:00:1f.6
[ 10.789541] DEV: registering device: ID = '0000:01:00.0'
[ 10.789902] PM: Adding info for pci:0000:01:00.0
[ 10.789906] bus pci: add device 0000:01:00.0
[ 10.789965] DEV: registering device: ID = '0000:02:00.0'
[ 10.790191] PM: Adding info for pci:0000:02:00.0
[ 10.790197] bus pci: add device 0000:02:00.0
[ 10.790284] DEV: registering device: ID = '0000:02:01.0'
[ 10.790499] PM: Adding info for pci:0000:02:01.0
[ 10.790505] bus pci: add device 0000:02:01.0
[ 10.790600] DEV: registering device: ID = '0000:02:01.1'
[ 10.790812] PM: Adding info for pci:0000:02:01.1
[ 10.790816] bus pci: add device 0000:02:01.1
[ 10.790900] DEV: registering device: ID = '0000:02:01.2'
[ 10.791132] PM: Adding info for pci:0000:02:01.2
[ 10.791142] bus pci: add device 0000:02:01.2
[ 10.791227] DEV: registering device: ID = '0000:02:03.0'
[ 10.791440] PM: Adding info for pci:0000:02:03.0
[ 10.791444] bus pci: add device 0000:02:03.0
[ 10.800686] ACPI: PCI Interrupt Link [LNKA] (IRQs 9 10 *11)
[ 10.801350] ACPI: PCI Interrupt Link [LNKB] (IRQs 5 7) *11
[ 10.801993] ACPI: PCI Interrupt Link [LNKC] (IRQs 9 10 *11)
[ 10.802628] ACPI: PCI Interrupt Link [LNKD] (IRQs 5 7 9 10 *11)
[ 10.803242] ACPI: PCI Interrupt Link [LNKE] (IRQs 3 4 5 6 7 9 10 11 12 14 15) *0, disabled.
[ 10.803878] ACPI: PCI Interrupt Link [LNKH] (IRQs 3 4 5 6 7 9 10 *11 12 14 15)
[ 10.805712] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0.AGP_._PRT]
[ 10.808270] ACPI: PCI Interrupt Routing Table [\_SB_.PCI0.PCIE._PRT]
[ 10.809598] Linux Plug and Play Support v0.97 (c) Adam Belay
[ 10.809613] bus type 'pnp' registered
[ 10.809614] pnp: PnP ACPI init
[ 10.809619] DEV: registering device: ID = 'pnp0'
[ 10.809622] PM: Adding info for No Bus:pnp0
[ 10.818494] DEV: registering device: ID = '00:00'
[ 10.818675] PM: Adding info for pnp:00:00
[ 10.818678] bus pnp: add device 00:00
[ 10.818753] DEV: registering device: ID = '00:01'
[ 10.818964] PM: Adding info for pnp:00:01
[ 10.818969] bus pnp: add device 00:01
[ 10.819043] DEV: registering device: ID = '00:02'
[ 10.819247] PM: Adding info for pnp:00:02
[ 10.819250] bus pnp: add device 00:02
[ 10.819330] DEV: registering device: ID = '00:03'
[ 10.819536] PM: Adding info for pnp:00:03
[ 10.819541] bus pnp: add device 00:03
[ 10.819615] DEV: registering device: ID = '00:04'
[ 10.819829] PM: Adding info for pnp:00:04
[ 10.819837] bus pnp: add device 00:04
[ 10.819920] DEV: registering device: ID = '00:05'
[ 10.820136] PM: Adding info for pnp:00:05
[ 10.820140] bus pnp: add device 00:05
[ 10.820220] DEV: registering device: ID = '00:06'
[ 10.820433] PM: Adding info for pnp:00:06
[ 10.820436] bus pnp: add device 00:06
[ 10.820506] DEV: registering device: ID = '00:07'
[ 10.820722] PM: Adding info for pnp:00:07
[ 10.820725] bus pnp: add device 00:07
[ 10.820809] DEV: registering device: ID = '00:08'
[ 10.821030] PM: Adding info for pnp:00:08
[ 10.821035] bus pnp: add device 00:08
[ 10.821109] DEV: registering device: ID = '00:09'
[ 10.821328] PM: Adding info for pnp:00:09
[ 10.821334] bus pnp: add device 00:09
[ 10.822069] pnp: PnP ACPI: found 10 devices
[ 10.822081] device class 'misc': registering
[ 10.822090] device class 'pcmcia_socket': registering
[ 10.822100] device class 'input': registering
[ 10.822110] bus type 'i2c' registered
[ 10.822112] bus i2c: add driver i2c_adapter
[ 10.822354] device class 'i2c-adapter': registering
[ 10.822362] bus type 'ac97' registered
[ 10.822364] PCI: Using ACPI for IRQ routing
[ 10.822369] PCI: If a device doesn't work, try "pci=routeirq". If it helps, post a report
[ 10.829255] device class 'net': registering
[ 10.838241] bus pnp: add driver system
[ 10.838455] pnp: Matched Device 00:00 with Driver system
[ 10.838458] bound device '00:00' to driver 'system'
[ 10.838461] pnp: Bound Device 00:00 to Driver system
[ 10.838463] pnp: Matched Device 00:01 with Driver system
[ 10.838466] pnp: 00:01: ioport range 0x4d0-0x4d1 has been reserved
[ 10.838471] pnp: 00:01: ioport range 0x800-0x805 could not be reserved
[ 10.838475] pnp: 00:01: ioport range 0x808-0x80f could not be reserved
[ 10.838478] bound device '00:01' to driver 'system'
[ 10.838480] pnp: Bound Device 00:01 to Driver system
[ 10.838482] pnp: Matched Device 00:02 with Driver system
[ 10.838484] pnp: 00:02: ioport range 0xf400-0xf4fe has been reserved
[ 10.838488] pnp: 00:02: ioport range 0x806-0x807 has been reserved
[ 10.838491] pnp: 00:02: ioport range 0x810-0x85f could not be reserved
[ 10.838495] pnp: 00:02: ioport range 0x860-0x87f has been reserved
[ 10.838498] pnp: 00:02: ioport range 0x880-0x8bf has been reserved
[ 10.838501] pnp: 00:02: ioport range 0x8c0-0x8df has been reserved
[ 10.838504] bound device '00:02' to driver 'system'
[ 10.838506] pnp: Bound Device 00:02 to Driver system
[ 10.838510] pnp: Matched Device 00:07 with Driver system
[ 10.838512] pnp: 00:07: ioport range 0x900-0x97f has been reserved
[ 10.838515] bound device '00:07' to driver 'system'
[ 10.838517] pnp: Bound Device 00:07 to Driver system
[ 10.838522] device class 'mem': registering
[ 10.838527] CLASS: registering class device: ID = 'mem'
[ 10.838532] class_hotplug - name = mem
[ 10.838535] class_device_create_hotplug called for mem
[ 10.838709] CLASS: registering class device: ID = 'kmem'
[ 10.838714] class_hotplug - name = kmem
[ 10.838716] class_device_create_hotplug called for kmem
[ 10.838936] CLASS: registering class device: ID = 'null'
[ 10.838941] class_hotplug - name = null
[ 10.838943] class_device_create_hotplug called for null
[ 10.839147] CLASS: registering class device: ID = 'port'
[ 10.839152] class_hotplug - name = port
[ 10.839154] class_device_create_hotplug called for port
[ 10.839360] CLASS: registering class device: ID = 'zero'
[ 10.839367] class_hotplug - name = zero
[ 10.839369] class_device_create_hotplug called for zero
[ 10.839565] CLASS: registering class device: ID = 'full'
[ 10.839571] class_hotplug - name = full
[ 10.839573] class_device_create_hotplug called for full
[ 10.839778] CLASS: registering class device: ID = 'random'
[ 10.839784] class_hotplug - name = random
[ 10.839786] class_device_create_hotplug called for random
[ 10.840012] CLASS: registering class device: ID = 'urandom'
[ 10.840019] class_hotplug - name = urandom
[ 10.840021] class_device_create_hotplug called for urandom
[ 10.840229] CLASS: registering class device: ID = 'kmsg'
[ 10.840235] class_hotplug - name = kmsg
[ 10.840237] class_device_create_hotplug called for kmsg
[ 10.840464] bus type 'pcmcia' registered
[ 10.840498] PCI: Bridge: 0000:00:01.0
[ 10.840505] IO window: c000-cfff
[ 10.840509] MEM window: fc000000-fdffffff
[ 10.840513] PREFETCH window: f0000000-f7ffffff
[ 10.840521] PCI: Bus 3, cardbus bridge: 0000:02:01.0
[ 10.840524] IO window: 0000e000-0000e0ff
[ 10.840528] IO window: 0000e400-0000e4ff
[ 10.840533] PREFETCH window: 70000000-71ffffff
[ 10.840537] MEM window: 74000000-75ffffff
[ 10.840541] PCI: Bridge: 0000:00:1e.0
[ 10.840545] IO window: e000-efff
[ 10.840550] MEM window: fa000000-fbffffff
[ 10.840555] PREFETCH window: 70000000-71ffffff
[ 10.840572] PCI: Setting latency timer of device 0000:00:1e.0 to 64
[ 10.840587] acpi_bus-0200 [01] bus_set_power : Device is not power manageable
[ 10.840595] PCI: Enabling device 0000:02:01.0 (0000 -> 0003)
[ 10.841037] ACPI: PCI Interrupt Link [LNKD] enabled at IRQ 11
[ 10.841042] PCI: setting IRQ 11 as level-triggered
[ 10.841046] ACPI: PCI Interrupt 0000:02:01.0[A] -> Link [LNKD] -> GSI 11 (level, low) -> IRQ 11
[ 10.841056] Registering sysdev class '<NULL>'
[ 10.841061] Registering sys device 'i82590'
[ 10.841297] Registering sysdev class '<NULL>'
[ 10.841301] Registering sys device 'i82370'
[ 10.841524] apm: BIOS not found.
[ 10.841530] Registering sysdev class '<NULL>'
[ 10.841534] Registering sys device 'lapic0'
[ 10.841739] Registering sysdev class '<NULL>'
[ 10.853124] Registering sysdev class '<NULL>'
[ 10.853128] Registering sys device 'clocksource0'
[ 10.853299] Registering sysdev class '<NULL>'
[ 10.853307] Registering sys device 'global_clock_event0'
[ 10.853506] Registering sysdev class '<NULL>'
[ 10.853510] Registering sys device 'timeofday0'
[ 10.853730] audit: initializing netlink socket (disabled)
[ 10.853746] audit(1131979227.293:1): initialized
[ 10.853803] highmem bounce pool size: 64 pages
[ 10.853808] Total HugeTLB memory allocated, 0
[ 10.853870] VFS: Disk quotas dquot_6.5.1
[ 10.853891] Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
[ 10.853970] Installing knfsd (copyright (C) 1996 [email protected]).
[ 10.854108] fuse init (API version 7.3)
[ 10.854122] CLASS: registering class device: ID = 'fuse'
[ 10.854132] class_hotplug - name = fuse
[ 10.854135] class_device_create_hotplug called for fuse
[ 10.854413] Initializing Cryptographic API
[ 10.854420] io scheduler noop registered
[ 10.854427] io scheduler anticipatory registered
[ 10.854432] io scheduler deadline registered
[ 10.854439] io scheduler cfq registered
[ 10.854535] pci_hotplug: PCI Hot Plug PCI Core version: 0.5
[ 10.854539] usbmon: debugfs is not available
[ 10.854544] bus pci: add driver ehci_hcd
[ 10.854784] pci: Matched Device 0000:00:1d.7 with Driver ehci_hcd
[ 10.854803] acpi_bus-0200 [01] bus_set_power : Device is not power manageable
[ 10.855248] ACPI: PCI Interrupt Link [LNKH] enabled at IRQ 11
[ 10.855253] ACPI: PCI Interrupt 0000:00:1d.7[D] -> Link [LNKH] -> GSI 11 (level, low) -> IRQ 11
[ 10.855267] PCI: Setting latency timer of device 0000:00:1d.7 to 64
[ 10.855270] ehci_hcd 0000:00:1d.7: EHCI Host Controller
[ 10.855283] ehci_hcd 0000:00:1d.7: debug port 1
[ 10.855297] CLASS: registering class device: ID = 'usb_host1'
[ 10.855306] class_hotplug - name = usb_host1
[ 10.855308] class_device_create_hotplug called for usb_host1
[ 10.855540] ehci_hcd 0000:00:1d.7: new USB bus registered, assigned bus number 1
[ 10.855550] ehci_hcd 0000:00:1d.7: irq 11, io mem 0xf8fffc00
[ 10.855562] PCI: cache line size of 32 is not supported by device 0000:00:1d.7
[ 10.859443] ehci_hcd 0000:00:1d.7: USB 2.0 initialized, EHCI 1.00, driver 10 Dec 2004
[ 10.859472] DEV: registering device: ID = 'usb1'
[ 10.859657] PM: Adding info for usb:usb1
[ 10.859662] bus usb: add device usb1
[ 10.859664] bound device 'usb1' to driver 'usb'
[ 10.859684] DEV: registering device: ID = '1-0:1.0'
[ 10.859891] PM: Adding info for usb:1-0:1.0
[ 10.859895] bus usb: add device 1-0:1.0
[ 10.859898] usb: Matched Device 1-0:1.0 with Driver hub
[ 10.859901] hub 1-0:1.0: USB hub found
[ 10.859911] hub 1-0:1.0: 6 ports detected
[ 10.882789] Time: tsc clocksource has been installed.
[ 10.883786] Ktimers: Switched to high resolution mode CPU 0
[ 10.960680] bound device '1-0:1.0' to driver 'hub'
[ 10.960682] usb: Bound Device 1-0:1.0 to Driver hub
[ 10.960691] CLASS: registering class device: ID = 'usbdev1.1'
[ 10.960697] class_hotplug - name = usbdev1.1
[ 10.960700] class_device_create_hotplug called for usbdev1.1
[ 10.960936] bound device '0000:00:1d.7' to driver 'ehci_hcd'
[ 10.960939] pci: Bound Device 0000:00:1d.7 to Driver ehci_hcd
[ 10.960946] USB Universal Host Controller Interface driver v2.3
[ 10.960964] bus pci: add driver uhci_hcd
[ 10.961178] pci: Matched Device 0000:00:1d.0 with Driver uhci_hcd
[ 10.961571] ACPI: PCI Interrupt Link [LNKA] enabled at IRQ 11
[ 10.961577] ACPI: PCI Interrupt 0000:00:1d.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 10.961589] PCI: Setting latency timer of device 0000:00:1d.0 to 64
[ 10.961592] uhci_hcd 0000:00:1d.0: UHCI Host Controller
[ 10.961611] CLASS: registering class device: ID = 'usb_host2'
[ 10.961622] class_hotplug - name = usb_host2
[ 10.961624] class_device_create_hotplug called for usb_host2
[ 10.961829] uhci_hcd 0000:00:1d.0: new USB bus registered, assigned bus number 2
[ 10.961838] uhci_hcd 0000:00:1d.0: irq 11, io base 0x0000bf80
[ 10.961880] DEV: registering device: ID = 'usb2'
[ 10.962064] PM: Adding info for usb:usb2
[ 10.962068] bus usb: add device usb2
[ 10.962070] bound device 'usb2' to driver 'usb'
[ 10.962085] DEV: registering device: ID = '2-0:1.0'
[ 10.962294] PM: Adding info for usb:2-0:1.0
[ 10.962297] bus usb: add device 2-0:1.0
[ 10.962300] usb: Matched Device 2-0:1.0 with Driver hub
[ 10.962302] hub 2-0:1.0: USB hub found
[ 10.962311] hub 2-0:1.0: 2 ports detected
[ 11.062530] bound device '2-0:1.0' to driver 'hub'
[ 11.062533] usb: Bound Device 2-0:1.0 to Driver hub
[ 11.062541] CLASS: registering class device: ID = 'usbdev2.1'
[ 11.062547] class_hotplug - name = usbdev2.1
[ 11.062551] class_device_create_hotplug called for usbdev2.1
[ 11.062777] bound device '0000:00:1d.0' to driver 'uhci_hcd'
[ 11.062780] pci: Bound Device 0000:00:1d.0 to Driver uhci_hcd
[ 11.062782] pci: Matched Device 0000:00:1d.1 with Driver uhci_hcd
[ 11.062791] ACPI: PCI Interrupt 0000:00:1d.1[B] -> Link [LNKD] -> GSI 11 (level, low) -> IRQ 11
[ 11.062802] PCI: Setting latency timer of device 0000:00:1d.1 to 64
[ 11.062804] uhci_hcd 0000:00:1d.1: UHCI Host Controller
[ 11.062823] CLASS: registering class device: ID = 'usb_host3'
[ 11.062829] class_hotplug - name = usb_host3
[ 11.062831] class_device_create_hotplug called for usb_host3
[ 11.063050] uhci_hcd 0000:00:1d.1: new USB bus registered, assigned bus number 3
[ 11.063059] uhci_hcd 0000:00:1d.1: irq 11, io base 0x0000bf40
[ 11.063091] DEV: registering device: ID = 'usb3'
[ 11.063278] PM: Adding info for usb:usb3
[ 11.063281] bus usb: add device usb3
[ 11.063283] bound device 'usb3' to driver 'usb'
[ 11.063293] DEV: registering device: ID = '3-0:1.0'
[ 11.063496] PM: Adding info for usb:3-0:1.0
[ 11.063501] bus usb: add device 3-0:1.0
[ 11.063503] usb: Matched Device 3-0:1.0 with Driver hub
[ 11.063505] hub 3-0:1.0: USB hub found
[ 11.063515] hub 3-0:1.0: 2 ports detected
[ 11.163386] bound device '3-0:1.0' to driver 'hub'
[ 11.163388] usb: Bound Device 3-0:1.0 to Driver hub
[ 11.163397] CLASS: registering class device: ID = 'usbdev3.1'
[ 11.163402] class_hotplug - name = usbdev3.1
[ 11.163405] class_device_create_hotplug called for usbdev3.1
[ 11.163637] bound device '0000:00:1d.1' to driver 'uhci_hcd'
[ 11.163640] pci: Bound Device 0000:00:1d.1 to Driver uhci_hcd
[ 11.163642] pci: Matched Device 0000:00:1d.2 with Driver uhci_hcd
[ 11.164047] ACPI: PCI Interrupt Link [LNKC] enabled at IRQ 11
[ 11.164052] ACPI: PCI Interrupt 0000:00:1d.2[C] -> Link [LNKC] -> GSI 11 (level, low) -> IRQ 11
[ 11.164063] PCI: Setting latency timer of device 0000:00:1d.2 to 64
[ 11.164066] uhci_hcd 0000:00:1d.2: UHCI Host Controller
[ 11.164087] CLASS: registering class device: ID = 'usb_host4'
[ 11.164094] class_hotplug - name = usb_host4
[ 11.164096] class_device_create_hotplug called for usb_host4
[ 11.164336] uhci_hcd 0000:00:1d.2: new USB bus registered, assigned bus number 4
[ 11.164344] uhci_hcd 0000:00:1d.2: irq 11, io base 0x0000bf20
[ 11.164395] DEV: registering device: ID = 'usb4'
[ 11.164579] PM: Adding info for usb:usb4
[ 11.164583] bus usb: add device usb4
[ 11.164585] bound device 'usb4' to driver 'usb'
[ 11.164602] DEV: registering device: ID = '4-0:1.0'
[ 11.164815] PM: Adding info for usb:4-0:1.0
[ 11.164819] bus usb: add device 4-0:1.0
[ 11.164822] usb: Matched Device 4-0:1.0 with Driver hub
[ 11.164824] hub 4-0:1.0: USB hub found
[ 11.164833] hub 4-0:1.0: 2 ports detected
[ 11.265239] bound device '4-0:1.0' to driver 'hub'
[ 11.265241] usb: Bound Device 4-0:1.0 to Driver hub
[ 11.265250] CLASS: registering class device: ID = 'usbdev4.1'
[ 11.265255] class_hotplug - name = usbdev4.1
[ 11.265259] class_device_create_hotplug called for usbdev4.1
[ 11.265493] bound device '0000:00:1d.2' to driver 'uhci_hcd'
[ 11.265496] pci: Bound Device 0000:00:1d.2 to Driver uhci_hcd
[ 11.265507] bus usb: add driver hiddev
[ 11.265722] usbcore: registered new driver hiddev
[ 11.265728] bus usb: add driver usbhid
[ 11.265910] usbcore: registered new driver usbhid
[ 11.265913] drivers/usb/input/hid-core.c: v2.6:USB HID core driver
[ 11.265923] bus type 'usb-serial' registered
[ 11.265930] bus usb: add driver usbserial
[ 11.266112] usbcore: registered new driver usbserial
[ 11.266118] bus usb-serial: add driver generic
[ 11.266310] drivers/usb/serial/usb-serial.c: USB Serial support registered for generic
[ 11.266317] bus usb: add driver usbserial_generic
[ 11.266496] usbcore: registered new driver usbserial_generic
[ 11.266500] drivers/usb/serial/usb-serial.c: USB Serial Driver core
[ 11.266504] bus usb-serial: add driver pl2303
[ 11.266526] drivers/usb/serial/usb-serial.c: USB Serial support registered for pl2303
[ 11.266531] bus usb: add driver pl2303
[ 11.266871] usbcore: registered new driver pl2303
[ 11.266875] drivers/usb/serial/pl2303.c: Prolific PL2303 USB to serial adaptor driver
[ 11.266882] bus pci: add driver radeonfb
[ 11.266909] pci: Matched Device 0000:01:00.0 with Driver radeonfb
[ 11.266911] radeonfb_pci_register BEGIN
[ 11.266925] acpi_bus-0200 [01] bus_set_power : Device is not power manageable
[ 11.266937] ACPI: PCI Interrupt 0000:01:00.0[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 11.266956] radeonfb (0000:01:00.0): Found 131072k of DDR 128 bits wide videoram
[ 11.266982] radeonfb (0000:01:00.0): mapped 16384k videoram
[ 11.266988] radeonfb: Retreived PLL infos from BIOS
[ 11.266992] radeonfb: Reference=27.00 MHz (RefDiv=6) Memory=445.00 Mhz, System=263.00 MHz
[ 11.266996] radeonfb: PLL min 20000 max 35000
[ 11.267025] DEV: registering device: ID = 'i2c-0'
[ 11.267032] PM: Adding info for No Bus:i2c-0
[ 11.267036] CLASS: registering class device: ID = 'i2c-0'
[ 11.267042] class_hotplug - name = i2c-0
[ 11.267390] i2c_adapter i2c-0: adapter [monid] registered
[ 11.267415] DEV: registering device: ID = 'i2c-1'
[ 11.267419] PM: Adding info for No Bus:i2c-1
[ 11.267422] CLASS: registering class device: ID = 'i2c-1'
[ 11.267427] class_hotplug - name = i2c-1
[ 11.267448] i2c_adapter i2c-1: adapter [dvi] registered
[ 11.267472] DEV: registering device: ID = 'i2c-2'
[ 11.267476] PM: Adding info for No Bus:i2c-2
[ 11.267483] CLASS: registering class device: ID = 'i2c-2'
[ 11.267488] class_hotplug - name = i2c-2
[ 11.267826] i2c_adapter i2c-2: adapter [vga] registered
[ 11.267851] DEV: registering device: ID = 'i2c-3'
[ 11.267855] PM: Adding info for No Bus:i2c-3
[ 11.267858] CLASS: registering class device: ID = 'i2c-3'
[ 11.267863] class_hotplug - name = i2c-3
[ 11.267884] i2c_adapter i2c-3: adapter [crt2] registered
[ 11.267889] 1 chips in connector info
[ 11.267893] - chip 1 has 2 connectors
[ 11.267896] * connector 0 of type 3 (DVI-I) : 3201
[ 11.267899] * connector 1 of type 2 (CRT) : 2320
[ 11.267902] Starting monitor auto detection...
[ 11.340128] i2c_adapter i2c-0: master_xfer[0] W, addr=0x50, len=1
[ 11.340131] i2c_adapter i2c-0: master_xfer[1] R, addr=0x50, len=128
[ 11.460953] i2c_adapter i2c-0: master_xfer[0] W, addr=0x50, len=1
[ 11.460955] i2c_adapter i2c-0: master_xfer[1] R, addr=0x50, len=128
[ 11.581779] i2c_adapter i2c-0: master_xfer[0] W, addr=0x50, len=1
[ 11.581781] i2c_adapter i2c-0: master_xfer[1] R, addr=0x50, len=128
[ 11.629712] radeonfb: I2C (port 1) ... not found
[ 11.702605] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 11.702607] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 11.823430] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 11.823432] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 11.944256] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 11.944258] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 11.992189] radeonfb: I2C (port 2) ... not found
[ 12.065082] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 12.065084] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 12.185908] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 12.185910] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 12.306733] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 12.306735] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 12.354666] radeonfb: I2C (port 3) ... not found
[ 12.561368] radeonfb: I2C (port 4) ... not found
[ 12.634261] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 12.634263] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 12.755087] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 12.755089] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 12.875913] i2c_adapter i2c-1: master_xfer[0] W, addr=0x50, len=1
[ 12.875915] i2c_adapter i2c-1: master_xfer[1] R, addr=0x50, len=128
[ 12.923846] radeonfb: I2C (port 2) ... not found
[ 13.130547] radeonfb: I2C (port 4) ... not found
[ 13.130550] Non-DDC laptop panel detected
[ 13.203441] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 13.203443] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 13.324266] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 13.324268] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 13.445092] i2c_adapter i2c-2: master_xfer[0] W, addr=0x50, len=1
[ 13.445094] i2c_adapter i2c-2: master_xfer[1] R, addr=0x50, len=128
[ 13.493025] radeonfb: I2C (port 3) ... not found
[ 13.699726] radeonfb: I2C (port 4) ... not found
[ 13.700734] radeonfb: Monitor 1 type LCD found
[ 13.700737] radeonfb: Monitor 2 type no found
[ 13.700740] radeonfb: panel ID string: W4554171WU1
[ 13.700741]
[ 13.700745] radeonfb: detected LVDS panel size from BIOS: 1920x1200
[ 13.700748] BIOS provided panel power delay: 1000
[ 13.700751] radeondb: BIOS provided dividers will be used
[ 13.700753] ref_divider = 9
[ 13.700755] post_divider = 0
[ 13.700757] fbk_divider = 36
[ 13.700759] Scanning BIOS table ...
[ 13.700762] 320 x 350
[ 13.700764] 320 x 400
[ 13.700766] 320 x 400
[ 13.700768] 320 x 480
[ 13.700769] 400 x 600
[ 13.700771] 512 x 384
[ 13.700773] 640 x 350
[ 13.700775] 640 x 400
[ 13.700777] 640 x 475
[ 13.700779] 640 x 480
[ 13.700781] 720 x 480
[ 13.700783] 720 x 576
[ 13.700785] 800 x 600
[ 13.700787] 848 x 480
[ 13.700789] 1024 x 768
[ 13.700791] 1280 x 1024
[ 13.700793] 1280 x 768
[ 13.700796] 1280 x 800
[ 13.700797] 1440 x 900
[ 13.700800] 1600 x 1200
[ 13.700802] 1680 x 1050
[ 13.700803] 1920 x 1200
[ 13.700806] Found panel in BIOS table:
[ 13.700808] hblank: 240
[ 13.700810] hOver_plus: 48
[ 13.700812] hSync_width: 32
[ 13.700814] vblank: 50
[ 13.700816] vOver_plus: 1
[ 13.700817] vSync_width: 3
[ 13.700819] clock: 16200
[ 13.700822] Setting up default mode based on panel info
[ 13.700854] radeonfb: Dynamic Clock Power Management enabled
[ 13.700858] CLASS: registering class device: ID = 'fb0'
[ 13.700865] class_hotplug - name = fb0
[ 13.700868] class_device_create_hotplug called for fb0
[ 13.701141] hStart = 1968, hEnd = 2000, hTotal = 2160
[ 13.701143] vStart = 1201, vEnd = 1204, vTotal = 1250
[ 13.701145] h_total_disp = 0xef010d hsync_strt_wid = 0x407aa
[ 13.701147] v_total_disp = 0x4af04e1 vsync_strt_wid = 0x304b0
[ 13.701148] pixclock = 6172
[ 13.701149] freq = 16202
[ 13.788821] Console: switching to colour frame buffer device 240x75
[ 13.791062] radeonfb (0000:01:00.0): ATI Radeon NP
[ 13.791140] radeonfb_pci_register END
[ 13.791190] bound device '0000:01:00.0' to driver 'radeonfb'
[ 13.791196] pci: Bound Device 0000:01:00.0 to Driver radeonfb
[ 13.791207] bus platform: add driver vesafb
[ 13.791555] Registering platform device 'vesafb.0'. Parent at platform
[ 13.791557] DEV: registering device: ID = 'vesafb.0'
[ 13.791743] PM: Adding info for platform:vesafb.0
[ 13.791748] bus platform: add device vesafb.0
[ 13.791749] platform: Matched Device vesafb.0 with Driver vesafb
[ 13.791754] vesafb: cannot reserve video memory at 0xf0000000
[ 13.791834] vesafb: framebuffer at 0xf0000000, mapped to 0xf9900000, using 5120k, total 131008k
[ 13.791932] vesafb: mode is 1280x1024x16, linelength=2560, pages=50
[ 13.792010] vesafb: protected mode interface info at c000:5aa2
[ 13.792083] vesafb: scrolling: redraw
[ 13.792133] vesafb: Truecolor: size=0:5:6:5, shift=0:11:5:0
[ 13.792204] vesafb: Mode is VGA compatible
[ 13.792265] CLASS: registering class device: ID = 'fb1'
[ 13.792272] class_hotplug - name = fb1
[ 13.792275] class_device_create_hotplug called for fb1
[ 13.792481] fb1: VESA VGA frame buffer device
[ 13.792538] bound device 'vesafb.0' to driver 'vesafb'
[ 13.792541] platform: Bound Device vesafb.0 to Driver vesafb
[ 13.793000] ACPI: AC Adapter [AC] (on-line)
[ 14.074210] ACPI: Battery Slot [BAT0] (battery present)
[ 14.074294] ACPI: Lid Switch [LID]
[ 14.074350] ACPI: Power Button (CM) [PBTN]
[ 14.074410] ACPI: Sleep Button (CM) [SBTN]
[ 14.076230] ACPI: Video Device [VID] (multi-head: yes rom: no post: no)
[ 14.076396] Using specific hotkey driver
[ 14.076451] Registering sysdev class '<NULL>'
[ 14.076456] Registering sys device 'irqrouter0'
[ 14.077362] ACPI: CPU0 (power states: C1[C1] C2[C2] C3[C3] C4[C3])
[ 14.077544] ACPI: Processor [CPU0] (supports 8 throttling states)
[ 14.081717] ACPI: Thermal Zone [THM] (49 C)
[ 14.081794] CLASS: registering class device: ID = 'tty'
[ 14.081803] class_hotplug - name = tty
[ 14.081805] class_device_create_hotplug called for tty
[ 14.081999] CLASS: registering class device: ID = 'console'
[ 14.082004] class_hotplug - name = console
[ 14.082006] class_device_create_hotplug called for console
[ 14.082200] CLASS: registering class device: ID = 'ptmx'
[ 14.082205] class_hotplug - name = ptmx
[ 14.082206] class_device_create_hotplug called for ptmx
[ 14.082391] CLASS: registering class device: ID = 'tty0'
[ 14.082396] class_hotplug - name = tty0
[ 14.082397] class_device_create_hotplug called for tty0
[ 14.082581] device class 'vc': registering
[ 14.082586] CLASS: registering class device: ID = 'vcs'
[ 14.082590] class_hotplug - name = vcs
[ 14.082592] class_device_create_hotplug called for vcs
[ 14.082779] CLASS: registering class device: ID = 'vcsa'
[ 14.082784] class_hotplug - name = vcsa
[ 14.082785] class_device_create_hotplug called for vcsa
[ 14.082972] CLASS: registering class device: ID = 'tty1'
[ 14.082977] class_hotplug - name = tty1
[ 14.082978] class_device_create_hotplug called for tty1
[ 14.083156] CLASS: registering class device: ID = 'tty2'
[ 14.083160] class_hotplug - name = tty2
[ 14.083162] class_device_create_hotplug called for tty2
[ 14.083354] CLASS: registering class device: ID = 'tty3'
[ 14.083359] class_hotplug - name = tty3
[ 14.083360] class_device_create_hotplug called for tty3
[ 14.083543] CLASS: registering class device: ID = 'tty4'
[ 14.083547] class_hotplug - name = tty4
[ 14.083549] class_device_create_hotplug called for tty4
[ 14.083734] CLASS: registering class device: ID = 'tty5'
[ 14.083739] class_hotplug - name = tty5
[ 14.083741] class_device_create_hotplug called for tty5
[ 14.083931] CLASS: registering class device: ID = 'tty6'
[ 14.083939] class_hotplug - name = tty6
[ 14.083941] class_device_create_hotplug called for tty6
[ 14.084136] CLASS: registering class device: ID = 'tty7'
[ 14.084140] class_hotplug - name = tty7
[ 14.084142] class_device_create_hotplug called for tty7
[ 14.084331] CLASS: registering class device: ID = 'tty8'
[ 14.084335] class_hotplug - name = tty8
[ 14.084337] class_device_create_hotplug called for tty8
[ 14.084525] CLASS: registering class device: ID = 'tty9'
[ 14.084529] class_hotplug - name = tty9
[ 14.084531] class_device_create_hotplug called for tty9
[ 14.084713] CLASS: registering class device: ID = 'tty10'
[ 14.084717] class_hotplug - name = tty10
[ 14.084719] class_device_create_hotplug called for tty10
[ 14.084909] CLASS: registering class device: ID = 'tty11'
[ 14.084913] class_hotplug - name = tty11
[ 14.084915] class_device_create_hotplug called for tty11
[ 14.085132] CLASS: registering class device: ID = 'tty12'
[ 14.085138] class_hotplug - name = tty12
[ 14.085140] class_device_create_hotplug called for tty12
[ 14.085337] CLASS: registering class device: ID = 'tty13'
[ 14.085341] class_hotplug - name = tty13
[ 14.085343] class_device_create_hotplug called for tty13
[ 14.085530] CLASS: registering class device: ID = 'tty14'
[ 14.085536] class_hotplug - name = tty14
[ 14.085538] class_device_create_hotplug called for tty14
[ 14.085733] CLASS: registering class device: ID = 'tty15'
[ 14.085739] class_hotplug - name = tty15
[ 14.085741] class_device_create_hotplug called for tty15
[ 14.085929] CLASS: registering class device: ID = 'tty16'
[ 14.085934] class_hotplug - name = tty16
[ 14.085935] class_device_create_hotplug called for tty16
[ 14.086152] CLASS: registering class device: ID = 'tty17'
[ 14.086157] class_hotplug - name = tty17
[ 14.086158] class_device_create_hotplug called for tty17
[ 14.086368] CLASS: registering class device: ID = 'tty18'
[ 14.086378] class_hotplug - name = tty18
[ 14.086380] class_device_create_hotplug called for tty18
[ 14.086573] CLASS: registering class device: ID = 'tty19'
[ 14.086577] class_hotplug - name = tty19
[ 14.086579] class_device_create_hotplug called for tty19
[ 14.086773] CLASS: registering class device: ID = 'tty20'
[ 14.086780] class_hotplug - name = tty20
[ 14.086782] class_device_create_hotplug called for tty20
[ 14.086984] CLASS: registering class device: ID = 'tty21'
[ 14.086989] class_hotplug - name = tty21
[ 14.086991] class_device_create_hotplug called for tty21
[ 14.087233] CLASS: registering class device: ID = 'tty22'
[ 14.087238] class_hotplug - name = tty22
[ 14.087240] class_device_create_hotplug called for tty22
[ 14.087437] CLASS: registering class device: ID = 'tty23'
[ 14.087443] class_hotplug - name = tty23
[ 14.087444] class_device_create_hotplug called for tty23
[ 14.087654] CLASS: registering class device: ID = 'tty24'
[ 14.087660] class_hotplug - name = tty24
[ 14.087662] class_device_create_hotplug called for tty24
[ 14.087879] CLASS: registering class device: ID = 'tty25'
[ 14.087886] class_hotplug - name = tty25
[ 14.087888] class_device_create_hotplug called for tty25
[ 14.088117] CLASS: registering class device: ID = 'tty26'
[ 14.088124] class_hotplug - name = tty26
[ 14.088126] class_device_create_hotplug called for tty26
[ 14.088351] CLASS: registering class device: ID = 'tty27'
[ 14.088357] class_hotplug - name = tty27
[ 14.088358] class_device_create_hotplug called for tty27
[ 14.088571] CLASS: registering class device: ID = 'tty28'
[ 14.088577] class_hotplug - name = tty28
[ 14.088579] class_device_create_hotplug called for tty28
[ 14.088774] CLASS: registering class device: ID = 'tty29'
[ 14.088782] class_hotplug - name = tty29
[ 14.088783] class_device_create_hotplug called for tty29
[ 14.088994] CLASS: registering class device: ID = 'tty30'
[ 14.089004] class_hotplug - name = tty30
[ 14.089006] class_device_create_hotplug called for tty30
[ 14.089244] CLASS: registering class device: ID = 'tty31'
[ 14.089252] class_hotplug - name = tty31
[ 14.089254] class_device_create_hotplug called for tty31
[ 14.089474] CLASS: registering class device: ID = 'tty32'
[ 14.089481] class_hotplug - name = tty32
[ 14.089483] class_device_create_hotplug called for tty32
[ 14.089693] CLASS: registering class device: ID = 'tty33'
[ 14.089699] class_hotplug - name = tty33
[ 14.089701] class_device_create_hotplug called for tty33
[ 14.089928] CLASS: registering class device: ID = 'tty34'
[ 14.089935] class_hotplug - name = tty34
[ 14.089937] class_device_create_hotplug called for tty34
[ 14.090180] CLASS: registering class device: ID = 'tty35'
[ 14.090187] class_hotplug - name = tty35
[ 14.090189] class_device_create_hotplug called for tty35
[ 14.090406] CLASS: registering class device: ID = 'tty36'
[ 14.090412] class_hotplug - name = tty36
[ 14.090413] class_device_create_hotplug called for tty36
[ 14.090649] CLASS: registering class device: ID = 'tty37'
[ 14.090655] class_hotplug - name = tty37
[ 14.090657] class_device_create_hotplug called for tty37
[ 14.090884] CLASS: registering class device: ID = 'tty38'
[ 14.090891] class_hotplug - name = tty38
[ 14.090893] class_device_create_hotplug called for tty38
[ 14.091116] CLASS: registering class device: ID = 'tty39'
[ 14.091124] class_hotplug - name = tty39
[ 14.091126] class_device_create_hotplug called for tty39
[ 14.091367] CLASS: registering class device: ID = 'tty40'
[ 14.091374] class_hotplug - name = tty40
[ 14.091376] class_device_create_hotplug called for tty40
[ 14.091615] CLASS: registering class device: ID = 'tty41'
[ 14.091622] class_hotplug - name = tty41
[ 14.091624] class_device_create_hotplug called for tty41
[ 14.091846] CLASS: registering class device: ID = 'tty42'
[ 14.091859] class_hotplug - name = tty42
[ 14.091861] class_device_create_hotplug called for tty42
[ 14.092115] CLASS: registering class device: ID = 'tty43'
[ 14.092129] class_hotplug - name = tty43
[ 14.092131] class_device_create_hotplug called for tty43
[ 14.092366] CLASS: registering class device: ID = 'tty44'
[ 14.092375] class_hotplug - name = tty44
[ 14.092377] class_device_create_hotplug called for tty44
[ 14.092606] CLASS: registering class device: ID = 'tty45'
[ 14.092615] class_hotplug - name = tty45
[ 14.092617] class_device_create_hotplug called for tty45
[ 14.092845] CLASS: registering class device: ID = 'tty46'
[ 14.092853] class_hotplug - name = tty46
[ 14.092855] class_device_create_hotplug called for tty46
[ 14.093085] CLASS: registering class device: ID = 'tty47'
[ 14.093093] class_hotplug - name = tty47
[ 14.093095] class_device_create_hotplug called for tty47
[ 14.093331] CLASS: registering class device: ID = 'tty48'
[ 14.093338] class_hotplug - name = tty48
[ 14.093340] class_device_create_hotplug called for tty48
[ 14.093564] CLASS: registering class device: ID = 'tty49'
[ 14.093571] class_hotplug - name = tty49
[ 14.093572] class_device_create_hotplug called for tty49
[ 14.093809] CLASS: registering class device: ID = 'tty50'
[ 14.093817] class_hotplug - name = tty50
[ 14.093819] class_device_create_hotplug called for tty50
[ 14.094050] CLASS: registering class device: ID = 'tty51'
[ 14.094058] class_hotplug - name = tty51
[ 14.094060] class_device_create_hotplug called for tty51
[ 14.094306] CLASS: registering class device: ID = 'tty52'
[ 14.094315] class_hotplug - name = tty52
[ 14.094317] class_device_create_hotplug called for tty52
[ 14.094547] CLASS: registering class device: ID = 'tty53'
[ 14.094555] class_hotplug - name = tty53
[ 14.094557] class_device_create_hotplug called for tty53
[ 14.094788] CLASS: registering class device: ID = 'tty54'
[ 14.094802] class_hotplug - name = tty54
[ 14.094804] class_device_create_hotplug called for tty54
[ 14.095045] CLASS: registering class device: ID = 'tty55'
[ 14.095053] class_hotplug - name = tty55
[ 14.095055] class_device_create_hotplug called for tty55
[ 14.095303] CLASS: registering class device: ID = 'tty56'
[ 14.095310] class_hotplug - name = tty56
[ 14.095312] class_device_create_hotplug called for tty56
[ 14.095534] CLASS: registering class device: ID = 'tty57'
[ 14.095543] class_hotplug - name = tty57
[ 14.095545] class_device_create_hotplug called for tty57
[ 14.095783] CLASS: registering class device: ID = 'tty58'
[ 14.095793] class_hotplug - name = tty58
[ 14.095795] class_device_create_hotplug called for tty58
[ 14.096016] CLASS: registering class device: ID = 'tty59'
[ 14.096025] class_hotplug - name = tty59
[ 14.096026] class_device_create_hotplug called for tty59
[ 14.096279] CLASS: registering class device: ID = 'tty60'
[ 14.096288] class_hotplug - name = tty60
[ 14.096290] class_device_create_hotplug called for tty60
[ 14.096511] CLASS: registering class device: ID = 'tty61'
[ 14.096521] class_hotplug - name = tty61
[ 14.096523] class_device_create_hotplug called for tty61
[ 14.096757] CLASS: registering class device: ID = 'tty62'
[ 14.096769] class_hotplug - name = tty62
[ 14.096771] class_device_create_hotplug called for tty62
[ 14.096998] CLASS: registering class device: ID = 'tty63'
[ 14.097009] class_hotplug - name = tty63
[ 14.097010] class_device_create_hotplug called for tty63
[ 14.097262] device class 'printer': registering
[ 14.097427] lp: driver loaded but no devices found
[ 14.097516] CLASS: registering class device: ID = 'rtc'
[ 14.097525] class_hotplug - name = rtc
[ 14.097527] class_device_create_hotplug called for rtc
[ 14.097737] Real Time Clock Driver v1.12
[ 14.097796] CLASS: registering class device: ID = 'hpet'
[ 14.097808] class_hotplug - name = hpet
[ 14.097810] class_device_create_hotplug called for hpet
[ 14.098048] CLASS: registering class device: ID = 'watchdog'
[ 14.098055] class_hotplug - name = watchdog
[ 14.098056] class_device_create_hotplug called for watchdog
[ 14.098310] Software Watchdog Timer: 0.07 initialized. soft_noboot=0 soft_margin=60 sec (nowayout= 0)
[ 14.098418] Linux agpgart interface v0.101 (c) Dave Jones
[ 14.098489] bus pci: add driver agpgart-ati
[ 14.098748] device class 'drm': registering
[ 14.098762] [drm] Initialized drm 1.0.0 20040925
[ 14.098826] Hangcheck: starting hangcheck timer 0.9.0 (tick is 180 seconds, margin is 60 seconds).
[ 14.098932] Hangcheck: Using monotonic_clock().
[ 14.099010] bus type 'serio' registered
[ 14.099014] bus pnp: add driver i8042 kbd
[ 14.099257] pnp: Matched Device 00:04 with Driver i8042 kbd
[ 14.099261] bound device '00:04' to driver 'i8042 kbd'
[ 14.099264] pnp: Bound Device 00:04 to Driver i8042 kbd
[ 14.099271] bus pnp: add driver i8042 aux
[ 14.099454] pnp: Matched Device 00:03 with Driver i8042 aux
[ 14.099458] bound device '00:03' to driver 'i8042 aux'
[ 14.099461] pnp: Bound Device 00:03 to Driver i8042 aux
[ 14.099469] PNP: PS/2 Controller [PNP0303:KBC,PNP0f13:PS2M] at 0x60,0x64 irq 1,12
[ 14.099937] bus platform: add driver i8042
[ 14.100131] Registering platform device 'i8042'. Parent at platform
[ 14.100133] DEV: registering device: ID = 'i8042'
[ 14.100179] PM: Adding info for platform:i8042
[ 14.100186] bus platform: add device i8042
[ 14.100188] platform: Matched Device i8042 with Driver i8042
[ 14.100190] bound device 'i8042' to driver 'i8042'
[ 14.100192] platform: Bound Device i8042 to Driver i8042
[ 14.103158] serio: i8042 AUX port at 0x60,0x64 irq 12
[ 14.103224] DEV: registering device: ID = 'serio0'
[ 14.103604] serio: i8042 KBD port at 0x60,0x64 irq 1
[ 14.103673] Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, IRQ sharing enabled
[ 14.103773] Registering platform device 'serial8250'. Parent at platform
[ 14.103775] DEV: registering device: ID = 'serial8250'
[ 14.103966] PM: Adding info for serio:serio0
[ 14.103970] bus serio: add device serio0
[ 14.103975] DEV: registering device: ID = 'serio1'
[ 14.104202] PM: Adding info for platform:serial8250
[ 14.104206] bus platform: add device serial8250
[ 14.104223] CLASS: registering class device: ID = 'ttyS0'
[ 14.104236] class_hotplug - name = ttyS0
[ 14.104239] class_device_create_hotplug called for ttyS0
[ 14.104435] PM: Adding info for serio:serio1
[ 14.104439] bus serio: add device serio1
[ 14.104651] CLASS: registering class device: ID = 'ttyS1'
[ 14.104661] class_hotplug - name = ttyS1
[ 14.104664] class_device_create_hotplug called for ttyS1
[ 14.104902] CLASS: registering class device: ID = 'ttyS2'
[ 14.104913] class_hotplug - name = ttyS2
[ 14.104915] class_device_create_hotplug called for ttyS2
[ 14.105165] CLASS: registering class device: ID = 'ttyS3'
[ 14.105176] class_hotplug - name = ttyS3
[ 14.105180] class_device_create_hotplug called for ttyS3
[ 14.105411] bus platform: add driver serial8250
[ 14.105640] platform: Matched Device serial8250 with Driver serial8250
[ 14.105643] bound device 'serial8250' to driver 'serial8250'
[ 14.105645] platform: Bound Device serial8250 to Driver serial8250
[ 14.105649] bus pnp: add driver serial
[ 14.106112] bus pci: add driver serial
[ 14.106159] pci: Matched Device 0000:00:1f.6 with Driver serial
[ 14.106179] acpi_bus-0200 [07] bus_set_power : Device is not power manageable
[ 14.106770] ACPI: PCI Interrupt Link [LNKB] enabled at IRQ 7
[ 14.106850] PCI: setting IRQ 7 as level-triggered
[ 14.106914] ACPI: PCI Interrupt 0000:00:1f.6[B] -> Link [LNKB] -> GSI 7 (level, low) -> IRQ 7
[ 14.107059] ACPI: PCI interrupt for device 0000:00:1f.6 disabled
[ 14.107163] bus pnp: add driver parport_pc
[ 14.107598] bus pci: add driver parport_pc
[ 14.107625] device class 'firmware': registering
[ 14.111111] RAMDISK driver initialized: 16 RAM disks of 16384K size 1024 blocksize
[ 14.111262] CLASS: registering class device: ID = 'lo'
[ 14.111269] class_hotplug - name = lo
[ 14.111526] bus pci: add driver b44
[ 14.111753] pci: Matched Device 0000:02:00.0 with Driver b44
[ 14.111756] b44.c:v0.96 (Nov 8, 2005)
[ 14.111838] ACPI: PCI Interrupt 0000:02:00.0[A] -> Link [LNKC] -> GSI 11 (level, low) -> IRQ 11
[ 14.120362] CLASS: registering class device: ID = 'eth0'
[ 14.120371] class_hotplug - name = eth0
[ 14.120813] eth0: Broadcom 4400 10/100BaseT Ethernet 00:11:43:5e:b3:ef
[ 14.126145] bound device '0000:02:00.0' to driver 'b44'
[ 14.126149] pci: Bound Device 0000:02:00.0 to Driver b44
[ 14.126155] netconsole: not configured, aborting
[ 14.131438] Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2
[ 14.136997] ide: Assuming 33MHz system bus speed for PIO modes; override with idebus=xx
[ 14.142621] bus type 'ide' registered
[ 14.142647] ICH4: IDE controller at PCI slot 0000:00:1f.1
[ 14.148242] PCI: Enabling device 0000:00:1f.1 (0005 -> 0007)
[ 14.153828] ACPI: PCI Interrupt 0000:00:1f.1[A] -> Link [LNKA] -> GSI 11 (level, low) -> IRQ 11
[ 14.159691] ICH4: chipset revision 1
[ 14.165567] ICH4: not 100% native mode: will probe irqs later
[ 14.171525] ide0: BM-DMA at 0xbfa0-0xbfa7, BIOS settings: hda:DMA, hdb:pio
[ 14.177573] ide1: BM-DMA at 0xbfa8-0xbfaf, BIOS settings: hdc:DMA, hdd:pio
[ 14.183639] Probing IDE interface ide0...
[ 14.357130] Falling back to C3 safe TSC
[ 14.424299] check_periodic_interval: Long interval! 190982348.
[ 14.430431] Something may be blocking interrupts.
[ 14.471403] hda: HTS726060M9AT00, ATA DISK drive
[ 14.477582] DEV: registering device: ID = 'ide0'
[ 14.477591] PM: Adding info for No Bus:ide0
[ 15.116214] ide0 at 0x1f0-0x1f7,0x3f6 on irq 14
[ 15.122429] DEV: registering device: ID = '0.0'
[ 15.122735] PM: Adding info for ide:0.0
[ 15.122739] bus ide: add device 0.0
[ 15.122744] Probing IDE interface ide1...
[ 15.825205] hdc: _NEC DVD+/-RW ND-6500A, ATAPI CD/DVD-ROM drive
[ 15.831543] DEV: registering device: ID = 'ide1'
[ 15.831548] PM: Adding info for No Bus:ide1
[ 16.486315] ide1 at 0x170-0x177,0x376 on irq 15
[ 16.492932] DEV: registering device: ID = '1.0'
[ 16.493223] PM: Adding info for ide:1.0
[ 16.493229] bus ide: add device 1.0
[ 16.493250] bus pci: add driver PIIX_IDE
[ 16.493450] pci: Matched Device 0000:00:1f.1 with Driver PIIX_IDE
[ 16.493453] bound device '0000:00:1f.1' to driver 'PIIX_IDE'
[ 16.493458] pci: Bound Device 0000:00:1f.1 to Driver PIIX_IDE
[ 16.493463] bus pci: add driver PCI_IDE
[ 16.493642] bus pnp: add driver ide
[ 16.493832] bus ide: add driver ide-disk
[ 16.494016] ide: Matched Device 0.0 with Driver ide-disk
[ 16.494031] hda: max request size: 1024KiB
[ 16.514499] hda: 117210240 sectors (60011 MB) w/7877KiB Cache, CHS=16383/255/63, UDMA(100)
[ 16.521373] hda: cache flushes supported
[ 16.528108] hda: hda1 hda2 hda3
[ 16.543177] bound device '0.0' to driver 'ide-disk'
[ 16.543180] ide: Bound Device 0.0 to Driver ide-disk
[ 16.543182] ide: Matched Device 1.0 with Driver ide-disk
[ 16.543185] bus ide: add driver ide-cdrom
[ 16.543394] ide: Matched Device 1.0 with Driver ide-cdrom
[ 16.561013] hdc: ATAPI 24X DVD-ROM DVD-R CD-R/RW drive, 2048kB Cache, UDMA(33)
[ 16.567759] Uniform CD-ROM driver Revision: 3.20
[ 16.600271] bound device '1.0' to driver 'ide-cdrom'
[ 16.600274] ide: Bound Device 1.0 to Driver ide-cdrom
[ 16.600288] bus type 'ieee1394' registered
[ 16.600291] device class 'ieee1394_host': registering
[ 16.600296] device class 'ieee1394_protocol': registering
[ 16.600303] device class 'ieee1394_node': registering
[ 16.600306] device class 'ieee1394': registering
[ 16.600310] bus pci: add driver ohci1394
[ 16.600513] pci: Matched Device 0000:02:01.1 with Driver ohci1394
[ 16.600516] ohci1394: $Rev: 1313 $ Ben Collins <[email protected]>
[ 16.607174] acpi_bus-0200 [07] bus_set_power : Device is not power manageable
[ 16.613933] ACPI: PCI Interrupt 0000:02:01.1[B] -> Link [LNKC] -> GSI 11 (level, low) -> IRQ 11
[ 16.620759] DEV: registering device: ID = 'fw-host0'
[ 16.620953] PM: Adding info for ieee1394:fw-host0
[ 16.620957] bus ieee1394: add device fw-host0
[ 16.620961] CLASS: registering class device: ID = 'fw-host0'
[ 16.620966] class_hotplug - name = fw-host0
[ 16.674249] ohci1394: fw-host0: OHCI-1394 1.1 (PCI): IRQ=[11] MMIO=[faffd800-faffdfff] Max Packet=[2048]
[ 16.688155] bound device '0000:02:01.1' to driver 'ohci1394'
[ 16.688158] pci: Bound Device 0000:02:01.1 to Driver ohci1394
[ 16.688166] CLASS: registering class device: ID = 'video1394-0'
[ 16.688174] class_hotplug - name = video1394-0
[ 16.688176] class_device_create_hotplug called for video1394-0
[ 16.688383] bus ieee1394: add driver video1394
[ 16.688586] video1394: Installed video1394 module
[ 16.695770] bus pci: add driver yenta_cardbus
[ 16.695964] pci: Matched Device 0000:02:01.0 with Driver yenta_cardbus
[ 16.695978] ACPI: PCI Interrupt 0000:02:01.0[A] -> Link [LNKD] -> GSI 11 (level, low) -> IRQ 11
[ 16.703004] Yenta: CardBus bridge found at 0000:02:01.0 [1028:01a2]
[ 16.835509] Yenta: ISA IRQ mask 0x0438, PCI irq 11
[ 16.842535] Socket status: 30000006
[ 16.849551] pcmcia: parent PCI bridge I/O window: 0xe000 - 0xefff
[ 16.856572] pcmcia: parent PCI bridge Memory window: 0xfa000000 - 0xfbffffff
[ 16.863817] pcmcia: parent PCI bridge Memory window: 0x70000000 - 0x71ffffff
[ 16.870841] CLASS: registering class device: ID = 'pcmcia_socket0'
[ 16.870849] class_hotplug - name = pcmcia_socket0
[ 17.132060] bound device '0000:02:01.0' to driver 'yenta_cardbus'
[ 17.132062] pci: Bound Device 0000:02:01.0 to Driver yenta_cardbus
[ 17.132068] CLASS: registering class device: ID = 'mice'
[ 17.132075] class_hotplug - name = mice
[ 17.132077] class_device_create_hotplug called for mice
[ 17.132246] mice: PS/2 mouse device common for all mice
[ 17.139212] bus serio: add driver atkbd
[ 17.139238] serio: Matched Device serio0 with Driver atkbd
[ 17.140008] input: PC Speaker as /class/input/input0
[ 17.146986] CLASS: registering class device: ID = 'input0'
[ 17.146991] class_hotplug - name = input0
[ 17.147179] CLASS: registering class device: ID = 'event0'
[ 17.147188] class_hotplug - name = event0
[ 17.147190] class_device_create_hotplug called for event0
[ 17.147455] serio: Matched Device serio1 with Driver atkbd
[ 17.147986] I2O subsystem v1.288
[ 17.154929] device class 'i2o_controller': registering
[ 17.154935] i2o: max drivers = 8
[ 17.161877] bus type 'i2o' registered
[ 17.161895] bus i2o: add driver exec-osm
[ 17.162108] bus pci: add driver PCI_I2O
[ 17.162291] I2O ProcFS OSM v1.145
[ 17.169234] bus i2o: add driver proc-osm
[ 17.169413] i2c /dev entries driver
[ 17.176346] device class 'i2c-dev': registering
[ 17.176351] bus i2c: add driver dev_driver
[ 17.176530] i2c-core: driver [dev_driver] registered
[ 17.176533] i2c-dev: adapter [monid] registered as minor 0
[ 17.176535] CLASS: registering class device: ID = 'i2c-0'
[ 17.176544] class_hotplug - name = i2c-0
[ 17.176725] i2c-dev: adapter [dvi] registered as minor 1
[ 17.176727] CLASS: registering class device: ID = 'i2c-1'
[ 17.176732] class_hotplug - name = i2c-1
[ 17.176948] i2c-dev: adapter [vga] registered as minor 2
[ 17.176951] CLASS: registering class device: ID = 'i2c-2'
[ 17.176961] class_hotplug - name = i2c-2
[ 17.177172] i2c-dev: adapter [crt2] registered as minor 3
[ 17.177174] CLASS: registering class device: ID = 'i2c-3'
[ 17.177180] class_hotplug - name = i2c-3
[ 17.177369] device class 'hwmon': registering
[ 17.177852] input: AT Translated Set 2 keyboard as /class/input/input1
[ 17.184861] CLASS: registering class device: ID = 'input1'
[ 17.184866] class_hotplug - name = input1
[ 17.185242] CLASS: registering class device: ID = 'event1'
[ 17.185248] class_hotplug - name = event1
[ 17.185251] class_device_create_hotplug called for event1
[ 17.185472] bound device 'serio1' to driver 'atkbd'
[ 17.185476] serio: Bound Device serio1 to Driver atkbd
[ 17.185481] bus serio: add driver psmouse
[ 17.185681] serio: Matched Device serio0 with Driver psmouse
[ 17.207354] device class 'sound': registering
[ 17.207372] bus platform: add driver snd_generic
[ 17.207558] Advanced Linux Sound Architecture Driver Version 1.0.10rc3 (Mon Nov 07 13:30:21 2005 UTC).
[ 17.214819] CLASS: registering class device: ID = 'timer'
[ 17.214823] class_hotplug - name = timer
[ 17.214825] class_device_create_hotplug called for timer
[ 17.215004] CLASS: registering class device: ID = 'sequencer'
[ 17.215009] class_hotplug - name = sequencer
[ 17.215011] class_device_create_hotplug called for sequencer
[ 17.215331] CLASS: registering class device: ID = 'sequencer2'
[ 17.215341] class_hotplug - name = sequencer2
[ 17.215343] class_device_create_hotplug called for sequencer2
[ 17.215767] CLASS: registering class device: ID = 'seq'
[ 17.215773] class_hotplug - name = seq
[ 17.215775] class_device_create_hotplug called for seq
[ 17.215806] bus pci: add driver Intel ICH
[ 17.215995] pci: Matched Device 0000:00:1f.5 with Driver Intel ICH
[ 17.216018] acpi_bus-0200 [07] bus_set_power : Device is not power manageable
[ 17.223439] ACPI: PCI Interrupt 0000:00:1f.5[B] -> Link [LNKB] -> GSI 7 (level, low) -> IRQ 7
[ 17.230884] PCI: Setting latency timer of device 0000:00:1f.5 to 64
[ 17.353923] input: PS/2 Generic Mouse as /class/input/input2
[ 17.361137] CLASS: registering class device: ID = 'input2'
[ 17.361147] class_hotplug - name = input2
[ 17.361369] CLASS: registering class device: ID = 'mouse0'
[ 17.361375] class_hotplug - name = mouse0
[ 17.361378] class_device_create_hotplug called for mouse0
[ 17.361580] CLASS: registering class device: ID = 'event2'
[ 17.361586] class_hotplug - name = event2
[ 17.361589] class_device_create_hotplug called for event2
[ 17.364899] bound device 'serio0' to driver 'psmouse'
[ 17.364902] serio: Bound Device serio0 to Driver psmouse
[ 18.076835] intel8x0_measure_ac97_clock: measured 50274 usecs
[ 18.084049] intel8x0: clocking to 48000
[ 18.091244] CLASS: registering class device: ID = 'pcmC0D4p'
[ 18.091255] class_hotplug - name = pcmC0D4p
[ 18.091259] class_device_create_hotplug called for pcmC0D4p
[ 18.091544] CLASS: registering class device: ID = 'pcmC0D3c'
[ 18.091551] class_hotplug - name = pcmC0D3c
[ 18.091554] class_device_create_hotplug called for pcmC0D3c
[ 18.091765] CLASS: registering class device: ID = 'pcmC0D2c'
[ 18.091774] class_hotplug - name = pcmC0D2c
[ 18.091777] class_device_create_hotplug called for pcmC0D2c
[ 18.091979] CLASS: registering class device: ID = 'pcmC0D1c'
[ 18.091985] class_hotplug - name = pcmC0D1c
[ 18.091988] class_device_create_hotplug called for pcmC0D1c
[ 18.092189] CLASS: registering class device: ID = 'adsp'
[ 18.092200] class_hotplug - name = adsp
[ 18.092203] class_device_create_hotplug called for adsp
[ 18.092400] CLASS: registering class device: ID = 'pcmC0D0p'
[ 18.092406] class_hotplug - name = pcmC0D0p
[ 18.092409] class_device_create_hotplug called for pcmC0D0p
[ 18.092614] CLASS: registering class device: ID = 'pcmC0D0c'
[ 18.092620] class_hotplug - name = pcmC0D0c
[ 18.092623] class_device_create_hotplug called for pcmC0D0c
[ 18.092837] CLASS: registering class device: ID = 'dsp'
[ 18.092843] class_hotplug - name = dsp
[ 18.092846] class_device_create_hotplug called for dsp
[ 18.093050] CLASS: registering class device: ID = 'audio'
[ 18.093058] class_hotplug - name = audio
[ 18.093061] class_device_create_hotplug called for audio
[ 18.093269] DEV: registering device: ID = 'card0-0'
[ 18.093484] PM: Adding info for ac97:card0-0
[ 18.093491] bus ac97: add device card0-0
[ 18.093499] CLASS: registering class device: ID = 'controlC0'
[ 18.093504] class_hotplug - name = controlC0
[ 18.093508] class_device_create_hotplug called for controlC0
[ 18.093725] CLASS: registering class device: ID = 'mixer'
[ 18.093734] class_hotplug - name = mixer
[ 18.093737] class_device_create_hotplug called for mixer
[ 18.094209] bound device '0000:00:1f.5' to driver 'Intel ICH'
[ 18.094213] pci: Bound Device 0000:00:1f.5 to Driver Intel ICH
[ 18.094223] bus pci: add driver Intel ICH Modem
[ 18.094455] pci: Matched Device 0000:00:1f.6 with Driver Intel ICH Modem
[ 18.094480] ACPI: PCI Interrupt 0000:00:1f.6[B] -> Link [LNKB] -> GSI 7 (level, low) -> IRQ 7
[ 18.101895] PCI: Setting latency timer of device 0000:00:1f.6 to 64
[ 18.119930] DEV: registering device: ID = '444fc000221b90c1'
[ 18.120211] PM: Adding info for ieee1394:444fc000221b90c1
[ 18.120218] bus ieee1394: add device 444fc000221b90c1
[ 18.120223] CLASS: registering class device: ID = '444fc000221b90c1'
[ 18.120235] class_hotplug - name = 444fc000221b90c1
[ 18.120454] ieee1394: Host added: ID:BUS[0-00:1023] GUID[444fc000221b90c1]
[ 18.205718] MC'97 1 converters and GPIO not ready (0xff00)
[ 18.213242] CLASS: registering class device: ID = 'pcmC1D0p'
[ 18.213252] class_hotplug - name = pcmC1D0p
[ 18.213255] class_device_create_hotplug called for pcmC1D0p
[ 18.213851] CLASS: registering class device: ID = 'pcmC1D0c'
[ 18.213857] class_hotplug - name = pcmC1D0c
[ 18.213861] class_device_create_hotplug called for pcmC1D0c
[ 18.214063] CLASS: registering class device: ID = 'dsp1'
[ 18.214071] class_hotplug - name = dsp1
[ 18.214074] class_device_create_hotplug called for dsp1
[ 18.214277] CLASS: registering class device: ID = 'audio1'
[ 18.214283] class_hotplug - name = audio1
[ 18.214286] class_device_create_hotplug called for audio1
[ 18.214493] DEV: registering device: ID = 'card1-1'
[ 18.214698] PM: Adding info for ac97:card1-1
[ 18.214703] bus ac97: add device card1-1
[ 18.214709] CLASS: registering class device: ID = 'controlC1'
[ 18.214714] class_hotplug - name = controlC1
[ 18.214718] class_device_create_hotplug called for controlC1
[ 18.214934] CLASS: registering class device: ID = 'mixer1'
[ 18.214942] class_hotplug - name = mixer1
[ 18.214945] class_device_create_hotplug called for mixer1
[ 18.215242] bound device '0000:00:1f.6' to driver 'Intel ICH Modem'
[ 18.215246] pci: Bound Device 0000:00:1f.6 to Driver Intel ICH Modem
[ 18.215254] ALSA device list:
[ 18.222616] #0: Intel 82801DB-ICH4 with STAC9750,51 at 0xf8fff800, irq 7
[ 18.229994] #1: Intel 82801DB-ICH4 Modem at 0xd400, irq 7
[ 18.237315] Registering sysdev class '<NULL>'
[ 18.237321] Registering sys device 'oprofile0'
[ 18.237543] oprofile: using NMI interrupt.
[ 18.244888] NET: Registered protocol family 2
[ 18.262485] IP route cache hash table entries: 65536 (order: 6, 262144 bytes)
[ 18.270401] TCP established hash table entries: 262144 (order: 8, 1048576 bytes)
[ 18.278825] TCP bind hash table entries: 65536 (order: 6, 262144 bytes)
[ 18.286398] TCP: Hash tables configured (established 262144 bind 65536)
[ 18.293735] TCP reno registered
[ 18.300988] IPv4 over IPv4 tunneling driver
[ 18.308274] CLASS: registering class device: ID = 'tunl0'
[ 18.308295] class_hotplug - name = tunl0
[ 18.308736] GRE over IPv4 tunneling driver
[ 18.315831] CLASS: registering class device: ID = 'gre0'
[ 18.315837] class_hotplug - name = gre0
[ 18.316130] TCP bic registered
[ 18.323838] Initializing IPsec netlink socket
[ 18.330844] NET: Registered protocol family 1
[ 18.337753] NET: Registered protocol family 17
[ 18.403492] Testing NMI watchdog ... OK.
[ 18.420236] Registering sysdev class '<NULL>'
[ 18.420242] Registering sys device 'lapic_nmi0'
[ 18.420442] Using IPI Shortcut mode
[ 18.427511] swsusp: Resume From Partition /dev/hda2
[ 18.427513] PM: Checking swsusp image.
[ 18.455914] swsusp: Error -22 check for resume file
[ 18.455916] PM: Resume from disk failed.
[ 18.455942] Registering sysdev class '<NULL>'
[ 18.455947] Registering sys device 'acpi0'
[ 18.456177] ACPI wakeup devices:
[ 18.462755] LID PBTN PCI0 USB0 USB1 USB2 USB3 MODM PCIE
[ 18.469475] ACPI: (supports S0 S1 S3 S4 S5)
[ 18.476321] Freeing unused kernel memory: 212k freed
[ 18.483263] CLASS: registering class device: ID = 'vcs1'
[ 18.483272] class_hotplug - name = vcs1
[ 18.483275] class_device_create_hotplug called for vcs1
[ 18.483525] CLASS: registering class device: ID = 'vcsa1'
[ 18.483535] class_hotplug - name = vcsa1
[ 18.483536] class_device_create_hotplug called for vcsa1
[ 18.637618] EXT3-fs: mounted filesystem with ordered data mode.
[ 18.649869] kjournald starting. Commit interval 5 seconds
[ 20.601927] CLASS: registering class device: ID = 'vcs2'
[ 20.601942] class_hotplug - name = vcs2
[ 20.601944] class_device_create_hotplug called for vcs2
[ 20.622898] CLASS: registering class device: ID = 'vcsa2'
[ 20.622905] class_hotplug - name = vcsa2
[ 20.622907] class_device_create_hotplug called for vcsa2
[ 20.623902] CLASS: Unregistering class device. ID = 'vcs2'
[ 20.623907] class_hotplug - name = vcs2
[ 20.623909] class_device_create_hotplug called for vcs2
[ 20.624156] device class 'vcs2': release.
[ 20.624159] class_device_create_release called for vcs2
[ 20.624161] CLASS: Unregistering class device. ID = 'vcsa2'
[ 20.624164] class_hotplug - name = vcsa2
[ 20.624166] class_device_create_hotplug called for vcsa2
[ 20.624396] device class 'vcsa2': release.
[ 20.624397] class_device_create_release called for vcsa2
[ 20.626032] CLASS: registering class device: ID = 'vcs3'
[ 20.626039] class_hotplug - name = vcs3
[ 20.626041] class_device_create_hotplug called for vcs3
[ 20.626304] CLASS: registering class device: ID = 'vcsa3'
[ 20.626311] class_hotplug - name = vcsa3
[ 20.626312] class_device_create_hotplug called for vcsa3
[ 20.628274] CLASS: Unregistering class device. ID = 'vcs3'
[ 20.628280] class_hotplug - name = vcs3
[ 20.628282] class_device_create_hotplug called for vcs3
[ 20.628568] device class 'vcs3': release.
[ 20.628570] class_device_create_release called for vcs3
[ 20.628572] CLASS: Unregistering class device. ID = 'vcsa3'
[ 20.628575] class_hotplug - name = vcsa3
[ 20.628577] class_device_create_hotplug called for vcsa3
[ 20.628816] device class 'vcsa3': release.
[ 20.628817] class_device_create_release called for vcsa3
[ 20.631495] CLASS: registering class device: ID = 'vcs4'
[ 20.631506] class_hotplug - name = vcs4
[ 20.631509] class_device_create_hotplug called for vcs4
[ 20.631844] CLASS: registering class device: ID = 'vcsa4'
[ 20.631851] class_hotplug - name = vcsa4
[ 20.631852] class_device_create_hotplug called for vcsa4
[ 20.633807] CLASS: Unregistering class device. ID = 'vcs4'
[ 20.633814] class_hotplug - name = vcs4
[ 20.633816] class_device_create_hotplug called for vcs4
[ 20.634123] device class 'vcs4': release.
[ 20.634125] class_device_create_release called for vcs4
[ 20.634127] CLASS: Unregistering class device. ID = 'vcsa4'
[ 20.634131] class_hotplug - name = vcsa4
[ 20.634132] class_device_create_hotplug called for vcsa4
[ 20.634381] device class 'vcsa4': release.
[ 20.634383] class_device_create_release called for vcsa4
[ 20.636152] CLASS: registering class device: ID = 'vcs5'
[ 20.636164] class_hotplug - name = vcs5
[ 20.636166] class_device_create_hotplug called for vcs5
[ 20.636948] CLASS: registering class device: ID = 'vcsa5'
[ 20.636956] class_hotplug - name = vcsa5
[ 20.636958] class_device_create_hotplug called for vcsa5
[ 20.638984] CLASS: Unregistering class device. ID = 'vcs5'
[ 20.638998] class_hotplug - name = vcs5
[ 20.639000] class_device_create_hotplug called for vcs5
[ 20.639311] device class 'vcs5': release.
[ 20.639314] class_device_create_release called for vcs5
[ 20.639316] CLASS: Unregistering class device. ID = 'vcsa5'
[ 20.639320] class_hotplug - name = vcsa5
[ 20.639322] class_device_create_hotplug called for vcsa5
[ 20.639573] device class 'vcsa5': release.
[ 20.639575] class_device_create_release called for vcsa5
[ 20.641413] CLASS: registering class device: ID = 'vcs6'
[ 20.641426] class_hotplug - name = vcs6
[ 20.641428] class_device_create_hotplug called for vcs6
[ 20.641749] CLASS: registering class device: ID = 'vcsa6'
[ 20.641755] class_hotplug - name = vcsa6
[ 20.641757] class_device_create_hotplug called for vcsa6
[ 20.643759] CLASS: Unregistering class device. ID = 'vcs6'
[ 20.643767] class_hotplug - name = vcs6
[ 20.643769] class_device_create_hotplug called for vcs6
[ 20.644130] device class 'vcs6': release.
[ 20.644132] class_device_create_release called for vcs6
[ 20.644134] CLASS: Unregistering class device. ID = 'vcsa6'
[ 20.644138] class_hotplug - name = vcsa6
[ 20.644140] class_device_create_hotplug called for vcsa6
[ 20.644391] device class 'vcsa6': release.
[ 20.644393] class_device_create_release called for vcsa6
[ 20.647803] CLASS: registering class device: ID = 'vcs7'
[ 20.647816] class_hotplug - name = vcs7
[ 20.647818] class_device_create_hotplug called for vcs7
[ 20.648177] CLASS: registering class device: ID = 'vcsa7'
[ 20.648183] class_hotplug - name = vcsa7
[ 20.648185] class_device_create_hotplug called for vcsa7
[ 20.650276] CLASS: Unregistering class device. ID = 'vcs7'
[ 20.650283] class_hotplug - name = vcs7
[ 20.650285] class_device_create_hotplug called for vcs7
[ 20.650587] device class 'vcs7': release.
[ 20.650589] class_device_create_release called for vcs7
[ 20.650591] CLASS: Unregistering class device. ID = 'vcsa7'
[ 20.650595] class_hotplug - name = vcsa7
[ 20.650596] class_device_create_hotplug called for vcsa7
[ 20.650852] device class 'vcsa7': release.
[ 20.650854] class_device_create_release called for vcsa7
[ 20.652806] CLASS: registering class device: ID = 'vcs8'
[ 20.652819] class_hotplug - name = vcs8
[ 20.652822] class_device_create_hotplug called for vcs8
[ 20.653165] CLASS: registering class device: ID = 'vcsa8'
[ 20.653172] class_hotplug - name = vcsa8
[ 20.653174] class_device_create_hotplug called for vcsa8
[ 20.655301] CLASS: Unregistering class device. ID = 'vcs8'
[ 20.655308] class_hotplug - name = vcs8
[ 20.655310] class_device_create_hotplug called for vcs8
[ 20.655632] device class 'vcs8': release.
[ 20.655635] class_device_create_release called for vcs8
[ 20.655637] CLASS: Unregistering class device. ID = 'vcsa8'
[ 20.655640] class_hotplug - name = vcsa8
[ 20.655642] class_device_create_hotplug called for vcsa8
[ 20.655904] device class 'vcsa8': release.
[ 20.655906] class_device_create_release called for vcsa8
[ 25.849523] floppy0: no floppy controllers found
[ 26.068672] ieee80211_crypt: registered algorithm 'NULL'
[ 26.102338] ieee80211: 802.11 data/management/control stack, git-1.1.7
[ 26.102341] ieee80211: Copyright (C) 2004-2005 Intel Corporation <[email protected]>
[ 26.147929] ipw2200: Intel(R) PRO/Wireless 2200/2915 Network Driver, git-1.0.8
[ 26.147933] ipw2200: Copyright(c) 2003-2005 Intel Corporation
[ 26.147936] bus pci: add driver ipw2200
[ 26.152270] pci: Matched Device 0000:02:03.0 with Driver ipw2200
[ 26.152326] ACPI: PCI Interrupt 0000:02:03.0[A] -> Link [LNKB] -> GSI 7 (level, low) -> IRQ 7
[ 26.152358] ipw2200: Detected Intel PRO/Wireless 2200BG Network Connection
[ 26.152380] CLASS: registering class device: ID = '0000:02:03.0'
[ 26.152391] class_hotplug - name = 0000:02:03.0
[ 26.152395] class->hotplug() returned -19
[ 26.152399] class_hotplug - name = 0000:02:03.0
[ 26.231890] CLASS: Unregistering class device. ID = '0000:02:03.0'
[ 26.231902] class_hotplug - name = 0000:02:03.0
[ 26.232402] device class '0000:02:03.0': release.
[ 26.232407] CLASS: registering class device: ID = '0000:02:03.0'
[ 26.232415] class_hotplug - name = 0000:02:03.0
[ 26.232418] class->hotplug() returned -19
[ 26.232421] class_hotplug - name = 0000:02:03.0
[ 26.250722] CLASS: Unregistering class device. ID = '0000:02:03.0'
[ 26.250735] class_hotplug - name = 0000:02:03.0
[ 26.251266] CLASS: registering class device: ID = '0000:02:03.0'
[ 26.251275] class_hotplug - name = 0000:02:03.0
[ 26.251278] class->hotplug() returned -19
[ 26.251282] class_hotplug - name = 0000:02:03.0
[ 26.258937] device class '0000:02:03.0': release.
[ 26.276321] CLASS: Unregistering class device. ID = '0000:02:03.0'
[ 26.276334] class_hotplug - name = 0000:02:03.0
[ 26.397597] device class '0000:02:03.0': release.
[ 26.398354] CLASS: registering class device: ID = 'eth1'
[ 26.398368] class_hotplug - name = eth1
[ 26.398867] bound device '0000:02:03.0' to driver 'ipw2200'
[ 26.398872] pci: Bound Device 0000:02:03.0 to Driver ipw2200
[ 27.305152] EXT3 FS on hda3, internal journal
[ 27.413602] kjournald starting. Commit interval 5 seconds
[ 27.413802] EXT3 FS on hda1, internal journal
[ 27.413806] EXT3-fs: mounted filesystem with ordered data mode.
[ 28.120587] Adding 2008116k swap on /dev/hda2. Priority:-1 extents:1 across:2008116k
[ 28.266219] CLASS: registering class device: ID = 'vcs2'
[ 28.266233] class_hotplug - name = vcs2
[ 28.266236] class_device_create_hotplug called for vcs2
[ 28.266529] CLASS: registering class device: ID = 'vcsa2'
[ 28.266538] class_hotplug - name = vcsa2
[ 28.266540] class_device_create_hotplug called for vcsa2
[ 28.267084] CLASS: Unregistering class device. ID = 'vcs2'
[ 28.267089] class_hotplug - name = vcs2
[ 28.267091] class_device_create_hotplug called for vcs2
[ 28.267339] device class 'vcs2': release.
[ 28.267341] class_device_create_release called for vcs2
[ 28.267343] CLASS: Unregistering class device. ID = 'vcsa2'
[ 28.267346] class_hotplug - name = vcsa2
[ 28.267348] class_device_create_hotplug called for vcsa2
[ 28.267582] device class 'vcsa2': release.
[ 28.267583] class_device_create_release called for vcsa2
[ 28.267801] CLASS: registering class device: ID = 'vcs2'
[ 28.267806] class_hotplug - name = vcs2
[ 28.267808] class_device_create_hotplug called for vcs2
[ 28.268052] CLASS: registering class device: ID = 'vcsa2'
[ 28.268057] class_hotplug - name = vcsa2
[ 28.268059] class_device_create_hotplug called for vcsa2
[ 28.313694] CLASS: registering class device: ID = 'vcs3'
[ 28.313706] class_hotplug - name = vcs3
[ 28.313708] class_device_create_hotplug called for vcs3
[ 28.314016] CLASS: registering class device: ID = 'vcsa3'
[ 28.314022] class_hotplug - name = vcsa3
[ 28.314024] class_device_create_hotplug called for vcsa3
[ 28.314548] CLASS: Unregistering class device. ID = 'vcs3'
[ 28.314553] class_hotplug - name = vcs3
[ 28.314554] class_device_create_hotplug called for vcs3
[ 28.314795] device class 'vcs3': release.
[ 28.314797] class_device_create_release called for vcs3
[ 28.314799] CLASS: Unregistering class device. ID = 'vcsa3'
[ 28.314802] class_hotplug - name = vcsa3
[ 28.314804] class_device_create_hotplug called for vcsa3
[ 28.315043] device class 'vcsa3': release.
[ 28.315045] class_device_create_release called for vcsa3
[ 28.315262] CLASS: registering class device: ID = 'vcs3'
[ 28.315268] class_hotplug - name = vcs3
[ 28.315269] class_device_create_hotplug called for vcs3
[ 28.315506] CLASS: registering class device: ID = 'vcsa3'
[ 28.315511] class_hotplug - name = vcsa3
[ 28.315513] class_device_create_hotplug called for vcsa3
[ 28.873990] pcmcia: Detected deprecated PCMCIA ioctl usage.
[ 28.873993] pcmcia: This interface will soon be removed from the kernel; please expect breakage unless you upgrade to new tools.
[ 28.873996] pcmcia: see http://www.kernel.org/pub/linux/utils/kernel/pcmcia/pcmcia.html for details.
[ 19.084000] Time: acpi_pm clocksource has been installed.
[ 19.085000] Ktimers: Switched to high resolution mode CPU 0
[ 21.128000] b44: eth0: Link is up at 100 Mbps, full duplex.
[ 21.128000] b44: eth0: Flow control is on for TX and on for RX.
[ 62.376000] CLASS: registering class device: ID = 'vcs4'
[ 62.376000] class_hotplug - name = vcs4
[ 62.376000] class_device_create_hotplug called for vcs4
[ 62.377000] CLASS: registering class device: ID = 'vcsa4'
[ 62.377000] class_hotplug - name = vcsa4
[ 62.377000] class_device_create_hotplug called for vcsa4
[ 62.379000] CLASS: Unregistering class device. ID = 'vcs4'
[ 62.379000] class_hotplug - name = vcs4
[ 62.379000] class_device_create_hotplug called for vcs4
[ 62.380000] device class 'vcs4': release.
[ 62.380000] class_device_create_release called for vcs4
[ 62.380000] CLASS: Unregistering class device. ID = 'vcsa4'
[ 62.380000] class_hotplug - name = vcsa4
[ 62.380000] class_device_create_hotplug called for vcsa4
[ 62.381000] device class 'vcsa4': release.
[ 62.381000] class_device_create_release called for vcsa4
[ 62.382000] CLASS: registering class device: ID = 'vcs4'
[ 62.382000] class_hotplug - name = vcs4
[ 62.382000] class_device_create_hotplug called for vcs4
[ 62.383000] CLASS: registering class device: ID = 'vcsa4'
[ 62.383000] class_hotplug - name = vcsa4
[ 62.383000] class_device_create_hotplug called for vcsa4
[ 62.387000] CLASS: registering class device: ID = 'vcs5'
[ 62.387000] class_hotplug - name = vcs5
[ 62.387000] class_device_create_hotplug called for vcs5
[ 62.388000] CLASS: registering class device: ID = 'vcsa5'
[ 62.388000] class_hotplug - name = vcsa5
[ 62.388000] class_device_create_hotplug called for vcsa5
[ 62.390000] CLASS: Unregistering class device. ID = 'vcs5'
[ 62.390000] class_hotplug - name = vcs5
[ 62.390000] class_device_create_hotplug called for vcs5
[ 62.391000] device class 'vcs5': release.
[ 62.391000] class_device_create_release called for vcs5
[ 62.391000] CLASS: Unregistering class device. ID = 'vcsa5'
[ 62.391000] class_hotplug - name = vcsa5
[ 62.391000] class_device_create_hotplug called for vcsa5
[ 62.392000] device class 'vcsa5': release.
[ 62.392000] class_device_create_release called for vcsa5
[ 62.394000] CLASS: registering class device: ID = 'vcs5'
[ 62.394000] class_hotplug - name = vcs5
[ 62.394000] class_device_create_hotplug called for vcs5
[ 62.396000] CLASS: registering class device: ID = 'vcs6'
[ 62.396000] class_hotplug - name = vcs6
[ 62.396000] class_device_create_hotplug called for vcs6
[ 62.626000] CLASS: registering class device: ID = 'vcsa5'
[ 62.626000] class_hotplug - name = vcsa5
[ 62.626000] class_device_create_hotplug called for vcsa5
[ 62.628000] CLASS: registering class device: ID = 'vcsa6'
[ 62.628000] class_hotplug - name = vcsa6
[ 62.628000] class_device_create_hotplug called for vcsa6
[ 62.709000] CLASS: Unregistering class device. ID = 'vcs6'
[ 62.709000] class_hotplug - name = vcs6
[ 62.709000] class_device_create_hotplug called for vcs6
[ 62.710000] device class 'vcs6': release.
[ 62.710000] class_device_create_release called for vcs6
[ 62.711000] CLASS: Unregistering class device. ID = 'vcsa6'
[ 62.711000] class_hotplug - name = vcsa6
[ 62.711000] class_device_create_hotplug called for vcsa6
[ 62.712000] device class 'vcsa6': release.
[ 62.713000] class_device_create_release called for vcsa6
[ 62.733000] CLASS: registering class device: ID = 'vcs6'
[ 62.733000] class_hotplug - name = vcs6
[ 62.733000] class_device_create_hotplug called for vcs6
[ 62.734000] CLASS: registering class device: ID = 'vcsa6'
[ 62.734000] class_hotplug - name = vcsa6
[ 62.734000] class_device_create_hotplug called for vcsa6
[ 71.188000] CLASS: registering class device: ID = 'vcs7'
[ 71.188000] class_hotplug - name = vcs7
[ 71.188000] class_device_create_hotplug called for vcs7
[ 71.189000] CLASS: registering class device: ID = 'vcsa7'
[ 71.189000] class_hotplug - name = vcsa7
[ 71.189000] class_device_create_hotplug called for vcsa7
[ 71.313000] CLASS: Unregistering class device. ID = 'vcs7'
[ 71.313000] class_hotplug - name = vcs7
[ 71.313000] class_device_create_hotplug called for vcs7
[ 71.314000] device class 'vcs7': release.
[ 71.314000] class_device_create_release called for vcs7
[ 71.314000] CLASS: Unregistering class device. ID = 'vcsa7'
[ 71.314000] class_hotplug - name = vcsa7
[ 71.314000] class_device_create_hotplug called for vcsa7
[ 71.315000] device class 'vcsa7': release.
[ 71.315000] class_device_create_release called for vcsa7
[ 71.819000] CLASS: registering class device: ID = 'vcs7'
[ 71.819000] class_hotplug - name = vcs7
[ 71.819000] class_device_create_hotplug called for vcs7
[ 71.820000] CLASS: registering class device: ID = 'vcsa7'
[ 71.820000] class_hotplug - name = vcsa7
[ 71.820000] class_device_create_hotplug called for vcsa7
[ 189.109000] Time: jiffies clocksource has been installed.
[ 189.364000] check_monotonic_clock: monotonic inconsistency detected!
[ 189.364000] from 2c4111daf6 (190070250230) to 2c4111daf5 (190070250229).
[ 189.364000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 189.364000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 189.364000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 189.364000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 189.364000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 189.364000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 189.364000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 189.364000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 189.364000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 189.364000] [<c025d24d>] __rb_erase_color+0xcd/0x190
[ 189.364000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 189.364000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 189.364000] [<c0124772>] __do_softirq+0x42/0xa0
[ 189.364000] [<c010531e>] do_softirq+0x4e/0x60
[ 189.364000] =======================
[ 189.364000] [<c0124895>] irq_exit+0x35/0x40
[ 189.364000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 189.364000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 189.364000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 189.364000] [<c01010f2>] cpu_idle+0x42/0x60
[ 189.364000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 189.364000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 189.619000] check_monotonic_clock: monotonic inconsistency detected!
[ 189.619000] from 2c5044782f (190325225519) to 2c5044782e (190325225518).
[ 189.619000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 189.619000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 189.619000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 189.619000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 189.619000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 189.619000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 189.619000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 189.619000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 189.619000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 189.619000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 189.619000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 189.619000] [<c0124772>] __do_softirq+0x42/0xa0
[ 189.619000] [<c010531e>] do_softirq+0x4e/0x60
[ 189.619000] =======================
[ 189.619000] [<c0124895>] irq_exit+0x35/0x40
[ 189.619000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 189.619000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 189.619000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 189.619000] [<c01010f2>] cpu_idle+0x42/0x60
[ 189.619000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 189.619000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 189.874000] check_monotonic_clock: monotonic inconsistency detected!
[ 189.874000] from 2c5f771568 (190580200808) to 2c5f771567 (190580200807).
[ 189.874000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 189.874000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 189.874000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 189.874000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 189.874000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 189.874000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 189.874000] [<c032a210>] i8042_timer_func+0x0/0x10
[ 189.874000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 189.874000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 189.874000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 189.874000] [<c0124772>] __do_softirq+0x42/0xa0
[ 189.874000] [<c010531e>] do_softirq+0x4e/0x60
[ 189.874000] =======================
[ 189.874000] [<c0124895>] irq_exit+0x35/0x40
[ 189.874000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 189.874000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 189.874000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 189.874000] [<c01010f2>] cpu_idle+0x42/0x60
[ 189.874000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 189.874000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 190.130000] check_monotonic_clock: monotonic inconsistency detected!
[ 190.130000] from 2c6eb8f480 (190836176000) to 2c6eb8f47f (190836175999).
[ 190.130000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 190.130000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 190.130000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 190.130000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 190.130000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 190.130000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 190.130000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 190.130000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 190.130000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 190.130000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 190.130000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 190.130000] [<c0124772>] __do_softirq+0x42/0xa0
[ 190.130000] [<c010531e>] do_softirq+0x4e/0x60
[ 190.130000] =======================
[ 190.130000] [<c0124895>] irq_exit+0x35/0x40
[ 190.130000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 190.130000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 190.130000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 190.130000] [<c01010f2>] cpu_idle+0x42/0x60
[ 190.130000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 190.130000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 190.589000] check_monotonic_clock: monotonic inconsistency detected!
[ 190.589000] from 2c8a140f80 (191295131520) to 2c8a140f7f (191295131519).
[ 190.589000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 190.589000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 190.589000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 190.589000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 190.589000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 190.589000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 190.589000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 190.589000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 190.589000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 190.589000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 190.589000] [<c0124772>] __do_softirq+0x42/0xa0
[ 190.589000] [<c010531e>] do_softirq+0x4e/0x60
[ 190.589000] =======================
[ 190.589000] [<c0124895>] irq_exit+0x35/0x40
[ 190.589000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 190.589000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 190.589000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 190.589000] [<c01010f2>] cpu_idle+0x42/0x60
[ 190.589000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 190.589000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 190.694000] check_monotonic_clock: monotonic inconsistency detected!
[ 190.694000] from 2c90561401 (191400121345) to 2c90561400 (191400121344).
[ 190.694000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 190.694000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 190.694000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 190.694000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 190.694000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 190.694000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 190.694000] [<c0116adb>] smp_apic_timer_interrupt+0x2b/0x30
[ 190.694000] [<c0103af0>] apic_timer_interrupt+0x1c/0x24
[ 190.694000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 190.694000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 190.694000] [<c0124772>] __do_softirq+0x42/0xa0
[ 190.694000] [<c010531e>] do_softirq+0x4e/0x60
[ 190.694000] =======================
[ 190.694000] [<c0124895>] irq_exit+0x35/0x40
[ 190.694000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 190.694000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 190.694000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 190.694000] [<c01010f2>] cpu_idle+0x42/0x60
[ 190.694000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 190.694000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 190.796000] check_monotonic_clock: monotonic inconsistency detected!
[ 190.796000] from 2c966a52e4 (191502111460) to 2c966a52e3 (191502111459).
[ 190.796000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 190.796000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 190.796000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 190.796000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 190.796000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 190.796000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 190.796000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 190.796000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 190.796000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 190.796000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 190.796000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 190.796000] [<c0124772>] __do_softirq+0x42/0xa0
[ 190.796000] [<c010531e>] do_softirq+0x4e/0x60
[ 190.796000] =======================
[ 190.796000] [<c0124895>] irq_exit+0x35/0x40
[ 190.796000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 190.796000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 190.796000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 190.796000] [<c01010f2>] cpu_idle+0x42/0x60
[ 190.796000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 190.796000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 190.949000] check_monotonic_clock: monotonic inconsistency detected!
[ 190.949000] from 2c9f88b13a (191655096634) to 2c9f88b139 (191655096633).
[ 190.949000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 190.949000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 190.949000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 190.949000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 190.949000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 190.949000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 190.949000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 190.949000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 190.949000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 190.949000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 190.949000] [<c0124772>] __do_softirq+0x42/0xa0
[ 190.949000] [<c010531e>] do_softirq+0x4e/0x60
[ 190.949000] =======================
[ 190.949000] [<c0124895>] irq_exit+0x35/0x40
[ 190.949000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 190.949000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 190.949000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 190.949000] [<c01010f2>] cpu_idle+0x42/0x60
[ 190.949000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 190.949000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 191.051000] check_monotonic_clock: monotonic inconsistency detected!
[ 191.051000] from 2ca59cf01d (191757086749) to 2ca59cf01c (191757086748).
[ 191.051000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 191.051000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 191.051000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 191.051000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 191.051000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 191.051000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 191.051000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 191.051000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 191.051000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 191.051000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 191.051000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 191.051000] [<c0124772>] __do_softirq+0x42/0xa0
[ 191.051000] [<c010531e>] do_softirq+0x4e/0x60
[ 191.051000] =======================
[ 191.051000] [<c0124895>] irq_exit+0x35/0x40
[ 191.051000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 191.051000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 191.051000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 191.051000] [<c01010f2>] cpu_idle+0x42/0x60
[ 191.051000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 191.051000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 191.204000] check_monotonic_clock: monotonic inconsistency detected!
[ 191.204000] from 2caebb4e73 (191910071923) to 2caebb4e72 (191910071922).
[ 191.204000] Badness in check_monotonic_clock at kernel/time/timeofday.c:156
[ 191.204000] [<c01381cc>] check_monotonic_clock+0x9c/0x110
[ 191.204000] [<c013858e>] get_monotonic_clock+0xae/0xd0
[ 191.204000] [<c0136aac>] enqueue_ktimer+0x2c/0x350
[ 191.204000] [<c0136ed1>] internal_restart_ktimer+0x51/0xb0
[ 191.204000] [<c013962a>] timeofday_periodic_hook+0x67a/0x8f0
[ 191.204000] [<f98891f7>] ipw_handle_mgmt_packet+0x37/0x2d0 [ipw2200]
[ 191.204000] [<c01365e2>] ktimer_interrupt+0x1e2/0x2e0
[ 191.204000] [<c0137258>] ktimer_run_queues+0x18/0x110
[ 191.204000] [<c0138fb0>] timeofday_periodic_hook+0x0/0x8f0
[ 191.204000] [<c01371f8>] run_ktimer_softirq+0x68/0xb0
[ 191.204000] [<c0124772>] __do_softirq+0x42/0xa0
[ 191.204000] [<c010531e>] do_softirq+0x4e/0x60
[ 191.204000] =======================
[ 191.204000] [<c0124895>] irq_exit+0x35/0x40
[ 191.204000] [<c01051da>] do_IRQ+0x5a/0xa0
[ 191.204000] [<c0103ace>] common_interrupt+0x1a/0x20
[ 191.204000] [<c02f6000>] acpi_processor_idle+0x127/0x2bd
[ 191.204000] [<c01010f2>] cpu_idle+0x42/0x60
[ 191.204000] [<c05a9855>] start_kernel+0x175/0x1e0
[ 191.204000] [<c05a93a0>] unknown_bootoption+0x0/0x1e0
[ 207.856000] Time: c3tsc clocksource has been installed.
[ 207.857000] Ktimers: Switched to high resolution mode CPU 0
[ 214.135000] Time: pit clocksource has been installed.
[ 219.565000] Time: jiffies clocksource has been installed.
[ 227.340000] Time: acpi_pm clocksource has been installed.
[ 227.341000] Ktimers: Switched to high resolution mode CPU 0
[ 236.159000] Time: c3tsc clocksource has been installed.
[ 236.159000] Ktimers: Switched to high resolution mode CPU 0


Attachments:
dmesg (95.81 kB)

2005-11-14 22:02:21

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Mon, 2005-11-14 at 14:53 -0700, Frank Sorenson wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> john stultz wrote:
> > Hmm... Not sure if this is mis-calibration or just bad-interaction w/
> > kthrt. Mind sending a dmesg to me?
>
> dmesg attached

Thanks, I'll start looking into it.

> >>and 'pit' seems to produce errors (system will not switch from pit to
> >>another clocksource anymore):
>
> Odd. This time, I got the errors when I switched from acpi_pm (which it
> had defaulted to at bootup) to jiffies. System has not locked at one
> clocksource yet, though.

Yea, jiffies and pit are similarly non-continuous clocksources.

> > Do the TOD patches have this issue by themselves, or is this only with
> > kthrt? I know I had some issues with non-continuous clocksources (pit,
> > jiffies) with the kthrt patch, where it wouldn't fall back to
> > non-high-res when the clocksource stopped supporting it.
>
> I only tried with kthrt because I ran into lots of conflicts when
> applying the patches to more recent kernels otherwise. I can try again
> with 2.6.14-mm2 in order to test it out.

You can alternatively drop the patches in the kthrt set that are after
the timeofday patches in Thomas' series file. Or even just disable the
high-res config option and see if that changes anything.

thanks
-john


2005-11-14 23:07:33

by Frank Sorenson

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

john stultz wrote:
> On Mon, 2005-11-14 at 14:53 -0700, Frank Sorenson wrote:
>
>>-----BEGIN PGP SIGNED MESSAGE-----
>>Hash: SHA1
>>
>>john stultz wrote:
>>
>>>Hmm... Not sure if this is mis-calibration or just bad-interaction w/
>>>kthrt. Mind sending a dmesg to me?

Okay, the c3tsc clock drift is definitely not an interaction with kthrt.
Here is 2.6.14-mm2 + the TOD B10 patches:
14 Nov 15:54:52 offset: -0.003031 drift: -3091.0 ppm
14 Nov 15:55:52 offset: -0.184073 drift: -3018.57377049 ppm
14 Nov 15:56:52 offset: -0.345268 drift: -2853.95041322 ppm
14 Nov 15:57:53 offset: -0.463002 drift: -2544.2967033 ppm
14 Nov 15:58:53 offset: -0.587743 drift: -2428.93801653 ppm

Running just 2.6.14-mm2 + TOD B10, I seem to be unable to reproduce the
Badness errors, and the clocksource has not frozen at one setting.

I can provide a dmesg if needed.

Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
[email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.7 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iD8DBQFDeRigaI0dwg4A47wRAsv6AJ4sPwUMg44sRN+kGpYjKjF8+qZWIACfZ1YL
pLHKnEHkMjMi32kGWeT+gEw=
=9hHi
-----END PGP SIGNATURE-----

2005-11-14 23:25:25

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Mon, 2005-11-14 at 16:07 -0700, Frank Sorenson wrote:
> >>john stultz wrote:
> >>>Hmm... Not sure if this is mis-calibration or just bad-interaction w/
> >>>kthrt. Mind sending a dmesg to me?
>
> Okay, the c3tsc clock drift is definitely not an interaction with kthrt.
> Here is 2.6.14-mm2 + the TOD B10 patches:
> 14 Nov 15:54:52 offset: -0.003031 drift: -3091.0 ppm
> 14 Nov 15:55:52 offset: -0.184073 drift: -3018.57377049 ppm
> 14 Nov 15:56:52 offset: -0.345268 drift: -2853.95041322 ppm
> 14 Nov 15:57:53 offset: -0.463002 drift: -2544.2967033 ppm
> 14 Nov 15:58:53 offset: -0.587743 drift: -2428.93801653 ppm
>
> Running just 2.6.14-mm2 + TOD B10, I seem to be unable to reproduce the
> Badness errors, and the clocksource has not frozen at one setting.
>
> I can provide a dmesg if needed.

Hrm.. How about sending a dmesg of just vanilla 2.6.14-mm2? Also does
the behavior change booting w/ idle=poll ?

Thanks so much for the problem report and testing, btw!
-john

2005-11-15 05:05:22

by Frank Sorenson

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

john stultz wrote:
> Hrm.. How about sending a dmesg of just vanilla 2.6.14-mm2? Also does
> the behavior change booting w/ idle=poll ?

idle=poll does seem to fix the major clock drift problem. There may
still be an issue, but it's much smaller:

2.6.14-mm2-todb10:
14 Nov 21:50:57 offset: -0.025373 drift: -22404.0 ppm
14 Nov 21:51:59 offset: -1.577053 drift: -24985.4603175 ppm
14 Nov 21:53:00 offset: -3.104569 drift: -25012.9032258 ppm

2.6.14-mm2-todb10 with idle=poll:
14 Nov 21:37:59 offset: 5.9e-05 drift: 63.0 ppm
14 Nov 21:39:00 offset: 0.003207 drift: 51.7903225806 ppm
14 Nov 21:40:00 offset: 0.012048 drift: 98.7868852459 ppm
14 Nov 21:41:00 offset: 0.020439 drift: 112.324175824 ppm
14 Nov 21:42:00 offset: 0.023596 drift: 97.520661157 ppm
14 Nov 21:43:00 offset: 0.026723 drift: 88.5 ppm
14 Nov 21:44:00 offset: 0.029856 drift: 82.4861878453 ppm
14 Nov 21:45:01 offset: 0.033036 drift: 78.1087470449 ppm

> Thanks so much for the problem report and testing, btw!

I just hope it helps! :)

Frank
- --
Frank Sorenson - KD7TZK
Systems Manager, Computer Science Department
Brigham Young University
[email protected]
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Fedora - http://enigmail.mozdev.org

iD8DBQFDeWx2aI0dwg4A47wRAtVoAJ9OmWr3FYTDMBcrUBSAY6NYoq7naACff+eX
jZ4Z8+uGxRXOtTCaoUWd2ns=
=S5T6
-----END PGP SIGNATURE-----

2005-11-15 19:53:40

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Mon, 2005-11-14 at 22:04 -0700, Frank Sorenson wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> john stultz wrote:
> > Hrm.. How about sending a dmesg of just vanilla 2.6.14-mm2? Also does
> > the behavior change booting w/ idle=poll ?
>
> idle=poll does seem to fix the major clock drift problem. There may
> still be an issue, but it's much smaller:
>
> 2.6.14-mm2-todb10:
> 14 Nov 21:50:57 offset: -0.025373 drift: -22404.0 ppm
> 14 Nov 21:51:59 offset: -1.577053 drift: -24985.4603175 ppm
> 14 Nov 21:53:00 offset: -3.104569 drift: -25012.9032258 ppm
>
> 2.6.14-mm2-todb10 with idle=poll:
> 14 Nov 21:37:59 offset: 5.9e-05 drift: 63.0 ppm
> 14 Nov 21:39:00 offset: 0.003207 drift: 51.7903225806 ppm

Hmm. It seems the c3 compensation is triggering when it shouldn't, or
maybe its over compensating.

I can't reproduce it on my laptop. Do you recall if in previous tests
you saw anything like this? I'm trying to narrow down if its just a
difference in hardware or if something in the c3 idle code changed.

thanks
-john


2005-11-15 20:54:13

by Ingo Molnar

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)


* john stultz <[email protected]> wrote:

> > idle=poll does seem to fix the major clock drift problem. There may
> > still be an issue, but it's much smaller:
> >
> > 2.6.14-mm2-todb10:
> > 14 Nov 21:50:57 offset: -0.025373 drift: -22404.0 ppm
> > 14 Nov 21:51:59 offset: -1.577053 drift: -24985.4603175 ppm
> > 14 Nov 21:53:00 offset: -3.104569 drift: -25012.9032258 ppm
> >
> > 2.6.14-mm2-todb10 with idle=poll:
> > 14 Nov 21:37:59 offset: 5.9e-05 drift: 63.0 ppm
> > 14 Nov 21:39:00 offset: 0.003207 drift: 51.7903225806 ppm
>
> Hmm. It seems the c3 compensation is triggering when it shouldn't, or
> maybe its over compensating.
>
> I can't reproduce it on my laptop. Do you recall if in previous tests
> you saw anything like this? I'm trying to narrow down if its just a
> difference in hardware or if something in the c3 idle code changed.

it's with an earlier queue of yours, but maybe it's related: i have a
report that HPET causes HRT inaccuracies (e.g. sleeps for 20 msecs last
21 msecs). If all HPET options are turned off in the .config then
everything is fine and accurate.

Ingo

2005-11-15 21:05:24

by john stultz

[permalink] [raw]
Subject: Re: [PATCH 0/13] Time: Generic Timeofday Subsystem (v B10)

On Tue, 2005-11-15 at 21:53 +0100, Ingo Molnar wrote:
> * john stultz <[email protected]> wrote:
>
> > > idle=poll does seem to fix the major clock drift problem. There may
> > > still be an issue, but it's much smaller:
> > >
> > > 2.6.14-mm2-todb10:
> > > 14 Nov 21:50:57 offset: -0.025373 drift: -22404.0 ppm
> > > 14 Nov 21:51:59 offset: -1.577053 drift: -24985.4603175 ppm
> > > 14 Nov 21:53:00 offset: -3.104569 drift: -25012.9032258 ppm
> > >
> > > 2.6.14-mm2-todb10 with idle=poll:
> > > 14 Nov 21:37:59 offset: 5.9e-05 drift: 63.0 ppm
> > > 14 Nov 21:39:00 offset: 0.003207 drift: 51.7903225806 ppm
> >
> > Hmm. It seems the c3 compensation is triggering when it shouldn't, or
> > maybe its over compensating.
> >
> > I can't reproduce it on my laptop. Do you recall if in previous tests
> > you saw anything like this? I'm trying to narrow down if its just a
> > difference in hardware or if something in the c3 idle code changed.
>
> it's with an earlier queue of yours, but maybe it's related: i have a
> report that HPET causes HRT inaccuracies (e.g. sleeps for 20 msecs last
> 21 msecs). If all HPET options are turned off in the .config then
> everything is fine and accurate.

Hmm. HPET would be separate from this, I believe. I suspect that could
be related to the HPET legacy replacement functionality, where the HPET
is triggering the timer interrupts. Not something I changed, but
probably should be looked into.

thanks
-john