diff options
| author | Roland McGrath <roland@redhat.com> | 2005-03-07 18:17:13 -0800 |
|---|---|---|
| committer | Linus Torvalds <torvalds@ppc970.osdl.org> | 2005-03-07 18:17:13 -0800 |
| commit | c1dcd6c2d9b7478baf876725bd356f1b19eeaa65 (patch) | |
| tree | a5b213b90ca550635f96303c75826d0e8e086a86 /kernel | |
| parent | 857b64a59ebac8c9e1d951039cc9fb6a078f8274 (diff) | |
[PATCH] make ITIMER_REAL per-process
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 <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/exit.c | 5 | ||||
| -rw-r--r-- | kernel/fork.c | 9 | ||||
| -rw-r--r-- | kernel/itimer.c | 115 |
3 files changed, 80 insertions, 49 deletions
diff --git a/kernel/exit.c b/kernel/exit.c index fbe293b10a47..db204cd02d8b 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -795,7 +795,6 @@ fastcall NORET_TYPE void do_exit(long code) } 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 code) acct_update_integrals(tsk); update_mem_hiwater(tsk); 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 --git a/kernel/fork.c b/kernel/fork.c index 718eaf0bb1cd..a1d1939f596c 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -740,6 +740,11 @@ static inline int copy_signal(unsigned long clone_flags, struct task_struct * ts 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 long clone_flags, 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 --git a/kernel/itimer.c b/kernel/itimer.c index e1743c563206..c9cc2a4cb40a 100644 --- a/kernel/itimer.c +++ b/kernel/itimer.c @@ -14,25 +14,31 @@ #include <asm/uaccess.h> +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 itimerval *value) 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, struct itimerval __user *value) 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 itimerval *value, struct itimerval *ovalue) 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) |
