summaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authorChristoph Lameter <clameter@sgi.com>2004-09-07 17:46:02 -0700
committerLinus Torvalds <torvalds@ppc970.osdl.org>2004-09-07 17:46:02 -0700
commitbd46a4f183290995b9c2fc4de40670e995482769 (patch)
tree8903c89ca648af88cb8ebc48af4e602f43bb6b9f /include
parentc9daeae66c9eea359c65210229b3962e66642a43 (diff)
[PATCH] Time interpolator: Scalability enhancements and high resolution time for IA64
This has been in the ia64 (and hence -mm) trees for a couple of months. Changelog: * Affects only architectures which define CONFIG_TIME_INTERPOLATION (currently only IA64) * Genericize time interpolation, make time interpolators easily usable and provide instructions on how to use the interpolator for other architectures. * Provide nanosecond resolution for clock_gettime and an accuracy up to the time interpolator time base. * clock_getres() reports resolution of underlying time basis which is typically <50ns and may be 1ns on some systems. * Make time interpolator self-tuning to limit time jumps and to make the interpolators work correctly on systems with broken time base specifications. * SMP scalability: Make clock_gettime and gettimeofday scale O(1) by removing the cmpxchg for most clocks (tested for up to 512 CPUs) * IA64: provide asm fastcall that doubles the performance of gettimeofday and clock_gettime on SGI and other IA64 systems (asm fastcalls scale O(1) together with the scalability fixes). * IA64: provide nojitter kernel option so that IA64 systems with correctly synchronized ITC counters may also enjoy the scalability enhancements. Performance measurements for single calls (ITC cycles): A. 4 way Intel IA64 SMP system (kmart) ITC offsets: kmart:/usr/src/noship-tests # dmesg|grep synchr CPU 1: synchronized ITC with CPU 0 (last diff 1 cycles, maxerr 417 cycles) CPU 2: synchronized ITC with CPU 0 (last diff 2 cycles, maxerr 417 cycles) CPU 3: synchronized ITC with CPU 0 (last diff 1 cycles, maxerr 417 cycles) A.1. Current kernel code kmart:/usr/src/noship-tests # ./dmt gettimeofday cycles: 3737 220 215 215 215 215 215 215 215 215 clock_gettime(REAL) cycles: 4058 575 564 576 565 566 558 558 558 558 clock_gettime(MONO) cycles: 1583 621 609 609 609 609 609 609 609 609 clock_gettime(PROCESS) cycles: 71428 298 259 259 259 259 259 259 259 259 clock_gettime(THREAD) cycles: 3982 336 290 298 298 298 298 286 286 286 A.2 New code using cmpxchg kmart:/usr/src/noship-tests # ./dmt gettimeofday cycles: 3145 213 216 213 213 213 213 213 213 213 clock_gettime(REAL) cycles: 3185 230 210 210 210 210 210 210 210 210 clock_gettime(MONO) cycles: 284 217 217 216 216 216 216 216 216 216 clock_gettime(PROCESS) cycles: 68857 289 270 259 259 259 259 259 259 259 clock_gettime(THREAD) cycles: 3862 339 298 298 298 298 290 286 286 286 A.3 New code with cmpxchg switched off (nojitter kernel option) kmart:/usr/src/noship-tests # ./dmt gettimeofday cycles: 3195 219 219 212 212 212 212 212 212 212 clock_gettime(REAL) cycles: 3003 228 205 205 205 205 205 205 205 205 clock_gettime(MONO) cycles: 279 209 209 209 208 208 208 208 208 208 clock_gettime(PROCESS) cycles: 65849 292 259 259 268 270 270 259 259 259 B. SGI SN2 system running 512 IA64 CPUs. B.1. Current kernel code [root@ascender noship-tests]# ./dmt gettimeofday cycles: 17221 1028 1007 1004 1004 1004 1010 25928 1002 1003 clock_gettime(REAL) cycles: 10388 1099 1055 1044 1064 1063 1051 1056 1061 1056 clock_gettime(MONO) cycles: 2363 96 96 96 96 96 96 96 96 96 clock_gettime(PROCESS) cycles: 46537 804 660 666 666 666 666 666 666 666 clock_gettime(THREAD) cycles: 10945 727 710 684 685 686 685 686 685 686 B.2 New code ascender:~/noship-tests # ./dmt gettimeofday cycles: 3874 610 588 588 588 588 588 588 588 588 clock_gettime(REAL) cycles: 3893 612 588 582 588 588 588 588 588 588 clock_gettime(MONO) cycles: 686 595 595 588 588 588 588 588 588 588 clock_gettime(PROCESS) cycles: 290759 322 269 269 259 265 265 265 259 259 clock_gettime(THREAD) cycles: 5153 358 306 298 296 304 290 298 298 298 Scalability of time functions (in time it takes to do a million calls): ======================================================================= A. 4 way Intel IA SMP system (kmart) A.1 Current code kmart:/usr/src/noship-tests # ./todscale -n1000000 CPUS WALL WALL/CPUS 1 0.192 0.192 2 1.125 0.563 4 9.229 2.307 A.2 New code using cmpxchg kmart:/usr/src/noship-tests # ./todscale CPUS WALL WALL/CPUS 1 0.188 0.188 2 0.457 0.229 4 0.413 0.103 (the measurement with 4 cpus may fluctuate up to 15.x somehow) A.3 New code without cmpxchg (nojitter kernel option) kmart:/usr/src/noship-tests # ./todscale -n10000000 CPUS WALL WALL/CPUS 1 0.180 0.180 2 0.180 0.090 4 0.252 0.063 B. SGI SN2 system running 512 IA64 CPUs. The system has a global monotonic clock and therefore has no need for compensation. Current code uses a cmpxchg. New code has no cmpxchg. B.1 current code ascender:~/noship-tests # ./todscale CPUS WALL WALL/CPUS 1 0.850 0.850 2 1.767 0.884 4 6.124 1.531 8 20.777 2.597 16 57.693 3.606 32 164.688 5.146 64 456.647 7.135 128 1093.371 8.542 256 2778.257 10.853 (System crash at 512 CPUs) B.2 New code ascender:~/noship-tests # ./todscale -n1000000 CPUS WALL WALL/CPUS 1 0.426 0.426 2 0.429 0.215 4 0.436 0.109 8 0.452 0.057 16 0.454 0.028 32 0.457 0.014 64 0.459 0.007 128 0.466 0.004 256 0.474 0.002 512 0.518 0.001 Clock Accuracy ============== A. 4 CPU SMP system A.1 Old code kmart:/usr/src/noship-tests # ./cdisp Gettimeofday() = 1092124757.270305000 CLOCK_REALTIME= 1092124757.270382000 resolution= 0.000976563 CLOCK_MONOTONIC= 89.696726590 resolution= 0.000976563 CLOCK_PROCESS_CPUTIME_ID= 0.001242507 resolution= 0.000000001 CLOCK_THREAD_CPUTIME_ID= 0.001255310 resolution= 0.000000001 A.2 New code kmart:/usr/src/noship-tests # ./cdisp Gettimeofday() = 1092124478.194530000 CLOCK_REALTIME= 1092124478.194603399 resolution= 0.000000001 CLOCK_MONOTONIC= 88.198315204 resolution= 0.000000001 CLOCK_PROCESS_CPUTIME_ID= 0.001241235 resolution= 0.000000001 CLOCK_THREAD_CPUTIME_ID= 0.001254747 resolution= 0.000000001 Signed-off-by: Christoph Lameter <clameter@sgi.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'include')
-rw-r--r--include/linux/timex.h123
1 files changed, 43 insertions, 80 deletions
diff --git a/include/linux/timex.h b/include/linux/timex.h
index c9e92e016524..356dc5e9295c 100644
--- a/include/linux/timex.h
+++ b/include/linux/timex.h
@@ -47,14 +47,18 @@
* kernel PLL updated to 1994-12-13 specs (rfc-1589)
* 1997-08-30 Ulrich Windl
* Added new constant NTP_PHASE_LIMIT
+ * 2004-08-12 Christoph Lameter
+ * Reworked time interpolation logic
*/
#ifndef _LINUX_TIMEX_H
#define _LINUX_TIMEX_H
#include <linux/config.h>
#include <linux/compiler.h>
+#include <linux/jiffies.h>
#include <asm/param.h>
+#include <asm/io.h>
/*
* The following defines establish the engineering parameters of the PLL
@@ -320,101 +324,60 @@ extern long pps_stbcnt; /* stability limit exceeded */
#ifdef CONFIG_TIME_INTERPOLATION
-struct time_interpolator {
- /* cache-hot stuff first: */
- unsigned long (*get_offset) (void);
- void (*update) (long);
- void (*reset) (void);
+#define TIME_SOURCE_CPU 0
+#define TIME_SOURCE_MMIO64 1
+#define TIME_SOURCE_MMIO32 2
+#define TIME_SOURCE_FUNCTION 3
+
+/* For proper operations time_interpolator clocks must run slightly slower
+ * than the standard clock since the interpolator may only correct by having
+ * time jump forward during a tick. A slower clock is usually a side effect
+ * of the integer divide of the nanoseconds in a second by the frequency.
+ * The accuracy of the division can be increased by specifying a shift.
+ * However, this may cause the clock not to be slow enough.
+ * The interpolator will self-tune the clock by slowing down if no
+ * resets occur or speeding up if the time jumps per analysis cycle
+ * become too high.
+ *
+ * Setting jitter compensates for a fluctuating timesource by comparing
+ * to the last value read from the timesource to insure that an earlier value
+ * is not returned by a later call. The price to pay
+ * for the compensation is that the timer routines are not as scalable anymore.
+ */
- /* cache-cold stuff follows here: */
- struct time_interpolator *next;
+#define INTERPOLATOR_ADJUST 65536
+#define INTERPOLATOR_MAX_SKIP 10*INTERPOLATOR_ADJUST
+
+struct time_interpolator {
+ unsigned short source; /* time source flags */
+ unsigned char shift; /* increases accuracy of multiply by shifting. */
+ /* Note that bits may be lost if shift is set too high */
+ unsigned char jitter; /* if set compensate for fluctuations */
+ unsigned nsec_per_cyc; /* set by register_time_interpolator() */
+ void *addr; /* address of counter or function */
+ unsigned long offset; /* nsec offset at last update of interpolator */
+ unsigned long last_counter; /* counter value in units of the counter at last update */
+ unsigned long last_cycle; /* Last timer value if TIME_SOURCE_JITTER is set */
unsigned long frequency; /* frequency in counts/second */
long drift; /* drift in parts-per-million (or -1) */
+ unsigned long skips; /* skips forward */
+ unsigned long ns_skipped; /* nanoseconds skipped */
+ struct time_interpolator *next;
};
-extern volatile unsigned long last_nsec_offset;
-#ifndef __HAVE_ARCH_CMPXCHG
-extern spin_lock_t last_nsec_offset_lock;
-#endif
-extern struct time_interpolator *time_interpolator;
-
extern void register_time_interpolator(struct time_interpolator *);
extern void unregister_time_interpolator(struct time_interpolator *);
-
-/* Called with xtime WRITE-lock acquired. */
-static inline void
-time_interpolator_update(long delta_nsec)
-{
- struct time_interpolator *ti = time_interpolator;
-
- if (last_nsec_offset > 0) {
-#ifdef __HAVE_ARCH_CMPXCHG
- unsigned long new, old;
-
- do {
- old = last_nsec_offset;
- if (old > delta_nsec)
- new = old - delta_nsec;
- else
- new = 0;
- } while (cmpxchg(&last_nsec_offset, old, new) != old);
-#else
- /*
- * This really hurts, because it serializes gettimeofday(), but without an
- * atomic single-word compare-and-exchange, there isn't all that much else
- * we can do.
- */
- spin_lock(&last_nsec_offset_lock);
- {
- last_nsec_offset -= min(last_nsec_offset, delta_nsec);
- }
- spin_unlock(&last_nsec_offset_lock);
-#endif
- }
-
- if (ti)
- (*ti->update)(delta_nsec);
-}
-
-/* Called with xtime WRITE-lock acquired. */
-static inline void
-time_interpolator_reset(void)
-{
- struct time_interpolator *ti = time_interpolator;
-
- last_nsec_offset = 0;
- if (ti)
- (*ti->reset)();
-}
-
-/* Called with xtime READ-lock acquired. */
-static inline unsigned long
-time_interpolator_get_offset(void)
-{
- struct time_interpolator *ti = time_interpolator;
- if (ti)
- return (*ti->get_offset)();
- return last_nsec_offset;
-}
+extern void time_interpolator_reset(void);
+extern unsigned long time_interpolator_resolution(void);
+extern unsigned long time_interpolator_get_offset(void);
#else /* !CONFIG_TIME_INTERPOLATION */
static inline void
-time_interpolator_update(long delta_nsec)
-{
-}
-
-static inline void
time_interpolator_reset(void)
{
}
-static inline unsigned long
-time_interpolator_get_offset(void)
-{
- return 0;
-}
-
#endif /* !CONFIG_TIME_INTERPOLATION */
#endif /* KERNEL */