diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/arch/ia64/Kconfig 2.6.0-t5-hrt.clean/arch/ia64/Kconfig --- 2.6.0-t5-hrt.orig/arch/ia64/Kconfig 2003-12-15 17:36:55.000000000 +0100 +++ 2.6.0-t5-hrt.clean/arch/ia64/Kconfig 2003-12-18 14:39:01.000000000 +0100 @@ -367,6 +367,49 @@ . If you don't know what to do here, say N. +config HIGH_RES_TIMERS + bool "High-Resolution Timers" + help + + POSIX timers are available by default. This option enables + high-resolution POSIX timers. With this option the resolution + is at least 1 microsecond. High resolution is not free. If + enabled this option will add a small overhead each time a + timer expires that is not on a 1/HZ tick boundary. If no such + timers are used the overhead is nil. + + This option enables two additional POSIX CLOCKS, + CLOCK_REALTIME_HR and CLOCK_MONOTONIC_HR. Note that this + option does not change the resolution of CLOCK_REALTIME or + CLOCK_MONOTONIC which remain at 1/HZ resolution. + +choice + prompt "Clock source" + depends on HIGH_RES_TIMERS + default HIGH_RES_TIMER_ITC + help + This option allows you to choose the hardware source in charge + of generating high precision interruptions on your system. + On IA-64 these are: + + + ITC Interval Time Counter 1/CPU clock + HPET High Precision Event Timer ~ (XXX:have to check the spec) + + The ITC timer is available on all the ia64 computers because + it is integrated directly into the processor. However it may not + give correct results on MP machines with processors running + at different clock rates. In this case you may want to use + the HPET if available on your machine. + + +config HIGH_RES_TIMER_ITC + bool "Interval Time Counter/ITC" + +config HIGH_RES_TIMER_HPET + bool "High Precision Event Timer/HPET" + +endchoice config PREEMPT bool "Preemptible Kernel" diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/arch/ia64/kernel/time.c 2.6.0-t5-hrt.clean/arch/ia64/kernel/time.c --- 2.6.0-t5-hrt.orig/arch/ia64/kernel/time.c 2003-12-15 17:36:55.000000000 +0100 +++ 2.6.0-t5-hrt.clean/arch/ia64/kernel/time.c 2003-12-18 14:44:51.000000000 +0100 @@ -8,6 +8,7 @@ * Copyright (C) 1999-2000 VA Linux Systems * Copyright (C) 1999-2000 Walt Drummond */ +#define _INCLUDED_FROM_TIME_C #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include @@ -60,6 +62,10 @@ unsigned long itc_get_offset (void) { +#ifdef CONFIG_HIGH_RES_TIMERS + unsigned long elapsed_cycles; + elapsed_cycles = get_arch_cycles(jiffies); +#else unsigned long elapsed_cycles, lost = jiffies - wall_jiffies; unsigned long now = ia64_get_itc(), last_tick; @@ -72,6 +78,7 @@ return last_nsec_offset; } elapsed_cycles = now - last_tick; +#endif return (elapsed_cycles*local_cpu_data->nsec_per_cyc) >> IA64_NSEC_PER_CYC_SHIFT; } @@ -236,6 +243,14 @@ ia64_do_profile(regs); +#ifdef CONFIG_HIGH_RES_TIMERS + new_itm = last_update + local_cpu_data->itm_delta; + + if (get_arch_cycles(jiffies) < arch_cycles_per_jiffy) { + do_hr_timer_int(); + local_cpu_data->itm_next = new_itm; + } else +#endif while (1) { #ifdef CONFIG_SMP @@ -252,6 +267,9 @@ */ write_seqlock(&xtime_lock); do_timer(regs); +#ifdef CONFIG_HIGH_RES_TIMERS + stake_cpuctr(); +#endif local_cpu_data->itm_next = new_itm; write_sequnlock(&xtime_lock); } else @@ -294,11 +312,31 @@ * Stagger the timer tick for each CPU so they don't occur all at (almost) the * same time: */ + // HRT do not support (yet?) the shifted timers interrupts +#ifndef CONFIG_HIGH_RES_TIMERS if (cpu) { unsigned long hi = 1UL << ia64_fls(cpu); shift = (2*(cpu - hi) + 1) * delta/hi/2; } +#endif local_cpu_data->itm_next = ia64_get_itc() + delta + shift; +#ifdef CONFIG_HIGH_RES_TIMERS + if (cpu == TIME_KEEPER_ID) { + init_hrtimers(); + printk(KERN_INFO " ** arch_to_usec: %lu\n", arch_to_usec); + printk(KERN_INFO " ** arch_to_nsec: %lu\n", arch_to_nsec); + printk(KERN_INFO " ** arch_to_latch: %lu\n", arch_to_latch); + printk(KERN_INFO " ** usec_to_arch: %lu\n", usec_to_arch); + printk(KERN_INFO " ** nsec_to_arch: %lu\n", nsec_to_arch); + printk(KERN_INFO " ** cycs_per_jiff: %lu\n", arch_cycles_per_jiffy); + printk(KERN_INFO " ** cyc_per_usec: %lu\n", local_cpu_data->cyc_per_usec); + printk(KERN_INFO " ** last_update: %lu\n", last_update); + } else + //XXX should we synchronise right now the CPU against TIME_KEEPER? + //that's quite easy with last_update + local_cpu_data->itm_next = last_update + local_cpu_data->itm_delta; + +#endif ia64_set_itm(local_cpu_data->itm_next); } @@ -372,6 +410,45 @@ ia64_cpu_local_tick(); } +#ifdef CONFIG_HIGH_RES_TIMERS +int _schedule_jiffies_int(unsigned long jiffie_f) +{ + if (__last_was_long) return 0; + + return _schedule_next_int(jiffie_f, arch_cycles_per_jiffy); +} + +int _schedule_next_int(unsigned long jiffie_f,long sub_jiffie_in) +{ + long sub_jiff_offset; + unsigned long seq; + + unsigned long j; + long js; + /* + * First figure where we are in time. + * A note on locking. We are under the timerlist_lock here. This + * means that interrupts are off already, so don't use irq versions. + */ + do { + seq = read_seqbegin(&xtime_lock); + sub_jiff_offset = sub_jiffie_in - get_arch_cycles(jiffie_f); + j = jiffies; + js = get_arch_cycles(jiffie_f); + } while (read_seqretry(&xtime_lock, seq)); + /* + * If time is already passed, just return saying so. + */ + if (sub_jiff_offset <= 0) + return 1; + + __last_was_long = arch_cycles_per_jiffy == sub_jiffie_in; + reload_timer_chip(sub_jiff_offset); + return 0; +} + +#endif /* CONFIG_HIGH_RES_TIMERS */ + static struct irqaction timer_irqaction = { .handler = timer_interrupt, .flags = SA_INTERRUPT, diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/include/asm-ia64/hrtime.h 2.6.0-t5-hrt.clean/include/asm-ia64/hrtime.h --- 2.6.0-t5-hrt.orig/include/asm-ia64/hrtime.h 1970-01-01 01:00:00.000000000 +0100 +++ 2.6.0-t5-hrt.clean/include/asm-ia64/hrtime.h 2003-12-18 14:38:52.000000000 +0100 @@ -0,0 +1,93 @@ +/* + * + * File: include/asm-ia64/hrtime.h + * 2002-09 Nick Pollitt, SGI. + * 2003-03 glue code for 2.5 by Eric Piel, Copyright (C) Bull S.A. 2003 + * + */ + +#ifndef _IA64_HRTIME_H +#define _IA64_HRTIME_H +#ifdef __KERNEL__ + +#include /* for CONFIG_APM etc... */ +#include /* for u16s */ +#include /* scaling math routines */ +#include +#include +#include /* for EXPORT_SYMBOL */ +#include + +struct timer_conversion_bits { + unsigned long _arch_to_usec; + unsigned long _arch_to_nsec; + unsigned long _usec_to_arch; + unsigned long _nsec_to_arch; + long _arch_cycles_per_jiffy; + unsigned long _arch_to_latch; + unsigned long volatile _last_update; +}; +extern struct timer_conversion_bits timer_conversion_bits; + +#define arch_to_usec timer_conversion_bits._arch_to_usec +#define arch_to_nsec timer_conversion_bits._arch_to_nsec +#define usec_to_arch timer_conversion_bits._usec_to_arch +#define nsec_to_arch timer_conversion_bits._nsec_to_arch +#define arch_cycles_per_jiffy timer_conversion_bits._arch_cycles_per_jiffy +#define arch_to_latch timer_conversion_bits._arch_to_latch +#define last_update timer_conversion_bits._last_update + +/* + * on IA64 we can hope down to 1000 nsec of resolution + * but it may be better to have it depending on the + * clock speed as it is cycles dependant + */ +#define CONFIG_HIGH_RES_RESOLUTION 1000 /* nano second resolution + we will use for high res. */ + +#ifdef _INCLUDED_FROM_TIME_C +EXPORT_SYMBOL(timer_conversion_bits); +#define EXTERN +//int timer_delta = TIMER_DELTA; +int hr_time_resolution = CONFIG_HIGH_RES_RESOLUTION; +#else +#define EXTERN extern +//extern int timer_delta; +extern int hr_time_resolution; +#endif + +#define schedule_hr_timer_int(a,b) _schedule_next_int(a,b) +#define schedule_jiffies_int(a) _schedule_jiffies_int(a) + +extern volatile unsigned long jiffies; +extern u64 jiffies_64; +extern int _schedule_next_int(unsigned long jiffie_f, long sub_jiffie_in); +extern int _schedule_jiffies_int(unsigned long jiffie_f); + +#if defined(SMP) +EXTERN int _last_was_long[NR_CPUS]; +#define __last_was_long _last_was_long[smp_processor_id()] +#else +EXTERN int _last_was_long; +#define __last_was_long _last_was_long +#endif + +/* + * insert the correct include according to which timer was selected + */ +#ifdef CONFIG_HIGH_RES_TIMER_ITC +# include +#elif defined(CONFIG_HIGH_RES_TIMER_HPET) +# include +#else +# error "Need one of: CONFIG_HIGH_RES_TIMER_ITC CONFIG_HIGH_RES_TIMER_HPET" +#endif + +extern inline long +get_arch_cycles(unsigned long ref) +{ + return (long)(jiffies - ref) * arch_cycles_per_jiffy + quick_get_cpuctr(); +} + +#endif /* __KERNEL__ */ +#endif /* _IA64_HRTIME_H */ diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/include/asm-ia64/hrtime-itc.h 2.6.0-t5-hrt.clean/include/asm-ia64/hrtime-itc.h --- 2.6.0-t5-hrt.orig/include/asm-ia64/hrtime-itc.h 1970-01-01 01:00:00.000000000 +0100 +++ 2.6.0-t5-hrt.clean/include/asm-ia64/hrtime-itc.h 2003-12-18 14:42:56.000000000 +0100 @@ -0,0 +1,137 @@ +/* + * + * File: include/asm-ia64/hrtime-itc.h + * 2002-09 Nick Pollitt, SGI. + * 2003-03 glue code for 2.5-2.6 by Eric Piel, Copyright (C) Bull S.A. 2003 + * + */ + +#ifndef _IA64_HRTIME_ITC_H +#define _IA64_HRTIME_ITC_H +#ifdef __KERNEL__ + +/* + * no of latch less than which events cannot be scheduled + */ +#define TIMER_DELTA 300 //300 cycles to arm the timer (itm) and answer it seems the minimum + +#ifdef _INCLUDED_FROM_TIME_C +struct timer_conversion_bits timer_conversion_bits; +#endif + +extern inline unsigned long quick_get_cpuctr(void) { + unsigned long itc_value = ia64_get_itc(); + // to ensure we never return a negative number (may happen on SMP) + if time_after(itc_value, last_update) + return itc_value - last_update; + else + return 0; +} + +/* + * This function moves the last_update value to the closest to the + * current 1/HZ boundry. This MUST be called under the write xtime_lock. + */ +extern inline unsigned long +stake_cpuctr(void) +{ + if (quick_get_cpuctr() >= arch_cycles_per_jiffy) { + last_update += arch_cycles_per_jiffy; + } +} + +/* The usual scaling for ia64 should be good here too */ +#define HR_TIME_SCALE_NSEC IA64_NSEC_PER_CYC_SHIFT +#define HR_TIME_SCALE_USEC IA64_NSEC_PER_CYC_SHIFT + +/* + * We use various scaling. The sc_n scales by the first parm. + */ +extern inline long arch_cycle_to_usec(unsigned long update) +{ + return (mpy_sc_n(HR_TIME_SCALE_USEC, update, arch_to_usec)); +} + +extern inline long arch_cycle_to_latch(unsigned long update) +{ + return (update); +} + +extern inline long arch_cycle_to_nsec(long update) +{ + return (mpy_sc_n(HR_TIME_SCALE_NSEC, update, arch_to_nsec)); +} +/* + * And the other way... + */ +extern inline long usec_to_arch_cycle(unsigned long usec) +{ + return (mpy_sc_n(HR_TIME_SCALE_USEC, usec, usec_to_arch)); +} +extern inline long nsec_to_arch_cycle(unsigned long nsec) +{ + return (mpy_sc_n(HR_TIME_SCALE_NSEC, nsec, nsec_to_arch)); +} +#ifdef _INCLUDED_FROM_TIME_C + +#include + +/* + * Set the next interruption in new_latch_value cycles + * from now. This check we are not setting an interrupt + * after the one already planned. + */ +extern inline void reload_timer_chip(long new_latch_value) +{ + unsigned long new_itm; + + new_latch_value = arch_cycle_to_latch(new_latch_value); + if (new_latch_value > arch_cycles_per_jiffy) + new_latch_value = arch_cycles_per_jiffy; + + if (new_latch_value < TIMER_DELTA) + new_latch_value = TIMER_DELTA; + + new_itm = ia64_get_itc() + new_latch_value; + /* check we really don't increase the time before the next interruption */ + if (time_before(new_itm, local_cpu_data->itm_next)) { + do { + while (time_after_eq(ia64_get_itc() + TIMER_DELTA / 2, new_itm)) + new_itm = ia64_get_itc() + TIMER_DELTA * 2; + local_cpu_data->itm_next = new_itm; + ia64_set_itm(new_itm); + /* double check, in case we got hit by a (slow) PMI: */ + } while (time_after_eq(ia64_get_itc(), new_itm)); + } + + return; +} + +/* + * Code for runtime calibration of high res timers + */ +/* + * XXX we should check that it's not a problem arch_to_nsec is not using + * the same rounding than the other values (round() instead of trunc()) + */ +#define init_hrtimers() \ + arch_to_usec = div_sc_n(HR_TIME_SCALE_USEC, \ + USEC_PER_SEC, \ + local_cpu_data->itc_freq); \ + arch_to_latch = 1; \ + arch_to_nsec = local_cpu_data->nsec_per_cyc; \ + nsec_to_arch = div_sc_n(HR_TIME_SCALE_NSEC, \ + local_cpu_data->itc_freq, \ + NSEC_PER_SEC); \ + usec_to_arch = div_sc_n(HR_TIME_SCALE_USEC, \ + local_cpu_data->itc_freq, \ + USEC_PER_SEC); \ + arch_cycles_per_jiffy = local_cpu_data->itm_delta;\ + last_update = local_cpu_data->itm_next - \ + local_cpu_data->itm_delta; \ + __last_was_long = 1; + +#endif /* _INCLUDED_FROM_TIME_C */ + +#endif /* __KERNEL__ */ +#endif /* _IA64_HRTIME_ITC_H */ diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/include/linux/sc_math.h 2.6.0-t5-hrt.clean/include/linux/sc_math.h --- 2.6.0-t5-hrt.orig/include/linux/sc_math.h 2003-12-15 17:39:09.000000000 +0100 +++ 2.6.0-t5-hrt.clean/include/linux/sc_math.h 2003-12-18 14:38:54.000000000 +0100 @@ -300,7 +300,7 @@ * Warning, this will do an exception if X overflows. * Well, it would if done in asm, this code just truncates.. */ -#define div_long_long_rem(a,b,c) div_ll_X_l_rem(a, b, c) +//#define div_long_long_rem(a,b,c) div_ll_X_l_rem(a, b, c) /* x = divs / div; *rem = divs % div; */ static inline unsigned long div_ll_X_l_rem(unsigned long divs, diff -urwN -X patches/dontdiff -x Documentation 2.6.0-t5-hrt.orig/kernel/timer.c 2.6.0-t5-hrt.clean/kernel/timer.c --- 2.6.0-t5-hrt.orig/kernel/timer.c 2003-12-15 17:39:09.000000000 +0100 +++ 2.6.0-t5-hrt.clean/kernel/timer.c 2003-12-18 16:17:46.000000000 +0100 @@ -425,11 +425,11 @@ * folks using short timers causing then to loop forever... * If sub_jiffie_f > cycles_per_jiffies we will just clean out all * timers at jiffies_f and quit. We get the rest on the next go round. - while ( unlikely(sub_jiffie_f >= cycles_per_jiffies)){ - sub_jiffie_f -= cycles_per_jiffies; +*/ while (unlikely(sub_jiffie_f >= arch_cycles_per_jiffy)) { + sub_jiffie_f -= arch_cycles_per_jiffy; jiffies_f++; } -*/ + while ((long)(jiffies_f - base->timer_jiffies) >= 0) { #else while ((long)(jiffies - base->timer_jiffies) >= 0) { @@ -513,8 +513,9 @@ * the current jiffie. */ #ifdef CONFIG_HIGH_RES_TIMERS - --base->timer_jiffies; //XXX needed when not HRT? + --base->timer_jiffies; if ((sub_jiff == -1) && schedule_jiffies_int(jiffies_f)) { + //this can't work as long as we don't increase jiffies_f at the beginning spin_unlock_irq(&base->lock); spin_lock_irq(&base->lock); goto run_timer_list_again;