Time: Generic Timeofday Subsystem (v B14)
Subsystems (8)
cover Time: Generic Timeofday Subsystem (v B14)
All,
The following patchset applies against 2.6.15-rc5-git + the
hrtimer patch-set 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.
The current patchset can be found here:
http://sr71.net/~jstultz/tod/
New in this release:
x clock_was_set
x x86-64 vsyscall fix suggested by Serge Belyshev. x dropped c3tsc for ACPI PM
x dropped ACPI PM rate verification (causing false positives)
Still on the TODO list:
o nothing?
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, Serge Belyshev,
Dominik Brodowski, Thomas Gleixner, Darren Hart, Christoph Lameter, Matt Mackal, Keith Mannthey, Ingo Molnar, Martin Schwidefsky, Frank Sorenson, Ulrich Windl, Jonathan Woithe, Darrick Wong, Roman Zippel and any others whom I've accidentally left off this list.
thanks
-john
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.
thanks
-john
timer.c | 123 ++++++++++++++++++++++++++++++++++++++++++++-------------------- 1 files changed, 85 insertions(+), 38 deletions(-)
linux-2.6.15-rc5_timeofday-ntp-part1_B14.patch
============================================
kernel/timer.c +85 -38
@@ -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
@@ -716,45 +717,86 @@ 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 +* @interval_ns: interval, in nanoseconds +* +* 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 (); + } +} + +/** +* phase_advance - advance the phase +* +* advance the phase, once it gets to one nanosecond advance the tick more. +*/ +static inline long phase_advance (void ) +{ +long delta =0 ; + + time_phase += time_adj; + +if ((time_phase >=FINENSEC ) || (time_phase <= -FINENSEC )) { + delta =shift_right (time_phase, (SHIFT_SCALE -10 )); + time_phase -= delta << (SHIFT_SCALE -10 ); + } + +return delta; +} + +/** +* xtime_advance - advance xtime +* @delta_nsec: adjustment in nsecs +*/ +static inline void xtime_advance (long delta_nsec) +{ + xtime.tv_nsec += delta_nsec; +if (likely (xtime.tv_nsec <NSEC_PER_SEC )) +return ; + + xtime.tv_nsec -=NSEC_PER_SEC ; + xtime.tv_sec++; }/*
@@ -762,19 +804,24 @@ static void update_wall_time_one_tick(vo* usually just one (we shouldn't be losing ticks, * we're doing this this way mainly for interrupt * latency reasons, not because we think we'll -* have lots of lost timer ticks +* have lots of lost timer ticks) */ static void update_wall_time (unsigned long ticks) {do { - ticks--; -update_wall_time_one_tick (); -if (xtime.tv_nsec >=1000000000 ) { - xtime.tv_nsec -=1000000000 ; - xtime.tv_sec++; -second_overflow (); - } - }while (ticks); +/* +* Calculate the nsec delta using the precomputed NTP +* adjustments: +* tick_nsec, time_adjust_step, time_adj +*/ +long delta_nsec = tick_nsec + time_adjust_step *1000 ; + delta_nsec +=phase_advance (); + +xtime_advance (delta_nsec); +ntp_advance (tick_nsec); +time_interpolator_update (delta_nsec); + + }while (--ticks); }/*
PATCH 2/13 Time: Reduced NTP Rework (part 2)
In reply to john stultzAll,
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
include/linux/timex.h | 23 +++++++ kernel/time.c | 8 +- kernel/timer.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 165 insertions(+), 10 deletions(-)
linux-2.6.15-rc5_timeofday-ntp-part2_B14.patch
============================================
include/linux/timex.h +22 -1
@@ -260,6 +260,8 @@ extern long pps_calcnt; /* calibrationextern 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,21 +269,40 @@ 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); }/** * ntp_synced - Returns 1 if the NTP status is not UNSYNC -* */ static inline int ntp_synced (void ) {return !(time_status &STA_UNSYNC ); } +/** +* ntp_get_ppm_adjustment - Returns Shifted PPM adjustment +*/ +extern long ntp_get_ppm_adjustment (void ); + +/** +* ntp_advance - Advances the NTP state machine by interval_ns +*/ +extern void ntp_advance (unsigned long interval_ns); + +/** +* ntp_leapsecond - NTP leapsecond processing code. +*/ +extern int ntp_leapsecond (struct timespec now); + +/* Required to safely shift negative values */ #define shift_right (x, s) ({ \ __typeof__(x) __x = (x); \
kernel/time.c +4 -4
@@ -258,6 +258,8 @@ int do_adjtimex(struct timex *txc)return -EINVAL ;write_seqlock_irq (&xtime_lock); +write_seqlock (&ntp_lock); + result = time_state;/* mostly `TIME_OK' */ /* Save for later - semantics of adjtime is to return old value */
@@ -395,6 +397,7 @@ leave: if ((time_status & (STA_UNSYNC|ST txc->calcnt = pps_calcnt; txc->errcnt = pps_errcnt; txc->stbcnt = pps_stbcnt; +write_sequnlock (&ntp_lock);write_sequnlock_irq (&xtime_lock);do_gettimeofday (&txc->time);notify_arch_cmos_timer ();
@@ -512,10 +515,7 @@ int do_settimeofday (struct timespec *tvset_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);
kernel/timer.c +139 -5
211 lines not on this page · raw message
PATCH 3/13 Time: Clocksource Infrastructure
In reply to john stultzAll,
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
Documentation/kernel-parameters.txt | 14 + include/linux/clocksource.h | 304 ++++++++++++++++++++++++++++++++++ kernel/Makefile | 1 kernel/time/Makefile | 1 kernel/time/clocksource.c | 313 ++++++++++++++++++++++++++++++++++++ kernel/time/jiffies.c | 75 ++++++++ 6 files changed, 704 insertions(+), 4 deletions(-)
linux-2.6.15-rc5_timeofday-clocksource-core_B14.patch
============================================
Documentation/kernel-parameters.txt +10 -4
@@ -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).
include/linux/clocksource.h new file +304 -0
304 lines not on this page · raw message
kernel/Makefile +1 -0
@@ -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 \ hrtimer.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
kernel/time/Makefile new file +1 -0
@@ -0,0 +1 @@ +obj-y += clocksource.o jiffies.o
kernel/time/clocksource.c new file +313 -0
313 lines not on this page · raw message
kernel/time/jiffies.c new file +75 -0
75 lines not on this page · raw message
PATCH 4/13 Time: Generic Timekeeping Infrastructure
In reply to john stultzAll,
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
Documentation/timekeeping.txt | 350 ++++++++++++++++++++ drivers/char/hangcheck-timer.c | 5 include/asm-generic/timeofday.h | 30 + include/linux/time.h | 30 + include/linux/timeofday.h | 46 ++ include/linux/timex.h | 2 include/sound/timer.h | 1 init/main.c | 2 kernel/hrtimer.c | 1 kernel/posix-timers.c | 2 kernel/time.c | 17 kernel/time/Makefile | 2 kernel/time/timeofday.c | 685 ++++++++++++++++++++++++++++++++++++++++ kernel/timer.c | 7 14 files changed, 1169 insertions(+), 11 deletions(-)
linux-2.6.15-rc5_timeofday-core_B14.patch
============================================
Documentation/timekeeping.txt new file +350 -0
@@ -0,0 +1,350 @@ +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!
drivers/char/hangcheck-timer.c +5 -0
@@ -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__
include/asm-generic/timeofday.h new file +30 -0
@@ -0,0 +1,30 @@ +/* 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) do { } while(0) +#endif /* CONFIG_GENERIC_TIME_VSYSCALL */ + +#endif /* CONFIG_GENERIC_TIME */ +#endif
include/linux/time.h +22 -8
@@ -27,6 +27,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
@@ -50,12 +54,6 @@ extern void set_normalized_timespec(stru#define timespec_valid (ts) \ (((ts)->tv_sec >= 0) && (((unsigned) (ts)->tv_nsec) < NSEC_PER_SEC)) -/* -* 64-bit nanosec type. Large enough to span 292+ years in nanosecond -* resolution. Ought to be enough for a while. -*/ -typedef s64 nsec_t ; -extern struct timespec xtime;extern struct timespec wall_to_monotonic;extern seqlock_t xtime_lock;
@@ -79,8 +77,6 @@ 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 void getnstimestamp (struct timespec *ts);extern struct timespec timespec_trunc (struct timespec t,unsigned gran);
@@ -125,6 +121,24 @@ extern struct timespec ns_to_timespec(co*/ extern struct timeval ns_to_timeval (const 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
include/linux/timeofday.h new file +46 -0
46 lines not on this page · raw message
include/linux/timex.h +2 -0
@@ -310,6 +310,7 @@ extern int ntp_leapsecond(struct timespe __x <0 ? -(-__x >> __s) : __x >> __s; \ }) +#ifndef CONFIG_GENERIC_TIME #ifdef CONFIG_TIME_INTERPOLATION
@@ -365,6 +366,7 @@ time_interpolator_reset(void) }#endif /* !CONFIG_TIME_INTERPOLATION */ +#endif /* !CONFIG_GENERIC_TIME */ #endif /* KERNEL */
include/sound/timer.h +1 -0
@@ -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 ;
init/main.c +2 -0
@@ -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(voidinit_timers ();hrtimers_init ();softirq_init (); +timeofday_init ();time_init ();/*
kernel/hrtimer.c +1 -0
@@ -29,6 +29,7 @@#include <linux/percpu.h> #include <linux/hrtimer.h> #include <linux/notifier.h> +#include <linux/timeofday.h> #include <linux/syscalls.h> #include <linux/interrupt.h>
kernel/posix-timers.c +1 -1
@@ -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>
kernel/time.c +17 -0
@@ -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
@@ -479,6 +493,7 @@ struct timespec timespec_trunc(struct ti }EXPORT_SYMBOL (timespec_trunc); +#ifndef CONFIG_GENERIC_TIME #ifdef CONFIG_TIME_INTERPOLATION void getnstimeofday (struct timespec *tv) {
@@ -586,6 +601,8 @@ void getnstimestamp(struct timespec *ts) }EXPORT_SYMBOL_GPL (getnstimestamp); +#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.
kernel/time/Makefile +1 -1
@@ -1 +1 @@ -obj-y += clocksource.o jiffies.o +obj-y += clocksource.o jiffies.o timeofday.o
kernel/time/timeofday.c new file +685 -0
685 lines not on this page · raw message
kernel/timer.c +6 -1
@@ -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>
@@ -882,6 +882,10 @@ void ntp_advance(unsigned long interval_write_sequnlock_irqrestore (&ntp_lock, flags); } +#ifdef CONFIG_GENERIC_TIME +# define update_wall_time (x) do { } while (0) +#else +/** * phase_advance - advance the phase *
@@ -957,6 +961,7 @@ static void update_wall_time(unsigned lo } while (--ticks); } +#endif /* !CONFIG_GENERIC_TIME */ /* * Called from the timer interrupt handler to charge one tick to the current
PATCH 5/13 Time: i386 Conversion - part 1: Move timer_pit.c to i8253.c
In reply to john stultzAll,
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
Makefile | 2 - i8253.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++ time.c | 6 ----- timers/timer_pit.c | 13 ----------- 4 files changed, 60 insertions(+), 20 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part1_B14.patch
============================================
arch/i386/kernel/Makefile +1 -1
@@ -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/
arch/i386/kernel/i8253.c new file +59 -0
59 lines not on this page · raw message
arch/i386/kernel/time.c +0 -6
@@ -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_devicif (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);
arch/i386/kernel/timers/timer_pit.c +0 -13
@@ -162,16 +162,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); -}
PATCH 6/13 Time: i386 Conversion - part 2: Move timer_tsc.c to tsc.c
In reply to john stultzAll,
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
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 | 312 ++++++++++++++++++++++++++++++++++++ include/asm-i386/timex.h | 34 --- include/asm-i386/tsc.h | 44 +++++ 6 files changed, 358 insertions(+), 330 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part2_B14.patch
============================================
arch/i386/kernel/Makefile +1 -1
@@ -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/
arch/i386/kernel/timers/common.c +0 -84
93 lines not on this page · raw message
arch/i386/kernel/timers/timer_tsc.c +0 -212
242 lines not on this page · raw message
arch/i386/kernel/tsc.c new file +312 -0
312 lines not on this page · raw message
include/asm-i386/timex.h +1 -33
@@ -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
include/asm-i386/tsc.h new file +44 -0
44 lines not on this page · raw message
PATCH 7/13 Time: i386 Conversion - part 3: Rework TSC Support
In reply to john stultzAll,
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
arch/i386/kernel/setup.c | 1 arch/i386/kernel/tsc.c | 194 ++++++++++++++-------------- drivers/acpi/processor_idle.c | 4 include/asm-i386/mach-default/mach_timer.h | 4 include/asm-i386/mach-summit/mach_mpparse.h | 3 include/asm-i386/tsc.h | 11 + 6 files changed, 116 insertions(+), 101 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part3_B14.patch
============================================
arch/i386/kernel/setup.c +1 -0
@@ -1620,6 +1620,7 @@ void __init setup_arch(char **cmdline_p) conswitchp = &dummy_con;#endif #endif +tsc_init (); }#include "setup_arch_post.h"
arch/i386/kernel/tsc.c +97 -97
304 lines not on this page · raw message
drivers/acpi/processor_idle.c +4 -0
@@ -371,6 +371,10 @@ static void acpi_processor_idle(void) ACPI_MTX_DO_NOT_LOCK); } +#ifdef CONFIG_GENERIC_TIME +/* TSC halts in C3, so notify users */ +mark_tsc_unstable (); +#endif /* Re-enable interrupts */ local_irq_enable ();set_thread_flag (TIF_POLLING_NRFLAG );
include/asm-i386/mach-default/mach_timer.h +3 -1
@@ -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 ) {
include/asm-i386/mach-summit/mach_mpparse.h +3 -0
@@ -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(chif (!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 ;
include/asm-i386/tsc.h +8 -3
@@ -25,9 +25,12 @@*/ typedef unsigned long long cycles_t ; -static inline cycles_t get_cycles (void ) +extern unsigned int cpu_khz; +extern unsigned int tsc_khz; + +static inline cycles_t get_cycles (void ) { -unsigned long long ret=0 ; +unsigned long long ret =0 ;#ifndef CONFIG_X86_TSC if (!cpu_has_tsc)
@@ -40,5 +43,7 @@ static inline cycles_t get_cycles (void) return ret; } -extern unsigned int cpu_khz; +extern void tsc_init (void ); +extern void mark_tsc_unstable (void ); +#endif
PATCH 8/13 Time: i386 Conversion - part 4: ACPI PM variable renaming and config change.
In reply to john stultzAll,
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 and removes the CONFIG_X86_PM_TIMER option.
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
arch/i386/kernel/acpi/boot.c | 22 ++++++++++------------ drivers/acpi/Kconfig | 18 ------------------ drivers/acpi/osl.c | 2 -- 3 files changed, 10 insertions(+), 32 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part4_B14.patch
============================================
arch/i386/kernel/acpi/boot.c +10 -12
@@ -608,9 +608,8 @@ static int __init acpi_parse_hpet(unsign#define acpi_parse_hpet NULL#endif -#ifdef CONFIG_X86_PM_TIMER -extern u32 pmtmr_ioport; -#endif +u32 acpi_pmtmr_ioport; +int acpi_pmtmr_buggy;static int __init acpi_parse_fadt (unsigned long phys,unsigned long size) {
@@ -629,7 +628,6 @@ static int __init acpi_parse_fadt(unsign acpi_fadt.force_apic_physical_destination_mode = fadt->force_apic_physical_destination_mode; -#ifdef CONFIG_X86_PM_TIMER /* detect the location of the ACPI PM Timer */ if (fadt->revision >=FADT2_REVISION_ID ) {/* FADT rev. 2 */
@@ -637,22 +635,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); -#endif + +if (acpi_pmtmr_ioport) + printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n", acpi_pmtmr_ioport); +return 0 ; }
drivers/acpi/Kconfig +0 -18
@@ -287,24 +287,6 @@ config ACPI_SYSTEM This driver will enable your system to shut down using ACPI, and dump your ACPI DSDT table using /proc/acpi/dsdt. -config X86_PM_TIMER - bool "Power Management Timer Support" - depends on X86 - depends on !X86_64 - default y - help - The Power Management Timer is available on all ACPI-capable, - in most cases even if ACPI is unusable or blacklisted. - - This timing source is not affected by powermanagement features - like aggressive processor idling, throttling, frequency and/or - voltage scaling, unlike the commonly used Time Stamp Counter - (TSC) timing source. - - So, if you see messages like 'Losing too many ticks!' in the - kernel logs, and/or you are using this on a notebook which - does not yet have an HPET, you should say "Y" here. - config ACPI_CONTAINER tristate "ACPI0004,PNP0A05 and PNP0A06 Container Driver (EXPERIMENTAL)" depends on EXPERIMENTAL
drivers/acpi/osl.c +0 -2
@@ -346,9 +346,7 @@ u64 acpi_os_get_timer(void)/* TBD: use HPET if available */ #endif -#ifdef CONFIG_X86_PM_TIMER /* TBD: default to PM timer if HPET was not available */ -#endif if (!t) printk(KERN_ERR PREFIX "acpi_os_get_timer() TBD\n");
PATCH 9/13 Time: i386 Conversion - part 5: Enable Generic Timekeeping
In reply to john stultzAll,
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
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 | 66 +++++++++++- include/asm-i386/delay.h | 2 include/asm-i386/timeofday.h | 4 include/asm-i386/timer.h | 57 ----------- 8 files changed, 104 insertions(+), 261 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part5_B14.patch
============================================
arch/i386/Kconfig +4 -0
@@ -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
arch/i386/kernel/Makefile +0 -1
@@ -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
arch/i386/kernel/time.c +34 -185
321 lines not on this page · raw message
arch/i386/kernel/tsc.c +2 -10
@@ -10,6 +10,7 @@#include <linux/init.h> #include <asm/tsc.h> +#include <asm/delay.h> #include <asm/io.h> #include "mach_timer.h"
@@ -45,16 +46,6 @@ static int __init tsc_setup(char *str)__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
@@ -207,6 +198,7 @@ void tsc_init(void) (unsigned long)cpu_khz % 1000);set_cyc2ns_scale (cpu_khz); +use_tsc_delay (); }#ifdef CONFIG_CPU_FREQ
arch/i386/lib/delay.c +58 -8
101 lines not on this page · raw message
include/asm-i386/delay.h +2 -0
@@ -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) */
include/asm-i386/timeofday.h new file +4 -0
@@ -0,0 +1,4 @@ +#ifndef _ASM_I386_TIMEOFDAY_H +#define _ASM_I386_TIMEOFDAY_H +#include <asm-generic/timeofday.h> +#endif
include/asm-i386/timer.h +0 -57
68 lines not on this page · raw message
PATCH 10/13 Time: i386 Conversion - part 6: Remove Old Code
In reply to john stultzAll,
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
Makefile | 9 - common.c | 88 ------------ timer.c | 75 ---------- timer_cyclone.c | 259 ------------------------------------- timer_hpet.c | 217 ------------------------------- timer_none.c | 39 ----- timer_pit.c | 164 ----------------------- timer_pm.c | 268 -------------------------------------- timer_tsc.c | 388 -------------------------------------------------------- 9 files changed, 1507 deletions(-)
linux-2.6.15-rc5_timeofday-arch-i386-part6_B14.patch
============================================
arch/i386/kernel/timers/Makefile deleted +0 -9
@@ -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
arch/i386/kernel/timers/common.c deleted +0 -88
88 lines not on this page · raw message
arch/i386/kernel/timers/timer.c deleted +0 -75
75 lines not on this page · raw message
arch/i386/kernel/timers/timer_cyclone.c deleted +0 -259
259 lines not on this page · raw message
arch/i386/kernel/timers/timer_hpet.c deleted +0 -217
217 lines not on this page · raw message
arch/i386/kernel/timers/timer_none.c deleted +0 -39
@@ -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, -};
arch/i386/kernel/timers/timer_pit.c deleted +0 -164
164 lines not on this page · raw message
arch/i386/kernel/timers/timer_pm.c deleted +0 -268
268 lines not on this page · raw message
arch/i386/kernel/timers/timer_tsc.c deleted +0 -388
388 lines not on this page · raw message
PATCH 11/13 Time: i386/x86-64 Clocksource Drivers
In reply to john stultzAll,
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
arch/i386/kernel/Makefile | 1 arch/i386/kernel/hpet.c | 69 +++++++++++++++++++++++ arch/i386/kernel/i8253.c | 60 ++++++++++++++++++++ arch/i386/kernel/tsc.c | 85 ++++++++++++++++++++++++++++- drivers/Makefile | 1 drivers/clocksource/Makefile | 2 drivers/clocksource/acpi_pm.c | 123 ++++++++++++++++++++++++++++++++++++++++++ drivers/clocksource/cyclone.c | 121 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 461 insertions(+), 1 deletion(-)
linux-2.6.15-rc5_timeofday-clocks-i386_B14.patch
============================================
arch/i386/kernel/Makefile +1 -0
@@ -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
arch/i386/kernel/hpet.c new file +69 -0
69 lines not on this page · raw message
arch/i386/kernel/i8253.c +60 -0
69 lines not on this page · raw message
arch/i386/kernel/tsc.c +84 -1
100 lines not on this page · raw message
drivers/Makefile +1 -0
@@ -70,3 +70,4 @@ obj-$(CONFIG_SGI_IOC4) += sn/ obj-y += firmware/ obj-$(CONFIG_CRYPTO) += crypto/ obj-$(CONFIG_SUPERH) += sh/ +obj-$(CONFIG_GENERIC_TIME) += clocksource/
drivers/clocksource/Makefile new file +2 -0
@@ -0,0 +1,2 @@ +obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o +obj-$(CONFIG_ACPI) += acpi_pm.o
drivers/clocksource/acpi_pm.c new file +123 -0
123 lines not on this page · raw message
drivers/clocksource/cyclone.c new file +121 -0
121 lines not on this page · raw message
PATCH 12/13 Time: x86-64 Conversion to Generic Timekeeping
In reply to john stultzAll,
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.
Please note, this patch is not yet ready for submission, but is being maintained as an example of how the generic framework supports multiple arches with different features (such as vsyscall). That said, I'd still appreciate any suggestions for improvements to this code.
thanks
-john
arch/x86_64/kernel/pmtimer.c | 101 ------- b/arch/i386/kernel/acpi/boot.c | 9 b/arch/x86_64/Kconfig | 23 - b/arch/x86_64/kernel/Makefile | 1 b/arch/x86_64/kernel/time.c | 503 +++++++++++++------------------------ b/arch/x86_64/kernel/vmlinux.lds.S | 12 b/arch/x86_64/kernel/vsyscall.c | 142 ++++++++-- 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 | 3 b/include/asm-x86_64/vsyscall.h | 8 12 files changed, 350 insertions(+), 468 deletions(-)
linux-2.6.15-rc5_timeofday-arch-x86-64_B14.patch
============================================
arch/i386/kernel/acpi/boot.c +5 -4
@@ -567,7 +567,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;
@@ -589,6 +589,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);
@@ -597,10 +598,10 @@ static int __init acpi_parse_hpet(unsignextern 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 ; }
arch/x86_64/Kconfig +8 -15
@@ -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
@@ -327,21 +335,6 @@ config HPET_TIMER as it is off-chip. You can find the HPET spec at <http://www.intel.com/hardwaredesign/hpetspec.htm>. -config X86_PM_TIMER - bool "PM timer" - depends on ACPI - default y - help - Support the ACPI PM timer for time keeping. This is slow, - but is useful on some chipsets without HPET on systems with more - than one CPU. On a single processor or single socket multi core - system it is normally not required. - When the PM timer is active 64bit vsyscalls are disabled - and should not be enabled (/proc/sys/kernel/vsyscall64 should - not be changed). - The kernel selects the PM timer only as a last resort, so it is - useful to enable just in case. - config HPET_EMULATE_RTC bool "Provide RTC interrupt" depends on HPET_TIMER && RTC=y
arch/x86_64/kernel/Makefile +0 -1
@@ -29,7 +29,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
arch/x86_64/kernel/pmtimer.c deleted +0 -101
101 lines not on this page · raw message
arch/x86_64/kernel/time.c +187 -316
609 lines not on this page · raw message
arch/x86_64/kernel/vmlinux.lds.S +12 -0
@@ -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) }
arch/x86_64/kernel/vsyscall.c +113 -29
197 lines not on this page · raw message
include/asm-generic/div64.h +9 -0
@@ -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 */
include/asm-x86_64/hpet.h +2 -1
@@ -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
@@ -51,6 +51,7 @@#define HPET_TN_ROUTE_SHIFT 9 +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 );
include/asm-x86_64/timeofday.h new file +4 -0
@@ -0,0 +1,4 @@ +#ifndef _ASM_X86_64_TIMEOFDAY_H +#define _ASM_X86_64_TIMEOFDAY_H +#include <asm-generic/timeofday.h> +#endif
include/asm-x86_64/timex.h +3 -0
@@ -24,6 +24,9 @@ static inline cycles_t get_cycles (void) }extern unsigned int cpu_khz; +extern unsigned int tsc_khz; + +extern void mark_tsc_unstable (void );extern int read_current_timer (unsigned long *timer_value);#define ARCH_HAS_READ_CURRENT_TIMER 1
include/asm-x86_64/vsyscall.h +7 -1
@@ -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
PATCH 13/13 Time: Generic Timekeeping Paraniod Debug Patch
In reply to john stultzAll,
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
arch/i386/kernel/tsc.c | 36 +++++++++++++ arch/x86_64/kernel/time.c | 26 +++++++++ include/asm-generic/bug.h | 10 +++ kernel/time/timeofday.c | 127 +++++++++++++++++++++++++++++++++++++++++++++- lib/Kconfig.debug | 5 + 5 files changed, 202 insertions(+), 2 deletions(-)
linux-2.6.15-rc5_timeofday-paranoid-debug_B14.patch
============================================
arch/i386/kernel/tsc.c +36 -0
@@ -309,6 +309,40 @@ 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! */ +static DEFINE_SPINLOCK (checktsc_lock); +static cycle_t last_tsc; + +static cycle_t read_tsc (void ) +{ +static int once =1 ; + +unsigned long flags; +cycle_t ret; + +spin_lock_irqsave (&checktsc_lock, flags); + +rdtscll (ret); + +if (once && ret < last_tsc) { + once =0 ; +spin_unlock_irqrestore (&checktsc_lock, flags); +printk ("BUG in read_tsc(): TSC went backward!\n" ); +if (num_online_cpus () >1 ) +printk ("... Unsynced TSCs?\n" ); +printk ("... [ from %016Lx to %016Lx ]\n" , last_tsc, ret); + + }else { + 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;
@@ -318,6 +352,8 @@ static cycle_t read_tsc(void)return ret; } +#endif /* CONFIG_PARANOID_GENERIC_TIME */ +static struct clocksource clocksource_tsc = { .name ="tsc" , .rating =300 ,
arch/x86_64/kernel/time.c +26 -0
@@ -1044,6 +1044,30 @@ static unsigned long current_tsc_khz = 0static int tsc_update_callback (void ); +#ifdef CONFIG_PARANOID_GENERIC_TIME +/* This will hurt performance! */ +static DEFINE_SPINLOCK (checktsc_lock); +static cycle_t last_tsc; + +static cycle_t read_tsc (void ) +{ +unsigned long flags; +cycle_t ret; + +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; +} + +#else /* CONFIG_PARANOID_GENERIC_TIME */ +static cycle_t read_tsc (void ) {cycle_t ret;
@@ -1053,6 +1077,8 @@ static cycle_t read_tsc(void)return ret; } +#endif /* CONFIG_PARANOID_GENERIC_TIME */ + static cycle_t __vsyscall_fn vread_tsc(void* unused) {cycle_t ret;
include/asm-generic/bug.h +10 -0
@@ -39,4 +39,14 @@#endif #endif +#define WARN_ON_ONCE (condition) \ +do { \ + static int warn_once = 1; \ + \ + if (condition) { \ + warn_once = 0; \ + WARN_ON(1); \ + } \ +} while (0); +#endif
kernel/time/timeofday.c +125 -2
175 lines not on this page · raw message
lib/Kconfig.debug +5 -0
@@ -46,6 +46,11 @@ config LOG_BUF_SHIFT 13 => 8 KB 12 => 4 KB +config PARANOID_GENERIC_TIME + default y + depends on GENERIC_TIME + bool "Paraniod Timekeeping Checks" + config DETECT_SOFTLOCKUP bool "Detect Soft Lockups" depends on DEBUG_KERNEL