public inbox for linux-ia64@vger.kernel.org
 help / color / mirror / Atom feed
From: eric.piel@tremplin-utc.net
To: linux-ia64@vger.kernel.org
Subject: High Resolution Timers on IA64
Date: Thu, 18 Dec 2003 16:44:02 +0000	[thread overview]
Message-ID: <marc-linux-ia64-107176619506875@msgid-missing> (raw)

[-- Attachment #1: Type: text/plain, Size: 2605 bytes --]

Hello,

Here is the first version of IA64 support for the High Resolution Timers Project. 

Let me sum up briefly the project for those not familiar with it: The aim of the
High Resolution Timers Project is to get rid of the current 1 jiffy resolution
of the kernel for the timers (and sleeps) to reach a resolution closer to the
one provided by the hardware. Grossly speaking, this feature is implemented by
scheduling additional clock ticks exactly at the time the next high resolution
timer is due to expire.
For instance, on our 1Ghz IA64 we measured timers with a resolution a bit
smaller than 10 microseconds (while for now it's around 1 millisecond).
Interestingly, on a x86 with the same frequency we obtained a resolution around
30 microseconds.

In order to try the patch you'll have to get a vanilla kernel 2.6.0-test5
and then apply in order those patches:
*official ia64 patch
*official HRT patch for 2.6.0-test5 (available at
sourceforge.net/projects/high-res-timers, it doesn't apply cleanly, don't worry)
*2.6.0-t5-hrt_syscalls.diff (it's available in the latest kernels, just correct
some typos)
*hrtimers-2.6.0-test5-compil-20031213.patch (an update for the official HRT patch)
*hrtimers-ia64-2.6.0-test5-20031218.patch (the real thing)
In the config you'll have to enable HRT and select ITC as the source clock.
After the reboot you should get a HRT actived kernel :-)


To use the feature from the kernel (in a driver for instance) you just create
timers normally and modify a new field "sub_jiffies". This field contains a
value between 0 and arch_cycles_per_jiffy - 1.
On the userland side you access the high resolution timers through the new POSIX
RT syscalls clock_*() and timer_*(), using the clock_id CLOCK_REALTIME_HR. As
there isn't yet support for those syscalls in the glibc you have to link with a
small special library. Get and apply hrtimers-support-2.5.62-1.0.patch (from
sourceforge.net/projects/high-res-timers, don't worry about the old version
number) and then apply hrtimers-support-ia64-2.6.0-t5-20031212.patch .
Then you compile your code with -lposixtime .

Note about this release:
Please consider this code as alpha. It shouldn't do bad things on your machine
but there is a known bug that causes some timers to still have a resolution
around one jiffy, especially on CPUs other than the time-keeper. Any comment,
reports of success or failure would be very welcome!

Thanks to Nick Pollitt for the original work on 2.4 and Alexandre Hessemann for
the current support.
Eric Piel

PS: All the attached patches can also be found at http://pieleric.free.fr/hrt/ .

[-- Attachment #2: hrtimers-2.6.0-test5-compil-20031213.patch --]
[-- Type: application/octet-stream, Size: 11204 bytes --]

diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/arch/i386/kernel/apic.c linux-2.6.0-test5-hrt/arch/i386/kernel/apic.c
--- linux-2.6.0-test5-hrt.orig/arch/i386/kernel/apic.c	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/arch/i386/kernel/apic.c	2003-11-27 21:49:44.000000000 +0100
@@ -1051,7 +1051,7 @@
 						per_cpu(prof_counter, cpu);
 		}
 
-		discipline_timer(cpu);
+		discipline_timer(cpu); //XXX this argument is very weird, the function expects a number of cycles elapsed!
 
 #ifdef CONFIG_SMP
 		update_process_times(user_mode(regs));
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/arch/i386/kernel/time.c linux-2.6.0-test5-hrt/arch/i386/kernel/time.c
--- linux-2.6.0-test5-hrt.orig/arch/i386/kernel/time.c	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/arch/i386/kernel/time.c	2003-12-12 23:23:38.000000000 +0100
@@ -319,7 +319,7 @@
         /*
          * If time is already passed, just return saying so.
          */
-        if (sub_jiff_offset < 0)
+        if (sub_jiff_offset <= 0)
                 return 1;
 
 	__last_was_long = arch_cycles_per_jiffy == sub_jiffie_in;
@@ -332,7 +332,7 @@
 {
         start_PIT();
 }
-#endif /* CONFIG__APM */
+#endif /* CONFIG_APM */
 #endif /* CONFIG_HIGH_RES_TIMERS */
 
 
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/arch/i386/kernel/timers/hrtimer_pm.c linux-2.6.0-test5-hrt/arch/i386/kernel/timers/hrtimer_pm.c
--- linux-2.6.0-test5-hrt.orig/arch/i386/kernel/timers/hrtimer_pm.c	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/arch/i386/kernel/timers/hrtimer_pm.c	2003-12-12 19:41:07.000000000 +0100
@@ -189,7 +189,6 @@
 #ifndef CONFIG_SMP
 			cpu_khz = cpufreq_scale(cpu_khz_ref, 
 						ref_freq, freq->new);
-		}
 #endif
 	}
 
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/arch/i386/kernel/timers/hrtimer_tsc.c linux-2.6.0-test5-hrt/arch/i386/kernel/timers/hrtimer_tsc.c
--- linux-2.6.0-test5-hrt.orig/arch/i386/kernel/timers/hrtimer_tsc.c	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/arch/i386/kernel/timers/hrtimer_tsc.c	2003-12-12 22:20:08.000000000 +0100
@@ -16,7 +16,7 @@
 extern int x86_udelay_tsc;
 extern spinlock_t i8253_lock;
 
-
+static int use_tsc;
 
 /* Cached *multiplier* to convert TSC counts to microseconds.
  * (see the equation below).
@@ -163,6 +163,7 @@
 #ifndef CONFIG_SMP
 		cpu_khz_ref = cpu_khz;
 #endif
+	}
 
 	if((val == CPUFREQ_PRECHANGE && (freq->old < freq->new)) ||
 	   (val == CPUFREQ_POSTCHANGE && (freq->old > freq->new))){
@@ -195,7 +196,6 @@
 						ref_freq, freq->new);
 #endif
 		}
-	}
 
 	return 0;
 }
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime.h linux-2.6.0-test5-hrt/include/asm-i386/hrtime.h
--- linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/asm-i386/hrtime.h	2003-12-12 23:25:26.000000000 +0100
@@ -32,7 +32,7 @@
 #include <asm/io.h>
 #include <linux/sc_math.h>	/* scaling math routines */
 #include <asm/delay.h>
-#include <asm/smp.h>
+#include <linux/smp.h>
 #include <linux/timex.h>	/* for LATCH */
 #include <asm/percpu.h>
 /*
@@ -177,10 +177,6 @@
 
 extern int _schedule_next_int(unsigned long jiffie_f, long sub_jiffie_in);
 extern int _schedule_jiffies_int(unsigned long jiffie_f);
-extern unsigned int volatile latch_reload;
-
-EXTERN int jiffies_intr;
-
 
 /*
  * Now go ahead and include the clock specific file 586/386/acpi
@@ -352,7 +348,7 @@
 	spin_unlock(&i8253_lock);
 	return;
 }
-#endif				//  ! CONFIG_X86_LOCAL_APIC
+#endif				//  ! USE_APIC_TIMERS
 /*
  * Time out for a discussion.  Because the PIT and TSC (or the PIT and
  * pm timer) may drift WRT each other, we need a way to get the jiffie
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime-M586.h linux-2.6.0-test5-hrt/include/asm-i386/hrtime-M586.h
--- linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime-M586.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/asm-i386/hrtime-M586.h	2003-11-09 11:28:19.000000000 +0100
@@ -38,6 +38,8 @@
 };
 #endif
 
+extern struct timer_opts hrtimer_tsc;
+
 extern inline unsigned long
 quick_get_cpuctr(void)
 {
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime-Macpi.h linux-2.6.0-test5-hrt/include/asm-i386/hrtime-Macpi.h
--- linux-2.6.0-test5-hrt.orig/include/asm-i386/hrtime-Macpi.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/asm-i386/hrtime-Macpi.h	2003-12-08 22:01:19.000000000 +0100
@@ -38,6 +38,7 @@
 #define SIZE_MASK 0xffffff
 
 extern int acpi_pm_tmr_address;
+extern struct timer_opts hrtimer_pm;
 
 extern inline unsigned long
 quick_get_cpuctr(void)
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/asm-i386/mach-visws/do_timer.h linux-2.6.0-test5-hrt/include/asm-i386/mach-visws/do_timer.h
--- linux-2.6.0-test5-hrt.orig/include/asm-i386/mach-visws/do_timer.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/asm-i386/mach-visws/do_timer.h	2003-11-09 11:28:18.000000000 +0100
@@ -8,7 +8,7 @@
 	/* Clear the interrupt */
 	co_cpu_write(CO_CPU_STAT,co_cpu_read(CO_CPU_STAT) & ~CO_STAT_TIMEINTR);
 
-ifdef CONFIG_HIGH_RES_TIMERS
+#ifdef CONFIG_HIGH_RES_TIMERS
  
         { 
 		long arch_cycles = get_arch_cycles(jiffies);
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/linux/posix-timers.h linux-2.6.0-test5-hrt/include/linux/posix-timers.h
--- linux-2.6.0-test5-hrt.orig/include/linux/posix-timers.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/linux/posix-timers.h	2003-12-02 22:27:47.000000000 +0100
@@ -15,6 +15,49 @@
 	void (*timer_get) (struct k_itimer * timr,
 			   struct itimerspec * cur_setting);
 };
+
+#ifdef CONFIG_HIGH_RES_TIMERS
+struct now_struct {
+	unsigned long jiffies;
+	long sub_jiffie;
+};
+
+/*
+ * The following locking assumes that irq off.
+ */
+static inline void
+posix_get_now(struct now_struct *now)
+{
+	unsigned long seq;
+	
+	(now)->jiffies = jiffies;
+	do {
+		seq = read_seqbegin(&xtime_lock);
+		(now)->sub_jiffie = get_arch_cycles((now)->jiffies);
+	} while (read_seqretry(&xtime_lock, seq));
+	
+	while (unlikely(((now)->sub_jiffie - arch_cycles_per_jiffy) > 0)) {
+		(now)->sub_jiffie = (now)->sub_jiffie - arch_cycles_per_jiffy;
+		(now)->jiffies++;
+	}
+}
+
+#define posix_time_before(timer, now) \
+         ( {long diff = (long)(timer)->expires - (long)(now)->jiffies;  \
+           (diff < 0) ||                                      \
+	   ((diff == 0) && ((timer)->sub_expires < (now)->sub_jiffie)); })
+
+#define posix_bump_timer(timr) do { \
+          (timr)->it_timer.expires += (timr)->it_incr; \
+          (timr)->it_timer.sub_expires += (timr)->it_sub_incr; \
+          if (((timr)->it_timer.sub_expires - arch_cycles_per_jiffy) >= 0){ \
+		  (timr)->it_timer.sub_expires -= arch_cycles_per_jiffy; \
+		  (timr)->it_timer.expires++; \
+	  }                                 \
+          (timr)->it_overrun++;               \
+        }while (0)
+
+#else
 struct now_struct {
 	unsigned long jiffies;
 };
@@ -27,4 +70,5 @@
                         (timr)->it_timer.expires += (timr)->it_incr; \
                         (timr)->it_overrun++;               \
                        }while (0)
+#endif				/* CONFIG_HIGH_RES_TIMERS */
 #endif
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/include/linux/sc_math.h linux-2.6.0-test5-hrt/include/linux/sc_math.h
--- linux-2.6.0-test5-hrt.orig/include/linux/sc_math.h	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/include/linux/sc_math.h	2003-12-13 00:02:10.000000000 +0100
@@ -251,7 +251,7 @@
  */
 static inline long mpy_sc32(long a, long b)
 {
-        return (mpy1 * mpy2) >> 32);
+        return ((a * b) >> 32);
 }
 /*
  * X = (a/b)<<32 or more precisely x = (a<<32)/b
@@ -287,7 +287,7 @@
 /* x = (aa * bb) >> N */
 
 
-#define mpy_sc_n(N,aa,bb) ((aa) * (bb)) >> N)
+#define mpy_sc_n(N,aa,bb) (((aa) * (bb)) >> N)
 
 /* x = (aa << N / bb)  */
 #define div_sc_n(N,aa,bb) (SC_n((N), (aa)) / (bb))
diff -urN -X /home/eric/dontdiff -x '*.dg' linux-2.6.0-test5-hrt.orig/kernel/timer.c linux-2.6.0-test5-hrt/kernel/timer.c
--- linux-2.6.0-test5-hrt.orig/kernel/timer.c	2003-10-16 18:01:10.000000000 +0200
+++ linux-2.6.0-test5-hrt/kernel/timer.c	2003-12-12 23:16:25.000000000 +0100
@@ -98,7 +98,7 @@
  	 * must be cli-ed when calling this
  	 */
   	unsigned long expires = timer->expires;
- 	IF_HIGH_RES(int sub_expires = timer->sub_expires;)
+ 	IF_HIGH_RES(long sub_expires = timer->sub_expires;)
  		int indx;
  	struct list_head *pos,*list_start;
   
@@ -119,7 +119,7 @@
  	}
  			
  	indx =	expires & NEW_TVEC_MASK;
- 	if ((expires - base->timer_jiffies) <= NEW_TVEC_SIZE) {
+ 	if ((expires - base->timer_jiffies) < NEW_TVEC_SIZE) {
 #ifdef CONFIG_HIGH_RES_TIMERS
  		unsigned long jiffies_f;
   		/*
@@ -155,7 +155,8 @@
  		if ( expires == (jiffies_f = base->timer_jiffies) && 
  		     list_start->next == &timer->entry &&
  		     (base->running_timer == NULL)) {
- 			schedule_hr_timer_int(jiffies_f, sub_expires);
+ 			if (schedule_hr_timer_int(jiffies_f, sub_expires))
+				run_local_timers();
  		}
 #else
  		pos = (&base->tv[indx])->next;
@@ -433,8 +434,7 @@
 #else
 	while ((long)(jiffies - base->timer_jiffies) >= 0) {
 #endif
- 
-		struct list_head *head, *curr;
+		struct list_head *head;
 		head = base->tv + (base->timer_jiffies & NEW_TVEC_MASK);
 		/*
 		 * Note that we never move "head" but continue to
@@ -443,13 +443,12 @@
 		 * function call below.
 		 */
 repeat:
-		curr = head->next;
-		if (curr != head) {
+		if (!list_empty(head)) {
 			void (*fn)(unsigned long);
 			unsigned long data;
 			struct timer_list *timer;
 
-			timer = list_entry(curr, struct timer_list, entry);
+			timer = list_entry(head->next, struct timer_list, entry);
 #ifdef CONFIG_HIGH_RES_TIMERS
 			/*
 			 * This would be simpler if we never got behind
@@ -472,7 +471,7 @@
 				list_del(&timer->entry);
 				timer->base = NULL;
 				timer->entry.next = timer->entry.prev = NULL;
-				base->running_timer = timer;
+				set_running_timer(base, timer);
 				spin_unlock_irq(&base->lock);
 				fn(data);
 				spin_lock_irq(&base->lock);
@@ -483,7 +482,7 @@
 				/*
 				 * The new timer list is not always emptied
 				 * here as it contains:
-				 * a.) entries (list size)^N*jiffies out and
+				 * a.) entries (list size)*N jiffies out and
 				 * b.) entries that match in jiffies, but have
 				 *     sub_expire times further out than now.
 				 */
@@ -513,16 +512,15 @@
 	 * This allows zero time inserts as well as sub_jiffie values in
 	 * the current jiffie.
 	 */
-	--base->timer_jiffies;
 #ifdef CONFIG_HIGH_RES_TIMERS
-	if (!sub_jiff && schedule_jiffies_int(jiffies_f)) {
+	--base->timer_jiffies; //XXX needed when not HRT?
+	if ((sub_jiff == -1) && schedule_jiffies_int(jiffies_f)) {
 		spin_unlock_irq(&base->lock);
 		spin_lock_irq(&base->lock);
 		goto run_timer_list_again;
 	}
 #endif
-	base->running_timer = NULL;
-
+ 	set_running_timer(base, NULL);
 
 	spin_unlock_irq(&base->lock);
 }

[-- Attachment #3: 2.6.0-t5-hrt_syscalls.diff --]
[-- Type: text/plain, Size: 1095 bytes --]

--- linux-2.6.0-test7/include/asm-ia64/unistd.h.~1~	2003-10-08 21:24:02.000000000 +0200
+++ linux-2.6.0-test7/include/asm-ia64/unistd.h	2003-10-10 16:57:46.000000000 +0200
@@ -237,17 +237,17 @@
 #define __NR_epoll_wait			1245
 #define __NR_restart_syscall		1246
 #define __NR_semtimedop			1247
-#define __NR_sys_timer_create		1248
-#define __NR_sys_timer_settime		1249
-#define __NR_sys_timer_gettime		1250
-#define __NR_sys_timer_getoverrun	1251
-#define __NR_sys_timer_delete		1252
-#define __NR_sys_clock_settime		1253
-#define __NR_sys_clock_gettime		1254
-#define __NR_sys_clock_getres		1255
-#define __NR_sys_clock_nanosleep	1256
-#define __NR_sys_fstatfs64		1257
-#define __NR_sys_statfs64		1258
+#define __NR_timer_create		1248
+#define __NR_timer_settime		1249
+#define __NR_timer_gettime		1250
+#define __NR_timer_getoverrun		1251
+#define __NR_timer_delete		1252
+#define __NR_clock_settime		1253
+#define __NR_clock_gettime		1254
+#define __NR_clock_getres		1255
+#define __NR_clock_nanosleep		1256
+#define __NR_fstatfs64			1257
+#define __NR_statfs64			1258
 
 #ifdef __KERNEL__
 

[-- Attachment #4: hrtimers-support-ia64-2.6.0-t5-20031212.patch --]
[-- Type: application/octet-stream, Size: 2418 bytes --]

diff -urN -X patches/dontdiff -x timers -x time-precision 2.6.0-t5-hrt.orig/Documentation/high-res-timers/lib/syscall_timer.c 2.6.0-t5-hrt/Documentation/high-res-timers/lib/syscall_timer.c
--- 2.6.0-t5-hrt.orig/Documentation/high-res-timers/lib/syscall_timer.c	2003-12-12 15:28:14.000000000 +0100
+++ 2.6.0-t5-hrt/Documentation/high-res-timers/lib/syscall_timer.c	2003-12-08 18:05:37.000000000 +0100
@@ -11,6 +11,69 @@
 #ifndef __set_errno
 #define __set_errno(val)       (errno = (val))
 #endif
+#if defined(__ia64__)
+
+//#include <unistd.h>
+
+
+int timer_create(clockid_t which_clock, 
+                        struct sigevent *timer_event_spec,
+                        timer_t *created_timer_id)
+{
+	return syscall(__NR_timer_create, which_clock, timer_event_spec, created_timer_id);
+}
+ 
+int timer_gettime(timer_t timer_id, struct itimerspec *setting)
+{
+	return syscall(__NR_timer_gettime, timer_id, setting);
+}
+
+int timer_settime(timer_t timer_id,
+                         int flags,
+                         const struct itimerspec *new_setting,
+                         struct itimerspec *old_setting) 
+{
+	return syscall(__NR_timer_settime, timer_id, flags, new_setting, old_setting);
+}
+
+int timer_getoverrun(timer_t timer_id)
+{
+	return syscall(__NR_timer_getoverrun, timer_id);
+}
+
+int timer_delete(timer_t timer_id) 
+{
+	return syscall(__NR_timer_delete, timer_id);
+}
+
+int clock_gettime(clockid_t which_clock, struct timespec *ts) 
+{
+	return syscall(__NR_clock_gettime, which_clock, ts);
+}
+
+int clock_settime(clockid_t which_clock, 
+                         const struct timespec *setting) 
+{
+	return syscall(__NR_clock_settime, which_clock, setting);
+}
+
+int clock_getres(clockid_t which_clock, 
+                        struct timespec *resolution) 
+{
+	return syscall(__NR_clock_getres, which_clock, resolution);
+}
+
+int clock_nanosleep(clockid_t which_clock,
+                    int flags,
+                    const struct timespec *new_setting, 
+                    struct timespec *old_setting) 
+{
+	return syscall(__NR_clock_nanosleep, which_clock, flags, new_setting, old_setting);
+}
+
+
+
+#else /*! __ia64__ */ 
 
 #define __NR___timer_create     __NR_timer_create
 #define __NR___timer_gettime    __NR_timer_gettime
@@ -98,4 +161,4 @@
           int, flags,
           const struct timespec *,rqtp,
           struct timespec *,rmtp)
-
+#endif /*ia64*/

[-- Attachment #5: hrtimers-ia64-2.6.0-test5-20031218.patch --]
[-- Type: application/octet-stream, Size: 15404 bytes --]

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 @@
 	  <http://www.tldp.org/docs.html#howto>.
 
 	  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:
+
+	  <timer>				<resolution>
+	  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 <drummond@valinux.com>
  */
+#define _INCLUDED_FROM_TIME_C
 #include <linux/config.h>
 
 #include <linux/init.h>
@@ -19,6 +20,7 @@
 #include <linux/efi.h>
 #include <linux/profile.h>
 #include <linux/timex.h>
+#include <linux/hrtime.h>
 
 #include <asm/delay.h>
 #include <asm/hw_irq.h>
@@ -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 <linux/config.h>	/* for CONFIG_APM etc... */
+#include <asm/types.h>		/* for u16s */
+#include <linux/sc_math.h>	/* scaling math routines */
+#include <asm/delay.h>
+#include <linux/smp.h>
+#include <linux/module.h>	/* for EXPORT_SYMBOL */
+#include <asm/percpu.h>
+
+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 <asm/hrtime-itc.h>
+#elif defined(CONFIG_HIGH_RES_TIMER_HPET)
+# include <asm/hrtime-hpet.h>
+#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 <asm/io.h>
+
+/*
+ *  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;


                 reply	other threads:[~2003-12-18 16:44 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=marc-linux-ia64-107176619506875@msgid-missing \
    --to=eric.piel@tremplin-utc.net \
    --cc=linux-ia64@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox