Skip to contentSkip to conversation

The alternate Posix timers patch8

PATCH New Jim Houston 1 participant 1 message Dec 16, 2002

Subsystems (7)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 The alternate Posix timers patch8

Hi Everyone,

This is the 8th version of my spin on the Posix timers. This patch works with linux-2.5.51.

This version fixes problems found using the Open Posix test suite
(http://posixtest.sourceforge.net/). In particular, I fixed problems with overruns and interactions between pending timers and setting the time. Thanks Julie and everyone else involved in writing these tests.

This patch is based on George Anzinger's Posix timers patch. My kernel work still relies on his user library and test code. Please see his page here:
http://sourceforge.net/projects/high-res-timers

Here is a summary of my changes:

- I keep the timers in seconds and nano-seconds. The mechanism
to expire the timers will work either with a periodic interrupt or a programable timer. This patch provides high resolution by sharing the local APIC timer.

- Changes to the arch/i386/kernel/timers code to use nanoseconds
consistently. I added do_[get/set]timeofday_ns() to get/set time in nanoseconds. I also added a monotonic time since boot clock do_gettime_sinceboot_ns().

- The posix timers are queued in their own queue. This avoids
interactions with the jiffie based timers.
I implemented this priority queue as a sorted list with a rbtree to index the list. It is deterministic and fast.
I want my posix timers to have low jitter so I will expire them directly from the interrupt. Having a separate queue gives me this flexibilty.

- A new id allocator/lookup mechanism based on a radix tree. It
includes a bitmap to summarize the portion of the tree which is in use. (George picked this up from me.) My version doesn't immediately re-use the id when it is freed. This is intended to catch application errors e.g. continuing to use a timer after it is destroyed.

- Code to limit the rate at which timers expire. Without this, an
errant program could swamp the system with interrupts. I added a sysctl interface to adjust the parameters which control this. It includes the resolution for posix timers and nanosleep and three values which set a duty cycle for timer expiry. It limits the number of timers expired from a single interrupt. If the system hits this limit, it waits a recovery time before expiring more timers.

- Uses the new ERESTART_RESTARTBLOCK interface to restart
nanosleep and clock_nanosleep calls which are interrupted but not delivered (e.g. debug signals).

Actually I use clock_nanosleep to implement nanosleep. This lets me play with the resolution which nanosleep supports.

- Andrea Arcangeli convinced me that the remaining time for
an interrupted nanosleep has to be precise not rounded to the nearest clock tick. This is fixed and the ltp nanosleep02 test passes.

Since I rely on the standard time, I have been seeing the existing problems with time keeping (bugzilla.kernel.org bug #100 and #105). I find that switching HZ back to 100 helps.

Jim Houston - Concurrent Computer Corp.

arch/i386/kernel/apic.c +35 -1
diff -X dontdiff -urN linux-2.5.51.orig/arch/i386/kernel/apic.c linux-2.5.51.timers/arch/i386/kernel/apic.c
@@ -32,6 +32,7 @@
 #include <asm/desc.h>
 #include <asm/arch_hooks.h>
 #include "mach_apic.h"
+#include <asm/div64.h>
 
 void __init apic_intr_init(void)
 {
@@ -807,7 +808,7 @@
 	unsigned int lvtt1_value, tmp_value;
 
 	lvtt1_value = SET_APIC_TIMER_BASE(APIC_TIMER_BASE_DIV) |
-			APIC_LVT_TIMER_PERIODIC | LOCAL_TIMER_VECTOR;
+			LOCAL_TIMER_VECTOR;
 	apic_write_around(APIC_LVTT, lvtt1_value);
 
 	/*
@@ -916,6 +917,31 @@
 
 static unsigned int calibration_result;
 
+/*
+ * Set the APIC timer for a one shot expiry in nanoseconds.
+ * This is called from the posix-timers code.
+ */
+int ns2clock;
+void set_APIC_timer(int ns)
+{
+	long long tmp;
+	int clocks;
+	unsigned int  tmp_value;
+
+	if (!ns2clock) {
+		tmp = (calibration_result * HZ);
+		tmp = tmp << 32;
+		do_div(tmp, 1000000000);
+		ns2clock = (int)tmp;
+		clocks = ((long long)ns2clock * ns) >> 32;
+	}
+	clocks = ((long long)ns2clock * ns) >> 32;
+	tmp_value = apic_read(APIC_TMCCT);
+	if (!tmp_value || clocks/APIC_DIVISOR < tmp_value)
+		apic_write_around(APIC_TMICT, clocks/APIC_DIVISOR);
+}
+
+
 int dont_use_local_apic_timer __initdata = 0;
 
 void __init setup_boot_APIC_clock(void)
@@ -1005,9 +1031,17 @@
  * value into /proc/profile.
  */
 
+long get_eip(void *regs)
+{
+	return(((struct pt_regs *)regs)->eip);
+}
+
 inline void smp_local_timer_interrupt(struct pt_regs * regs)
 {
 	int cpu = smp_processor_id();
+
+	if (!run_posix_timers((void *)regs)) 
+		return;
 
 	x86_do_profile(regs);
 
arch/i386/kernel/entry.S +9 -0
diff -X dontdiff -urN linux-2.5.51.orig/arch/i386/kernel/entry.S linux-2.5.51.timers/arch/i386/kernel/entry.S
@@ -743,6 +743,15 @@
 	.long sys_epoll_wait
  	.long sys_remap_file_pages
  	.long sys_set_tid_address
+ 	.long sys_timer_create
+	.long sys_timer_settime	/* 260 */
+	.long sys_timer_gettime
+ 	.long sys_timer_getoverrun
+ 	.long sys_timer_delete
+ 	.long sys_clock_settime
+ 	.long sys_clock_gettime	/* 265 */
+ 	.long sys_clock_getres
+	.long sys_clock_nanosleep
 
 
 	.rept NR_syscalls-(.-sys_call_table)/4
arch/i386/kernel/smpboot.c +1 -3
diff -X dontdiff -urN linux-2.5.51.orig/arch/i386/kernel/smpboot.c linux-2.5.51.timers/arch/i386/kernel/smpboot.c
@@ -181,8 +181,6 @@
 
 #define NR_LOOPS 5
 
-extern unsigned long fast_gettimeoffset_quotient;
-
 /*
  * accurate 64-bit/32-bit division, expanded to 32-bit divisions and 64-bit
  * multiplication. Not terribly optimized but we need it at boot time only
@@ -222,7 +220,7 @@
 
 	printk("checking TSC synchronization across %u CPUs: ", num_booting_cpus());
 
-	one_usec = ((1<<30)/fast_gettimeoffset_quotient)*(1<<2);
+	one_usec = cpu_khz/1000;
 
 	atomic_set(&tsc_start_flag, 1);
 	wmb();
arch/i386/kernel/time.c +71 -24

132 lines not on this page · raw message

arch/i386/kernel/timers/timer_cyclone.c +4 -4
diff -X dontdiff -urN linux-2.5.51.orig/arch/i386/kernel/timers/timer_cyclone.c linux-2.5.51.timers/arch/i386/kernel/timers/timer_cyclone.c
@@ -47,7 +47,7 @@
 	count |= inb(0x40) << 8;
 	spin_unlock(&i8253_lock);
 
-	count = ((LATCH-1) - count) * TICK_SIZE;
+	count = ((LATCH-1) - count) * tick_nsec;
 	delay_at_last_interrupt = (count + LATCH/2) / LATCH;
 }
 
@@ -64,11 +64,11 @@
 	/* .. relative to previous jiffy */
 	offset = offset - last_cyclone_timer;
 
-	/* convert cyclone ticks to microseconds */	
+	/* convert cyclone ticks to nanoseconds */	
 	/* XXX slow, can we speed this up? */
-	offset = offset/(CYCLONE_TIMER_FREQ/1000000);
+	offset = offset*(1000000000/CYCLONE_TIMER_FREQ);
 
-	/* our adjusted time offset in microseconds */
+	/* our adjusted time offset in nanoseconds */
 	return delay_at_last_interrupt + offset;
 }
 
arch/i386/kernel/timers/timer_pit.c +1 -1
diff -X dontdiff -urN linux-2.5.51.orig/arch/i386/kernel/timers/timer_pit.c linux-2.5.51.timers/arch/i386/kernel/timers/timer_pit.c
@@ -115,7 +115,7 @@
 
 	count_p = count;
 
-	count = ((LATCH-1) - count) * TICK_SIZE;
+	count = ((LATCH-1) - count) * tick_nsec;
 	count = (count + LATCH/2) / LATCH;
 
 	return count;
arch/i386/kernel/timers/timer_tsc.c +26 -19

115 lines not on this page · raw message

fs/exec.c +1 -0
diff -X dontdiff -urN linux-2.5.51.orig/fs/exec.c linux-2.5.51.timers/fs/exec.c
@@ -779,6 +779,7 @@
 			
 	flush_signal_handlers(current);
 	flush_old_files(current->files);
+	exit_itimers(current, 0);
 
 	return 0;
 
include/asm-generic/siginfo.h +8 -4
diff -X dontdiff -urN linux-2.5.51.orig/include/asm-generic/siginfo.h linux-2.5.51.timers/include/asm-generic/siginfo.h
@@ -43,8 +43,9 @@
 
 		/* POSIX.1b timers */
 		struct {
-			unsigned int _timer1;
-			unsigned int _timer2;
+			timer_t _tid;		/* timer id */
+			int _overrun;		/* overrun count */
+			sigval_t _sigval;	/* same as below */
 		} _timer;
 
 		/* POSIX.1b signals */
@@ -86,8 +87,8 @@
  */
 #define si_pid		_sifields._kill._pid
 #define si_uid		_sifields._kill._uid
-#define si_timer1	_sifields._timer._timer1
-#define si_timer2	_sifields._timer._timer2
+#define si_tid		_sifields._timer._tid
+#define si_overrun	_sifields._timer._overrun
 #define si_status	_sifields._sigchld._status
 #define si_utime	_sifields._sigchld._utime
 #define si_stime	_sifields._sigchld._stime
@@ -221,6 +222,7 @@
 #define SIGEV_SIGNAL	0	/* notify via signal */
 #define SIGEV_NONE	1	/* other notification: meaningless */
 #define SIGEV_THREAD	2	/* deliver via thread creation */
+#define SIGEV_THREAD_ID 4	/* deliver to thread */
 
 #define SIGEV_MAX_SIZE	64
 #ifndef SIGEV_PAD_SIZE
@@ -235,6 +237,7 @@
 	int sigev_notify;
 	union {
 		int _pad[SIGEV_PAD_SIZE];
+		 int _tid;
 
 		struct {
 			void (*_function)(sigval_t);
@@ -247,6 +250,7 @@
 
 #define sigev_notify_function	_sigev_un._sigev_thread._function
 #define sigev_notify_attributes	_sigev_un._sigev_thread._attribute
+#define sigev_notify_thread_id	 _sigev_un._tid
 
 #ifdef __KERNEL__
 
include/asm-i386/posix_types.h +2 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/asm-i386/posix_types.h linux-2.5.51.timers/include/asm-i386/posix_types.h
@@ -22,6 +22,8 @@
 typedef long		__kernel_time_t;
 typedef long		__kernel_suseconds_t;
 typedef long		__kernel_clock_t;
+typedef int		__kernel_timer_t;
+typedef int		__kernel_clockid_t;
 typedef int		__kernel_daddr_t;
 typedef char *		__kernel_caddr_t;
 typedef unsigned short	__kernel_uid16_t;
include/asm-i386/unistd.h +9 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/asm-i386/unistd.h linux-2.5.51.timers/include/asm-i386/unistd.h
@@ -264,6 +264,15 @@
 #define __NR_epoll_wait		256
 #define __NR_remap_file_pages	257
 #define __NR_set_tid_address	258
+#define __NR_timer_create	259
+#define __NR_timer_settime	(__NR_timer_create+1)
+#define __NR_timer_gettime	(__NR_timer_create+2)
+#define __NR_timer_getoverrun	(__NR_timer_create+3)
+#define __NR_timer_delete	(__NR_timer_create+4)
+#define __NR_clock_settime	(__NR_timer_create+5)
+#define __NR_clock_gettime	(__NR_timer_create+6)
+#define __NR_clock_getres	(__NR_timer_create+7)
+#define __NR_clock_nanosleep	(__NR_timer_create+8)
 
 
 /* user-visible error numbers are in the range -1 - -124: see <asm-i386/errno.h> */
include/linux/id2ptr.h +47 -0

47 lines not on this page · raw message

include/linux/init_task.h +4 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/linux/init_task.h linux-2.5.51.timers/include/linux/init_task.h
@@ -93,6 +93,10 @@
 	.sig		= &init_signals,				\
 	.pending	= { NULL, &tsk.pending.head, {{0}}},		\
 	.blocked	= {{0}},					\
+	.posix_timers	= LIST_HEAD_INIT(tsk.posix_timers),		\
+	.nanosleep_tmr.it_v.it_interval.tv_sec = 0,			\
+	.nanosleep_tmr.it_v.it_interval.tv_nsec = 0,			\
+	.nanosleep_tmr.it_process = &tsk,				\
 	.alloc_lock	= SPIN_LOCK_UNLOCKED,				\
 	.switch_lock	= SPIN_LOCK_UNLOCKED,				\
 	.journal_info	= NULL,						\
include/linux/posix-timers.h +62 -0

62 lines not on this page · raw message

include/linux/sched.h +5 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/linux/sched.h linux-2.5.51.timers/include/linux/sched.h
@@ -27,6 +27,7 @@
 #include <linux/compiler.h>
 #include <linux/completion.h>
 #include <linux/pid.h>
+#include <linux/posix-timers.h>
 
 struct exec_domain;
 
@@ -339,6 +340,9 @@
 	unsigned long it_real_value, it_prof_value, it_virt_value;
 	unsigned long it_real_incr, it_prof_incr, it_virt_incr;
 	struct timer_list real_timer;
+	struct list_head posix_timers; /* POSIX.1b Interval Timers */
+	struct k_itimer nanosleep_tmr;
+	struct timespec nanosleep_ts;	/* un-rounded completion time */
 	unsigned long utime, stime, cutime, cstime;
 	unsigned long start_time;
 /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
@@ -577,6 +581,7 @@
 
 extern void exit_mm(struct task_struct *);
 extern void exit_files(struct task_struct *);
+extern void exit_itimers(struct task_struct *, int);
 extern void exit_sighand(struct task_struct *);
 extern void __exit_sighand(struct task_struct *);
 
include/linux/sys.h +1 -1
diff -X dontdiff -urN linux-2.5.51.orig/include/linux/sys.h linux-2.5.51.timers/include/linux/sys.h
@@ -4,7 +4,7 @@
 /*
  * system call entry points ... but not all are defined
  */
-#define NR_syscalls 260
+#define NR_syscalls 275
 
 /*
  * These are system calls that will be removed at some time
include/linux/sysctl.h +11 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/linux/sysctl.h linux-2.5.51.timers/include/linux/sysctl.h
@@ -129,6 +129,7 @@
 	KERN_CADPID=54,		/* int: PID of the process to notify on CAD */
 	KERN_PIDMAX=55,		/* int: PID # limit */
   	KERN_CORE_PATTERN=56,	/* string: pattern for core-file names */
+  	KERN_POSIX_TIMERS=57,	/* posix timer parameters */
 };
 
 
@@ -188,6 +189,16 @@
 	RANDOM_WRITE_THRESH=4,
 	RANDOM_BOOT_ID=5,
 	RANDOM_UUID=6
+};
+
+/* /proc/sys/kernel/posix-timers */
+enum
+{
+	POSIX_TIMERS_RESOLUTION=1,
+	POSIX_TIMERS_NANOSLEEP_RES=2,
+	POSIX_TIMERS_MAX_EXPIRIES=3,
+	POSIX_TIMERS_RECOVERY_TIME=4,
+	POSIX_TIMERS_MIN_DELAY=5
 };
 
 /* /proc/sys/bus/isa */
include/linux/time.h +41 -1

68 lines not on this page · raw message

include/linux/types.h +2 -0
diff -X dontdiff -urN linux-2.5.51.orig/include/linux/types.h linux-2.5.51.timers/include/linux/types.h
@@ -23,6 +23,8 @@
 typedef __kernel_daddr_t	daddr_t;
 typedef __kernel_key_t		key_t;
 typedef __kernel_suseconds_t	suseconds_t;
+typedef __kernel_timer_t	timer_t;
+typedef __kernel_clockid_t	clockid_t;
 
 #ifdef __KERNEL__
 typedef __kernel_uid32_t	uid_t;
kernel/Makefile +1 -1
diff -X dontdiff -urN linux-2.5.51.orig/kernel/Makefile linux-2.5.51.timers/kernel/Makefile
@@ -10,7 +10,7 @@
 	    exit.o itimer.o time.o softirq.o resource.o \
 	    sysctl.o capability.o ptrace.o timer.o user.o \
 	    signal.o sys.o kmod.o workqueue.o futex.o platform.o pid.o \
-	    rcupdate.o intermodule.o extable.o
+	    rcupdate.o intermodule.o extable.o posix-timers.o id2ptr.o
 
 obj-$(CONFIG_GENERIC_ISA_DMA) += dma.o
 obj-$(CONFIG_SMP) += cpu.o
kernel/exit.c +1 -0
diff -X dontdiff -urN linux-2.5.51.orig/kernel/exit.c linux-2.5.51.timers/kernel/exit.c
@@ -659,6 +659,7 @@
 	__exit_files(tsk);
 	__exit_fs(tsk);
 	exit_namespace(tsk);
+	exit_itimers(tsk, 1);
 	exit_thread();
 
 	if (current->leader)
kernel/fork.c +6 -0
diff -X dontdiff -urN linux-2.5.51.orig/kernel/fork.c linux-2.5.51.timers/kernel/fork.c
@@ -810,6 +810,12 @@
 		goto bad_fork_cleanup_files;
 	if (copy_sighand(clone_flags, p))
 		goto bad_fork_cleanup_fs;
+	INIT_LIST_HEAD(&p->posix_timers);
+	p->nanosleep_tmr.it_v.it_interval.tv_sec = 0;
+	p->nanosleep_tmr.it_v.it_interval.tv_nsec = 0;
+	p->nanosleep_tmr.it_process = p;
+	p->nanosleep_tmr.it_type = NANOSLEEP;
+	p->nanosleep_tmr.it_pq = 0;
 	if (copy_mm(clone_flags, p))
 		goto bad_fork_cleanup_sighand;
 	if (copy_namespace(clone_flags, p))
kernel/id2ptr.c +225 -0

225 lines not on this page · raw message

kernel/posix-timers.c +1200 -0

1,200 lines not on this page · raw message

kernel/sched.c +1 -0
diff -X dontdiff -urN linux-2.5.51.orig/kernel/sched.c linux-2.5.51.timers/kernel/sched.c
@@ -2255,6 +2255,7 @@
 	wake_up_process(current);
 
 	init_timers();
+	init_posix_timers();
 
 	/*
 	 * The boot idle thread does lazy MMU switching as well:
kernel/signal.c +34 -11

76 lines not on this page · raw message

kernel/sysctl.c +3 -0
diff -X dontdiff -urN linux-2.5.51.orig/kernel/sysctl.c linux-2.5.51.timers/kernel/sysctl.c
@@ -118,6 +118,7 @@
 static ctl_table debug_table[];
 static ctl_table dev_table[];
 extern ctl_table random_table[];
+extern ctl_table posix_timers_table[];
 
 /* /proc declarations: */
 
@@ -157,6 +158,7 @@
 	{0}
 };
 
+
 static ctl_table kern_table[] = {
 	{KERN_OSTYPE, "ostype", system_utsname.sysname, 64,
 	 0444, NULL, &proc_doutsstring, &sysctl_string},
@@ -259,6 +261,7 @@
 #endif
 	{KERN_PIDMAX, "pid_max", &pid_max, sizeof (int),
 	 0600, NULL, &proc_dointvec},
+	{KERN_POSIX_TIMERS, "posix-timers", NULL, 0, 0555, posix_timers_table},
 	{0}
 };
 
kernel/timer.c +39 -14

148 lines not on this page · raw message