Skip to contentSkip to conversation

High-res-timers part 2 (x86 platform code)

PATCH In review george anzinger 2 participants 2 messages Sep 27, 2002

lore
Share & tools
lore
#0 lore raw

PATCH 1/1 High-res-timers part 2 (x86 platform code)

This patch, in conjunction with the "core" high-res-timers patch implements high resolution timers on the i386 platforms. The high-res-timers use the periodic interrupt to "remind" the system to look at the clock. The clock should be relatively high resolution (1 micro second or better). This patch allows configuring of three possible clocks, the TSC, the ACPM pm timer, or the Programmable interrupt timer (PIT). Most of the changes in this patch are in the arch/i386/time.c code.

With this patch applied and enabled (at config time in the processor feature section), the system clock will be the specified clock. The PIT is not used to keep track of time, but only to remind the system to look at the clock. Sub jiffies are kept and available for code that knows how to use them.

Signature · 5 lines
George Anzinger   [email protected]
High-res-timers: 
http://sourceforge.net/projects/high-res-timers/
Preemption patch:
http://www.kernel.org/pub/linux/kernel/people/rml
arch/i386/Config.help +69 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/arch/i386/Config.help linux/arch/i386/Config.help
@@ -44,6 +44,75 @@
   Say Y here if you are building a kernel for a desktop, embedded
   or real-time system.  Say N if you are unsure.
 
+High-res-timers
+CONFIG_HIGH_RES_TIMERS
+  POSIX timers are available by default.  This option enables high
+  resolution POSIX timers.  With this option the resolution is at
+  least 1 micro second.  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 boundry.  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.
+
+High-res-timers clock
+CONFIG_HIGH_RES_TIMER_ACPI_PM 
+  This option allows you to choose the wall clock timer for your system.
+  With high resolution timers on the x86 platforms it is best to keep
+  the interrupt generating timer separate from the time keeping timer.
+  On x86 platforms there are three possible sources implemented for the
+  wall clock.  These are:
+ 
+  <timer>				<resolution>
+  ACPI power management (pm) timer	~280 nano seconds
+  TSC (Time Stamp Counter)		1/CPU clock
+  PIT (Programmable Interrupt Timer)	~838 nano seconds
+
+  The PIT is used to generate interrupts and at any given time will be
+  programmed to interrupt when the next timer is to expire or on the
+  next 1/HZ tick.  For this reason it is best to not use this timer as
+  the wall clock timer.  This timer has a resolution of 838 nano
+  seconds.  THIS OPTION SHOULD ONLY BE USED IF BOTH ACPI AND TSC ARE
+  NOT AVAILABLE.
+
+  The TSC runs at the cpu clock rate (i.e. its resolution is 1/CPU
+  clock) and it has a very low access time.  However, it is subject,
+  in some (incorrect) processors, to throttling to cool the cpu, and
+  to other slow downs during power management.  If your cpu is correct
+  and does not change the TSC frequency for throttling or power
+  management this is the best clock timer.
+
+  The ACPI pm timer is available on systems with Advanced Configuration
+  and Power Interface support.  The pm timer is available on these
+  systems even if you don't use or enable ACPI in the software or the
+  BIOS (but see Default ACPI pm timer address).  The timer has a
+  resolution of about 280 nanoseconds, however, the access time is a bit
+  higher that that of the TSC.  Since it is part of ACPI it is intended
+  to keep track of time while the system is under power management, thus
+  it is not subject to the problems of the TSC.
+
+  If you enable the ACPI pm timer and it can not be found, it is
+  possible that your BIOS is not producing the ACPI table or that your
+  machine does not support ACPI.  In the former case, see "Default ACPI
+  pm timer address".  If the timer is not found the boot will fail when
+  trying to calibrate the delay timer.
+
+Default ACPI pm timer address
+CONFIG_HIGH_RES_TIMER_ACPI_PM_ADD
+  This option is available for use on systems where the BIOS does not
+  generate the ACPI tables if ACPI is not enabled.  For example some
+  BIOSes will not generate the ACPI tables if APM is enabled.  The ACPI
+  pm timer is still available but can not be found by the software.
+  This option allows you to supply the needed address.  When the high
+  resolution timers code finds a valid ACPI pm timer address it reports
+  it in the boot messages log (look for lines that begin with
+  "High-res-timers:").  You can turn on the ACPI support in the BIOS,
+  boot the system and find this value.  You can then enter it at
+  configure time.  Both the report and the entry are in decimal.
+
 CONFIG_X86
   This is Linux's home port.  Linux was originally native to the Intel
   386, and runs on all the later x86 processors including the Intel
arch/i386/config.in +17 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/arch/i386/config.in linux/arch/i386/config.in
@@ -157,6 +157,23 @@
 bool 'Huge TLB Page Support' CONFIG_HUGETLB_PAGE
 
 bool 'Symmetric multi-processing support' CONFIG_SMP
+bool 'Configure High-Resolution-Timers' CONFIG_HIGH_RES_TIMERS
+#
+# We assume that if the box doesn't have a TSC it doesn't have ACPI either.
+#
+if [ "$CONFIG_HIGH_RES_TIMERS" = "y" -a "$CONFIG_X86_TSC" = "y" ]; then
+	choice 'Clock source?' \
+		"ACPI-pm-timer  CONFIG_HIGH_RES_TIMER_ACPI_PM  \
+		Time-stamp-counter/TSC  CONFIG_HIGH_RES_TIMER_TSC \
+		Programable-interrupt-timer/PIT CONFIG_HIGH_RES_TIMER_PIT" Time-stamp-counter/TSC
+else
+	if [ "$CONFIG_HIGH_RES_TIMERS" = "y" ]; then
+		define_bool CONFIG_HIGH_RES_TIMER_PIT y
+        fi
+fi
+if [ "$CONFIG_HIGH_RES_TIMER_ACPI_PM" = "y" ]; then
+	int 'Default ACPI pm timer address' CONFIG_HIGH_RES_TIMER_ACPI_PM_ADD 0
+fi 
 bool 'Preemptible Kernel' CONFIG_PREEMPT
 if [ "$CONFIG_SMP" != "y" ]; then
    bool 'Local APIC support on uniprocessors' CONFIG_X86_UP_APIC
arch/i386/kernel/Makefile +1 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/arch/i386/kernel/Makefile linux/arch/i386/kernel/Makefile
@@ -16,6 +16,7 @@
 obj-$(CONFIG_MCA)		+= mca.o
 obj-$(CONFIG_X86_MSR)		+= msr.o
 obj-$(CONFIG_X86_CPUID)		+= cpuid.o
+obj-$(CONFIG_HIGH_RES_TIMER_ACPI_PM) += high-res-tbxfroot.o
 obj-$(CONFIG_MICROCODE)		+= microcode.o
 obj-$(CONFIG_APM)		+= apm.o
 obj-$(CONFIG_ACPI)		+= acpi.o
arch/i386/kernel/high-res-tbxfroot.c +272 -0

272 lines not on this page · raw message

arch/i386/kernel/time.c +220 -36

416 lines not on this page · raw message

arch/i386/mach-generic/do_timer.h +5 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/arch/i386/mach-generic/do_timer.h linux/arch/i386/mach-generic/do_timer.h
@@ -14,6 +14,11 @@
 static inline void do_timer_interrupt_hook(struct pt_regs *regs)
 {
 	do_timer(regs);
+        IF_HIGH_RES(
+                if (!new_jiffie())
+                        return;
+                jiffies_intr = 0;
+                )
 /*
  * In the SMP case we use the local APIC timer interrupt to do the
  * profiling, except when we simulate SMP mode on a uniprocessor
include/asm-i386/hrtime-M386.h +247 -0

247 lines not on this page · raw message

include/asm-i386/hrtime-M586.h +160 -0

160 lines not on this page · raw message

include/asm-i386/hrtime-Macpi.h +195 -0

195 lines not on this page · raw message

include/asm-i386/hrtime.h +458 -0

458 lines not on this page · raw message

include/asm-i386/sc_math.h +143 -0

143 lines not on this page · raw message

include/asm-i386/signal.h +38 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/include/asm-i386/signal.h linux/include/asm-i386/signal.h
@@ -216,9 +216,47 @@
 	__asm__("bsfl %1,%0" : "=r"(word) : "rm"(word) : "cc");
 	return word;
 }
+/*
+ * These macros are used by nanosleep() however, they could be used
+ * by other code that needs the pt_regs.  These are passed in different
+ * ways by the various platforms.  In the x86, the address of the first
+ * parameter is the address of the registers (and the first param is in 
+ * a register because of the asmcall stuff.
+ * Darn, I give up.  No more generic stuff.  Here we code the required 
+ * entry for selected given functions.  One way or another, we will let 
+ * the various archs work out how they want to deal with it.  Too bad 
+ * there isn't a fixed way to grab the pt_regs...
 
+ * And while were at it, there needs to be a way to set the return code
+ * on the way to do_signal().  It (i.e. do_signal()) saves the regs on 
+ * the callers stack to call the user handler and then the return is
+ * done using those registers.  This means that the error code MUST be
+ * set in the register PRIOR to calling do_signal().  See our answer 
+ * below...thanks to  Jim Houston <[email protected]>
+ */
 struct pt_regs;
 extern int FASTCALL(do_signal(struct pt_regs *regs, sigset_t *oldset));
+
+#define NANOSLEEP_ENTRY(a) asmlinkage long sys_nanosleep( struct timespec* rqtp, \
+                                                          struct timespec * rmtp) \
+{       struct pt_regs *regs = (struct pt_regs *)&rqtp; \
+        a
+
+#define CLOCK_NANOSLEEP_ENTRY(a) asmlinkage long sys_clock_nanosleep( \
+                               clockid_t which_clock,      \
+                               int flags,                  \
+                               const struct timespec *rqtp, \
+                               struct timespec *rmtp)       \
+{       struct pt_regs *regs = (struct pt_regs *)&which_clock; \
+        a
+
+#define PT_REGS_ENTRY(type,name,p1_type,p1, p2_type,p2) \
+type name(p1_type p1,p2_type p2)\
+{       struct pt_regs *regs = (struct pt_regs *)&p1;
+
+#define _do_signal() (regs->eax = -EINTR, do_signal(regs, NULL))
+
+
 
 #endif /* __KERNEL__ */
 
include/linux/hrtime.h +1 -0
diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/include/linux/hrtime.h linux/include/linux/hrtime.h
@@ -10,6 +10,7 @@
  * arch defines the CONFIG, it had better have the 
  * asm/hrtime.h file in place.
  */
+
 /*
  * This gets filled in at init time, either static or dynamic.
  * Someday this will be what NTP fiddles with.
#1 lore raw
Quoted 10 lines · george anzinger

This patch, in conjunction with the "core" high-res-timers patch implements high resolution timers on the i386 platforms. The high-res-timers use the periodic interrupt to "remind" the system to look at the clock. The clock should be relatively high resolution (1 micro second or better). This patch allows configuring of three possible clocks, the TSC, the ACPM pm timer, or the Programmable interrupt timer (PIT). Most of the changes in this patch are in the arch/i386/time.c code.

Quoted 6 lines

With this patch applied and enabled (at config time in the processor feature section), the system clock will be the specified clock. The PIT is not used to keep track of time, but only to remind the system to look at the clock. Sub jiffies are kept and available for code that knows how to use them.

Looks very cool. There are a couple of nice cleanups that I feel could be submitted outside the scope of this patch. However if this just goes in it won't really matter. Anyway, I'll point them out below just in case this doesn't slip right in.

<snip>

Quoted 3 lines

diff -urP -I \$Id:.*Exp \$ -X /usr/src/patch.exclude linux-2.5.38-bk5-core/arch/i386/kernel/time.c linux/arch/i386/kernel/time.c

arch/i386/kernel/time.c +0 -0

<snip>

Quoted 8 lines

+/*
+ * We have three of these do_xxx_gettimeoffset() routines:
+ * do_fast_gettimeoffset(void) for TSC systems with out high-res-timers
+ * do_slow_gettimeoffset(void) for ~TSC systems with out high-res-timers
+ * do_highres__gettimeoffset(void) for systems with high-res-timers
+ *
+ * Pick the desired one at compile time...
+ */

<snip>

arch/i386/kernel/time.c line 148

Quoted 3 lines
arch/i386/kernel/time.c +1 -2
-#ifndef CONFIG_X86_TSC
-
+#if  ! defined(CONFIG_HIGH_RES_TIMERS) && ! defined(CONFIG_X86_TSC)

Well, first a plug. My timer-changes patch makes most of this ugliness go away. I'll clean it up should your code go in first, however do take a look at the way I'm abstracting out the timer_ops structure. I'm sure you have some insights as to what extra interfaces could be useful.

Quoted 31 lines
arch/i386/kernel/time.c +12 -3
@@ -240,16 +272,25 @@
 	read_lock_irqsave(&xtime_lock, flags);
 	usec = do_gettimeoffset();
 	{
+                /*
+                 * FIX ME***** Due to adjtime and such
+                 * this should be changed to actually update
+                 * wall time using the proper routine.
+                 * Otherwise we run the risk of time moving
+                 * backward due to different interpretations
+                 * of the jiffie.  I.e jiffie != 1/HZ
+                 * (but it is close).
+                 */
 		unsigned long lost = jiffies - wall_jiffies;
 		if (lost)
-			usec += lost * (1000000 / HZ);
+			usec += lost * (USEC_PER_SEC / HZ);
 	}
 	sec = xtime.tv_sec;
 	usec += (xtime.tv_nsec / 1000);
 	read_unlock_irqrestore(&xtime_lock, flags);
 
-	while (usec >= 1000000) {
-		usec -= 1000000;
+	while (usec >= USEC_PER_SEC) {
+		usec -= USEC_PER_SEC;
 		sec++;
 	}
 
@@ -267,10 +308,10 @@
 	 * made, and then undo it!

arch/i386/kernel/time.c line 316

Quoted 10 lines
arch/i386/kernel/time.c +2 -2
 	 */
 	tv->tv_usec -= do_gettimeoffset();
-	tv->tv_usec -= (jiffies - wall_jiffies) * (1000000 / HZ);
+	tv->tv_usec -= (jiffies - wall_jiffies) * (USEC_PER_SEC / HZ);
 
 	while (tv->tv_usec < 0) {
-		tv->tv_usec += 1000000;
+		tv->tv_usec += USEC_PER_SEC;
 		tv->tv_sec--;
 	}

These look like very good just general cleanups

Quoted 15 lines
arch/i386/kernel/time.c +4 -0
@@ -439,6 +474,10 @@
 		 * the same time. We do the RDTSC stuff first, since it's
 		 * faster. To avoid any inconsistencies, we need interrupts
 		 * disabled locally.
+                 * Note: It is dumb to put the spin_lock() between these two
+                 * operations since we are trying to sync the two clocks.
+                 * Also, the rdtscl is so fast, know one will know the
+                 * difference.
 		 */
 
 		/*
@@ -446,11 +485,11 @@
 		 * has the SA_INTERRUPT flag set. -arca
 		 */
 

arch/i386/kernel/time.c line 495

Quoted 10 lines
arch/i386/kernel/time.c +2 -2
-		/* read Pentium cycle counter */
 
+		spin_lock(&i8253_lock);
+		/* read Pentium cycle counter */
 		rdtscl(last_tsc_low);
 
-		spin_lock(&i8253_lock);
 		outb_p(0x00, 0x43);     /* latch the count ASAP */
 
 		count = inb_p(0x40);    /* read the latched count */

This should def go in! Grabbing a lock in between isn't smart. Very good fix!

There are probably more, but I gotta' get home.
I'll look at it more later.

very cool, though.
thanks
-john