summaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>2005-03-07 18:17:13 -0800
committerLinus Torvalds <torvalds@ppc970.osdl.org>2005-03-07 18:17:13 -0800
commitc1dcd6c2d9b7478baf876725bd356f1b19eeaa65 (patch)
treea5b213b90ca550635f96303c75826d0e8e086a86 /kernel
parent857b64a59ebac8c9e1d951039cc9fb6a078f8274 (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.c5
-rw-r--r--kernel/fork.c9
-rw-r--r--kernel/itimer.c115
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(&current->real_timer)) {
- val = current->real_timer.expires - jiffies;
-
- /* look out for negative/zero itimer.. */
- if ((long) val <= 0)
- val = 1;
- }
+ spin_lock_irq(&current->sighand->siglock);
+ interval = current->signal->it_real_incr;
+ val = it_real_value(current->signal);
+ spin_unlock_irq(&current->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(&current->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(&current->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)