From: Roland McGrath POSIX requires that setitimer, getitimer, and alarm work on a per-process basis. Currently, Linux implements these for individual threads. This patch fixes these semantics for the ITIMER_REAL timer (which generates SIGALRM), making it shared by all threads in a process (thread group). Signed-off-by: Roland McGrath Signed-off-by: Andrew Morton --- 25-akpm/fs/proc/array.c | 4 - 25-akpm/include/linux/init_task.h | 3 25-akpm/include/linux/sched.h | 6 + 25-akpm/kernel/exit.c | 5 - 25-akpm/kernel/fork.c | 9 +- 25-akpm/kernel/itimer.c | 115 +++++++++++++++++++++++--------------- 6 files changed, 87 insertions(+), 55 deletions(-) diff -puN fs/proc/array.c~make-itimer_real-per-process fs/proc/array.c --- 25/fs/proc/array.c~make-itimer_real-per-process 2005-01-26 18:24:07.889198512 -0800 +++ 25-akpm/fs/proc/array.c 2005-01-26 18:24:07.900196840 -0800 @@ -317,6 +317,7 @@ static int do_task_stat(struct task_stru unsigned long min_flt = 0, maj_flt = 0; cputime_t cutime, cstime, utime, stime; unsigned long rsslim = 0; + unsigned long it_real_value = 0; struct task_struct *t; char tcomm[sizeof(task->comm)]; @@ -372,6 +373,7 @@ static int do_task_stat(struct task_stru utime = cputime_add(utime, task->signal->utime); stime = cputime_add(stime, task->signal->stime); } + it_real_value = task->signal->it_real_value; } ppid = pid_alive(task) ? task->group_leader->real_parent->tgid : 0; read_unlock(&tasklist_lock); @@ -420,7 +422,7 @@ static int do_task_stat(struct task_stru priority, nice, num_threads, - jiffies_to_clock_t(task->it_real_value), + jiffies_to_clock_t(it_real_value), start_time, vsize, mm ? mm->rss : 0, /* you might want to shift this left 3 */ diff -puN include/linux/init_task.h~make-itimer_real-per-process include/linux/init_task.h --- 25/include/linux/init_task.h~make-itimer_real-per-process 2005-01-26 18:24:07.891198208 -0800 +++ 25-akpm/include/linux/init_task.h 2005-01-26 18:24:07.901196688 -0800 @@ -90,9 +90,6 @@ extern struct group_info init_groups; .children = LIST_HEAD_INIT(tsk.children), \ .sibling = LIST_HEAD_INIT(tsk.sibling), \ .group_leader = &tsk, \ - .real_timer = { \ - .function = it_real_fn \ - }, \ .group_info = &init_groups, \ .cap_effective = CAP_INIT_EFF_SET, \ .cap_inheritable = CAP_INIT_INH_SET, \ diff -puN include/linux/sched.h~make-itimer_real-per-process include/linux/sched.h --- 25/include/linux/sched.h~make-itimer_real-per-process 2005-01-26 18:24:07.892198056 -0800 +++ 25-akpm/include/linux/sched.h 2005-01-26 18:24:07.902196536 -0800 @@ -300,6 +300,10 @@ struct signal_struct { /* POSIX.1b Interval Timers */ struct list_head posix_timers; + /* ITIMER_REAL timer for the process */ + struct timer_list real_timer; + unsigned long it_real_value, it_real_incr; + /* job control IDs */ pid_t pgrp; pid_t tty_old_pgrp; @@ -606,10 +610,8 @@ struct task_struct { int __user *clear_child_tid; /* CLONE_CHILD_CLEARTID */ unsigned long rt_priority; - unsigned long it_real_value, it_real_incr; cputime_t it_virt_value, it_virt_incr; cputime_t it_prof_value, it_prof_incr; - struct timer_list real_timer; cputime_t utime, stime; unsigned long nvcsw, nivcsw; /* context switch counts */ struct timespec start_time; diff -puN kernel/exit.c~make-itimer_real-per-process kernel/exit.c --- 25/kernel/exit.c~make-itimer_real-per-process 2005-01-26 18:24:07.894197752 -0800 +++ 25-akpm/kernel/exit.c 2005-01-26 18:24:07.903196384 -0800 @@ -795,7 +795,6 @@ fastcall NORET_TYPE void do_exit(long co } tsk->flags |= PF_EXITING; - del_timer_sync(&tsk->real_timer); /* * Make sure we don't try to process any timer firings @@ -813,8 +812,10 @@ fastcall NORET_TYPE void do_exit(long co acct_update_integrals(); update_mem_hiwater(); group_dead = atomic_dec_and_test(&tsk->signal->live); - if (group_dead) + if (group_dead) { + del_timer_sync(&tsk->signal->real_timer); acct_process(code); + } exit_mm(tsk); exit_sem(tsk); diff -puN kernel/fork.c~make-itimer_real-per-process kernel/fork.c --- 25/kernel/fork.c~make-itimer_real-per-process 2005-01-26 18:24:07.896197448 -0800 +++ 25-akpm/kernel/fork.c 2005-01-26 18:24:07.905196080 -0800 @@ -740,6 +740,11 @@ static inline int copy_signal(unsigned l init_sigpending(&sig->shared_pending); INIT_LIST_HEAD(&sig->posix_timers); + sig->it_real_value = sig->it_real_incr = 0; + sig->real_timer.function = it_real_fn; + sig->real_timer.data = (unsigned long) tsk; + init_timer(&sig->real_timer); + sig->tty = current->signal->tty; sig->pgrp = process_group(current); sig->session = current->signal->session; @@ -870,14 +875,10 @@ static task_t *copy_process(unsigned lon clear_tsk_thread_flag(p, TIF_SIGPENDING); init_sigpending(&p->pending); - p->it_real_value = 0; - p->it_real_incr = 0; p->it_virt_value = cputime_zero; p->it_virt_incr = cputime_zero; p->it_prof_value = cputime_zero; p->it_prof_incr = cputime_zero; - init_timer(&p->real_timer); - p->real_timer.data = (unsigned long) p; p->utime = cputime_zero; p->stime = cputime_zero; diff -puN kernel/itimer.c~make-itimer_real-per-process kernel/itimer.c --- 25/kernel/itimer.c~make-itimer_real-per-process 2005-01-26 18:24:07.897197296 -0800 +++ 25-akpm/kernel/itimer.c 2005-01-26 18:24:07.906195928 -0800 @@ -14,25 +14,31 @@ #include +static unsigned long it_real_value(struct signal_struct *sig) +{ + unsigned long val = 0; + if (timer_pending(&sig->real_timer)) { + val = sig->real_timer.expires - jiffies; + + /* look out for negative/zero itimer.. */ + if ((long) val <= 0) + val = 1; + } + return val; +} + int do_getitimer(int which, struct itimerval *value) { - register unsigned long val; + unsigned long interval, val; switch (which) { case ITIMER_REAL: - val = 0; - /* - * FIXME! This needs to be atomic, in case the kernel timer happens! - */ - if (timer_pending(¤t->real_timer)) { - val = current->real_timer.expires - jiffies; - - /* look out for negative/zero itimer.. */ - if ((long) val <= 0) - val = 1; - } + spin_lock_irq(¤t->sighand->siglock); + interval = current->signal->it_real_incr; + val = it_real_value(current->signal); + spin_unlock_irq(¤t->sighand->siglock); jiffies_to_timeval(val, &value->it_value); - jiffies_to_timeval(current->it_real_incr, &value->it_interval); + jiffies_to_timeval(interval, &value->it_interval); break; case ITIMER_VIRTUAL: cputime_to_timeval(current->it_virt_value, &value->it_value); @@ -48,7 +54,6 @@ int do_getitimer(int which, struct itime return 0; } -/* SMP: Only we modify our itimer values. */ asmlinkage long sys_getitimer(int which, struct itimerval __user *value) { int error = -EFAULT; @@ -63,60 +68,87 @@ asmlinkage long sys_getitimer(int which, return error; } +/* + * Called with P->sighand->siglock held and P->signal->real_timer inactive. + * If interval is nonzero, arm the timer for interval ticks from now. + */ +static inline void it_real_arm(struct task_struct *p, unsigned long interval) +{ + p->signal->it_real_value = interval; /* XXX unnecessary field?? */ + if (interval == 0) + return; + if (interval > (unsigned long) LONG_MAX) + interval = LONG_MAX; + p->signal->real_timer.expires = jiffies + interval; + add_timer(&p->signal->real_timer); +} + void it_real_fn(unsigned long __data) { struct task_struct * p = (struct task_struct *) __data; - unsigned long interval; send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p); - interval = p->it_real_incr; - if (interval) { - if (interval > (unsigned long) LONG_MAX) - interval = LONG_MAX; - p->real_timer.expires = jiffies + interval; - add_timer(&p->real_timer); - } + + /* + * Now restart the timer if necessary. We don't need any locking + * here because do_setitimer makes sure we have finished running + * before it touches anything. + */ + it_real_arm(p, p->signal->it_real_incr); } int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue) { - unsigned long expire; + struct task_struct *tsk = current; + unsigned long val, interval; cputime_t cputime; - int k; - if (ovalue && (k = do_getitimer(which, ovalue)) < 0) - return k; switch (which) { case ITIMER_REAL: - del_timer_sync(¤t->real_timer); - expire = timeval_to_jiffies(&value->it_value); - current->it_real_value = expire; - current->it_real_incr = + spin_lock_irq(&tsk->sighand->siglock); + interval = tsk->signal->it_real_incr; + val = it_real_value(tsk->signal); + if (val) + del_timer_sync(&tsk->signal->real_timer); + tsk->signal->it_real_incr = timeval_to_jiffies(&value->it_interval); - if (!expire) - break; - if (expire > (unsigned long) LONG_MAX) - expire = LONG_MAX; - current->real_timer.expires = jiffies + expire; - add_timer(¤t->real_timer); + it_real_arm(tsk, timeval_to_jiffies(&value->it_value)); + spin_unlock_irq(&tsk->sighand->siglock); + if (ovalue) { + jiffies_to_timeval(val, &ovalue->it_value); + jiffies_to_timeval(interval, + &ovalue->it_interval); + } break; case ITIMER_VIRTUAL: + if (ovalue) { + cputime_to_timeval(tsk->it_virt_value, + &ovalue->it_value); + cputime_to_timeval(tsk->it_virt_incr, + &ovalue->it_interval); + } cputime = timeval_to_cputime(&value->it_value); if (cputime_gt(cputime, cputime_zero)) cputime = cputime_add(cputime, jiffies_to_cputime(1)); - current->it_virt_value = cputime; + tsk->it_virt_value = cputime; cputime = timeval_to_cputime(&value->it_interval); - current->it_virt_incr = cputime; + tsk->it_virt_incr = cputime; break; case ITIMER_PROF: + if (ovalue) { + cputime_to_timeval(tsk->it_prof_value, + &ovalue->it_value); + cputime_to_timeval(tsk->it_prof_incr, + &ovalue->it_interval); + } cputime = timeval_to_cputime(&value->it_value); if (cputime_gt(cputime, cputime_zero)) cputime = cputime_add(cputime, jiffies_to_cputime(1)); - current->it_prof_value = cputime; + tsk->it_prof_value = cputime; cputime = timeval_to_cputime(&value->it_interval); - current->it_prof_incr = cputime; + tsk->it_prof_incr = cputime; break; default: return -EINVAL; @@ -124,9 +156,6 @@ int do_setitimer(int which, struct itime return 0; } -/* SMP: Again, only we play with our itimers, and signals are SMP safe - * now so that is not an issue at all anymore. - */ asmlinkage long sys_setitimer(int which, struct itimerval __user *value, struct itimerval __user *ovalue) _