Skip to contentSkip to conversation

The alternate Posix timers patch5

PATCH New Jim Houston 1 participant 1 message Nov 18, 2002

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

PATCH 1/1 The alternate Posix timers patch5

Hi Everyone,

This is the fifth version of my spin on the Posix timers. This patch works with linux-2.5.48. I started with George Anzinger's patch, but I have made major changes.

This version fixes a bunch of my bugs. It supports high resolution by sharing the local APIC timer as the timing source. If you don't have this timer, this patch is not for you.

Here is a summary of my changes:

- I keep the timers in seconds and nano-seconds.

- 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().

- A new queue just for Posix timers and code to handle expiring
timers. This supports high resolution without having to change the existing jiffie based timers. It also works fine with tick based time measurement.

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.

- Nanosleep shouldn't complete early. It is not allowed to
return early if the process is hit with a signal that is not delivered. The existing nanosleep does return early.

George has fixed this in his patch by calling do_signal() from inside nanosleep (actually clock_nanosleep). This is ugly because it requires a pointer to the registers which makes it architecture specific code.

I take a different approach. I let nanosleep return to do the do_signal() from entry.S, but I arrange to restart the nanosleep if the signal is not delivered. The logic is similar to the existing ERESTARTNOHAND mechanism. This interface is close to what I want, but the system call doesn't have a clue that it's being restarted. I ended up making a small change to do_signal which should not be too painful to add to the other architectures.

It now passes all of the tests that are included in George's timers support package. Actually I had to make a small change to the clock_nanosleeptest to get around a synchronization problem. I also have a minor problem with the ltp nanosleep02 test. It doesn't expect the time to be rounded up to the clock resolution.

I have been using George's version of the patch and would be glad to see it included into the 2.5 tree. On the other hand, since we don't know what might appeal to Linus, it makes sense to give him a choice.

Jim Houston - Concurrent Computer Corp.

arch/i386/kernel/apic.c +35 -1
diff -X dontdiff -urN linux-2.5.48.orig/arch/i386/kernel/apic.c linux-2.5.48/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.48.orig/arch/i386/kernel/entry.S linux-2.5.48/arch/i386/kernel/entry.S
@@ -768,6 +768,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/signal.c +11 -0
diff -X dontdiff -urN linux-2.5.48.orig/arch/i386/kernel/signal.c linux-2.5.48/arch/i386/kernel/signal.c
@@ -507,6 +507,7 @@
 		/* If so, check system call restarting.. */
 		switch (regs->eax) {
 			case -ERESTARTNOHAND:
+			case -ERESTARTNANOSLP:
 				regs->eax = -EINTR;
 				break;
 
@@ -588,6 +589,16 @@
 		if (regs->eax == -ERESTARTNOHAND ||
 		    regs->eax == -ERESTARTSYS ||
 		    regs->eax == -ERESTARTNOINTR) {
+			regs->eax = regs->orig_eax;
+			regs->eip -= 2;
+		}
+		/*
+		 * If a nanosleep or clock_nanosleep is interrupted
+		 * by a non delivered signal we want to complete 
+		 * the requested delay.
+		 */
+		if (regs->eax == -ERESTARTNANOSLP) {
+			current->nanosleep_restart = RESTART_ACK;
 			regs->eax = regs->orig_eax;
 			regs->eip -= 2;
 		}
arch/i386/kernel/smpboot.c +1 -3
diff -X dontdiff -urN linux-2.5.48.orig/arch/i386/kernel/smpboot.c linux-2.5.48/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.48.orig/arch/i386/kernel/timers/timer_cyclone.c linux-2.5.48/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.48.orig/arch/i386/kernel/timers/timer_pit.c linux-2.5.48/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.48.orig/fs/exec.c linux-2.5.48/fs/exec.c
@@ -778,6 +778,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.48.orig/include/asm-generic/siginfo.h linux-2.5.48/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.48.orig/include/asm-i386/posix_types.h linux-2.5.48/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.48.orig/include/asm-i386/unistd.h linux-2.5.48/include/asm-i386/unistd.h
@@ -263,6 +263,15 @@
 #define __NR_sys_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/errno.h +1 -0
diff -X dontdiff -urN linux-2.5.48.orig/include/linux/errno.h linux-2.5.48/include/linux/errno.h
@@ -10,6 +10,7 @@
 #define ERESTARTNOINTR	513
 #define ERESTARTNOHAND	514	/* restart if no handler.. */
 #define ENOIOCTLCMD	515	/* No ioctl command */
+#define ERESTARTNANOSLP	516
 
 /* Defined for the NFSv3 protocol */
 #define EBADHANDLE	521	/* Illegal NFS file handle */
include/linux/id2ptr.h +47 -0

47 lines not on this page · raw message

include/linux/init_task.h +6 -0
diff -X dontdiff -urN linux-2.5.48.orig/include/linux/init_task.h linux-2.5.48/include/linux/init_task.h
@@ -93,6 +93,12 @@
 	.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,				\
+	.nanosleep_tmr.it_type = NANOSLEEP,				\
+	.nanosleep_restart = RESTART_NONE,				\
 	.alloc_lock	= SPIN_LOCK_UNLOCKED,				\
 	.switch_lock	= SPIN_LOCK_UNLOCKED,				\
 	.journal_info	= NULL,						\
include/linux/posix-timers.h +83 -0

83 lines not on this page · raw message

include/linux/sched.h +5 -0
diff -X dontdiff -urN linux-2.5.48.orig/include/linux/sched.h linux-2.5.48/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;
 
@@ -338,6 +339,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;
+	int	nanosleep_restart;
 	unsigned long utime, stime, cutime, cstime;
 	unsigned long start_time;
 	long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];
@@ -607,6 +611,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.48.orig/include/linux/sys.h linux-2.5.48/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.48.orig/include/linux/sysctl.h linux-2.5.48/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 +39 -1
diff -X dontdiff -urN linux-2.5.48.orig/include/linux/time.h linux-2.5.48/include/linux/time.h
@@ -40,6 +40,19 @@
  */
 #define MAX_JIFFY_OFFSET ((~0UL >> 1)-1)
 
+/* Parameters used to convert the timespec values */
+#ifndef USEC_PER_SEC
+#define USEC_PER_SEC (1000000L)
+#endif
+
+#ifndef NSEC_PER_SEC
+#define NSEC_PER_SEC (1000000000L)
+#endif
+
+#ifndef NSEC_PER_USEC
+#define NSEC_PER_USEC (1000L)
+#endif
+
 static __inline__ unsigned long
 timespec_to_jiffies(struct timespec *value)
 {
@@ -119,7 +132,8 @@
 	)*60 + sec; /* finally seconds */
 }
 
-extern struct timespec xtime;
+extern struct timespec xtime;	/* time of day */
+extern struct timespec ytime;	/* time since boot */
 extern rwlock_t xtime_lock;
 
 static inline unsigned long get_seconds(void)
@@ -137,7 +151,11 @@
 
 #ifdef __KERNEL__
 extern void do_gettimeofday(struct timeval *tv);
+extern void do_gettimeofday_ns(struct timespec *tv);
 extern void do_settimeofday(struct timeval *tv);
+extern void do_settimeofday_ns(struct timespec *tv);
+extern void do_gettime_sinceboot_ns(struct timespec *tv);
+extern int do_sys_settimeofday(struct timeval *tv, struct timezone *tz);
 #endif
 
 #define FD_SETSIZE		__FD_SETSIZE
@@ -163,5 +181,25 @@
 	struct	timeval it_interval;	/* timer interval */
 	struct	timeval it_value;	/* current value */
 };
+
+
+/*
+ * The IDs of the various system clocks (for POSIX.1b interval timers).
+ */
+#define CLOCK_REALTIME		  0
+#define CLOCK_MONOTONIC	  1
+#define CLOCK_PROCESS_CPUTIME_ID 2
+#define CLOCK_THREAD_CPUTIME_ID	 3
+#define CLOCK_REALTIME_HR	 4
+#define CLOCK_MONOTONIC_HR	  5
+
+#define MAX_CLOCKS 6
+
+/*
+ * The various flags for setting POSIX.1b interval timers.
+ */
+
+#define TIMER_ABSTIME 0x01
+
 
 #endif
include/linux/types.h +2 -0
diff -X dontdiff -urN linux-2.5.48.orig/include/linux/types.h linux-2.5.48/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.48.orig/kernel/Makefile linux-2.5.48/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
+	    rcupdate.o intermodule.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.48.orig/kernel/exit.c linux-2.5.48/kernel/exit.c
@@ -660,6 +660,7 @@
 	__exit_files(tsk);
 	__exit_fs(tsk);
 	exit_namespace(tsk);
+	exit_itimers(tsk, 1);
 	exit_thread();
 
 	if (current->leader)
kernel/fork.c +7 -0
diff -X dontdiff -urN linux-2.5.48.orig/kernel/fork.c linux-2.5.48/kernel/fork.c
@@ -815,6 +815,13 @@
 		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;
+	p->nanosleep_restart = RESTART_NONE;
 	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 +1230 -0

1,230 lines not on this page · raw message

kernel/signal.c +35 -12

77 lines not on this page · raw message

kernel/sysctl.c +3 -0
diff -X dontdiff -urN linux-2.5.48.orig/kernel/sysctl.c linux-2.5.48/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 +38 -14

147 lines not on this page · raw message