The alternate Posix timers patch8
Subsystems (7)
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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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