The alternate Posix timers patch6
Subsystems (7)
PATCH 1/1 The alternate Posix timers patch6
Hi Linus, Everyone,
Linus, I hope you are still planning to merge posix timers. I would be happy to see you merge George's patch. There were a few odds and ends I thought I could do better so I have been working on a patch of my own. It's at the useable stage:-)
This is the 6th version of my spin on the Posix timers. This patch works with linux-2.5.50. I started with George Anzinger's patch, but I have made many changes.
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.
- 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.
- 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.
It now passes all of the tests that are included in George's timers support package. I have been doing over-night runs with looping these tests, and it seems to be stable.
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
@@ -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/signal.c +11 -0
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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 +65 -0
65 lines not on this page · raw message
include/linux/sched.h +6 -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 ;
@@ -340,6 +341,10 @@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 */ +int nanosleep_restart;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 */
@@ -580,6 +585,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 +39 -1
@@ -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
@@ -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
@@ -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
@@ -812,6 +812,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 +1186 -0
1,186 lines not on this page · raw message
kernel/sched.c +1 -0
@@ -2240,6 +2240,7 @@wake_up_process (current);init_timers (); +init_posix_timers ();/* * The boot idle thread does lazy MMU switching as well:
kernel/signal.c +35 -12
77 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 +38 -14
147 lines not on this page · raw message