
From: Roland McGrath <roland@redhat.com>

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_PROF timer (which generates SIGPROF) and
the ITIMER_VIRTUAL timer (which generates SIGVTALRM), making them shared by
all threads in a process (thread group).  This patch should be applied after
the one that fixes ITIMER_REAL.

The essential machinery for these timers is tied into the new posix-timers
code for process CPU clocks and timers.  This patch requires the cputimers
patch and its dependencies.

Signed-off-by: Roland McGrath <roland@redhat.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
---

 25-akpm/include/linux/posix-timers.h |    2 
 25-akpm/include/linux/sched.h        |    6 -
 25-akpm/include/linux/signal.h       |    1 
 25-akpm/kernel/exit.c                |    3 
 25-akpm/kernel/fork.c                |   16 +--
 25-akpm/kernel/itimer.c              |  176 +++++++++++++++++++++++------------
 25-akpm/kernel/posix-cpu-timers.c    |  157 +++++++++++++++++++++++++++----
 25-akpm/kernel/sched.c               |   47 ---------
 25-akpm/kernel/signal.c              |    2 
 9 files changed, 277 insertions(+), 133 deletions(-)

diff -puN include/linux/posix-timers.h~make-itimer_prof-itimer_virtual-per-process include/linux/posix-timers.h
--- 25/include/linux/posix-timers.h~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/include/linux/posix-timers.h	2005-03-07 20:41:53.000000000 -0800
@@ -133,5 +133,7 @@ void run_posix_cpu_timers(struct task_st
 void posix_cpu_timers_exit(struct task_struct *);
 void posix_cpu_timers_exit_group(struct task_struct *);
 
+void set_process_cpu_timer(struct task_struct *, unsigned int,
+			   cputime_t *, cputime_t *);
 
 #endif
diff -puN include/linux/sched.h~make-itimer_prof-itimer_virtual-per-process include/linux/sched.h
--- 25/include/linux/sched.h~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/include/linux/sched.h	2005-03-07 20:41:53.000000000 -0800
@@ -305,6 +305,10 @@ struct signal_struct {
 	struct timer_list real_timer;
 	unsigned long it_real_value, it_real_incr;
 
+	/* ITIMER_PROF and ITIMER_VIRTUAL timers for the process */
+	cputime_t it_prof_expires, it_virt_expires;
+	cputime_t it_prof_incr, it_virt_incr;
+
 	/* job control IDs */
 	pid_t pgrp;
 	pid_t tty_old_pgrp;
@@ -609,8 +613,6 @@ struct task_struct {
 	int __user *clear_child_tid;		/* CLONE_CHILD_CLEARTID */
 
 	unsigned long rt_priority;
-	cputime_t it_virt_value, it_virt_incr;
-	cputime_t it_prof_value, it_prof_incr;
 	cputime_t utime, stime;
 	unsigned long nvcsw, nivcsw; /* context switch counts */
 	struct timespec start_time;
diff -puN include/linux/signal.h~make-itimer_prof-itimer_virtual-per-process include/linux/signal.h
--- 25/include/linux/signal.h~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/include/linux/signal.h	2005-03-07 21:14:14.000000000 -0800
@@ -212,6 +212,7 @@ static inline void init_sigpending(struc
 }
 
 extern int group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p);
+extern int __group_send_sig_info(int, struct siginfo *, struct task_struct *);
 extern long do_sigpending(void __user *, unsigned long);
 extern int sigprocmask(int, sigset_t *, sigset_t *);
 
diff -puN kernel/exit.c~make-itimer_prof-itimer_virtual-per-process kernel/exit.c
--- 25/kernel/exit.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/exit.c	2005-03-07 20:41:53.000000000 -0800
@@ -753,9 +753,6 @@ static void exit_notify(struct task_stru
 		state = EXIT_DEAD;
 	tsk->exit_state = state;
 
-	tsk->it_virt_value = cputime_zero;
-	tsk->it_prof_value = cputime_zero;
-
 	write_unlock_irq(&tasklist_lock);
 
 	list_for_each_safe(_p, _n, &ptrace_dead) {
diff -puN kernel/fork.c~make-itimer_prof-itimer_virtual-per-process kernel/fork.c
--- 25/kernel/fork.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/fork.c	2005-03-07 21:14:14.000000000 -0800
@@ -745,6 +745,11 @@ static inline int copy_signal(unsigned l
 	sig->real_timer.data = (unsigned long) tsk;
 	init_timer(&sig->real_timer);
 
+	sig->it_virt_expires = cputime_zero;
+	sig->it_virt_incr = cputime_zero;
+	sig->it_prof_expires = cputime_zero;
+	sig->it_prof_incr = cputime_zero;
+
 	sig->tty = current->signal->tty;
 	sig->pgrp = process_group(current);
 	sig->session = current->signal->session;
@@ -875,11 +880,6 @@ static task_t *copy_process(unsigned lon
 	clear_tsk_thread_flag(p, TIF_SIGPENDING);
 	init_sigpending(&p->pending);
 
-	p->it_virt_value = cputime_zero;
-	p->it_virt_incr = cputime_zero;
-	p->it_prof_value = cputime_zero;
-	p->it_prof_incr = cputime_zero;
-
 	p->utime = cputime_zero;
 	p->stime = cputime_zero;
  	p->sched_time = 0;
@@ -1028,7 +1028,11 @@ static task_t *copy_process(unsigned lon
 			set_tsk_thread_flag(p, TIF_SIGPENDING);
 		}
 
-		if (!list_empty(&current->signal->cpu_timers[0]) ||
+		if (!cputime_eq(current->signal->it_virt_expires,
+				cputime_zero) ||
+		    !cputime_eq(current->signal->it_prof_expires,
+				cputime_zero) ||
+		    !list_empty(&current->signal->cpu_timers[0]) ||
 		    !list_empty(&current->signal->cpu_timers[1]) ||
 		    !list_empty(&current->signal->cpu_timers[2])) {
 			/*
diff -puN kernel/itimer.c~make-itimer_prof-itimer_virtual-per-process kernel/itimer.c
--- 25/kernel/itimer.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/itimer.c	2005-03-07 20:41:53.000000000 -0800
@@ -11,6 +11,7 @@
 #include <linux/interrupt.h>
 #include <linux/syscalls.h>
 #include <linux/time.h>
+#include <linux/posix-timers.h>
 
 #include <asm/uaccess.h>
 
@@ -29,24 +30,67 @@ static unsigned long it_real_value(struc
 
 int do_getitimer(int which, struct itimerval *value)
 {
+	struct task_struct *tsk = current;
 	unsigned long interval, val;
+	cputime_t cinterval, cval;
 
 	switch (which) {
 	case ITIMER_REAL:
-		spin_lock_irq(&current->sighand->siglock);
-		interval = current->signal->it_real_incr;
-		val = it_real_value(current->signal);
-		spin_unlock_irq(&current->sighand->siglock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		interval = tsk->signal->it_real_incr;
+		val = it_real_value(tsk->signal);
+		spin_unlock_irq(&tsk->sighand->siglock);
 		jiffies_to_timeval(val, &value->it_value);
 		jiffies_to_timeval(interval, &value->it_interval);
 		break;
 	case ITIMER_VIRTUAL:
-		cputime_to_timeval(current->it_virt_value, &value->it_value);
-		cputime_to_timeval(current->it_virt_incr, &value->it_interval);
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		cval = tsk->signal->it_virt_expires;
+		cinterval = tsk->signal->it_virt_incr;
+		if (!cputime_eq(cval, cputime_zero)) {
+			struct task_struct *t = tsk;
+			cputime_t utime = tsk->signal->utime;
+			do {
+				utime = cputime_add(utime, t->utime);
+				t = next_thread(t);
+			} while (t != tsk);
+			if (cputime_le(cval, utime)) { /* about to fire */
+				cval = jiffies_to_cputime(1);
+			} else {
+				cval = cputime_sub(cval, utime);
+			}
+		}
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+		cputime_to_timeval(cval, &value->it_value);
+		cputime_to_timeval(cinterval, &value->it_interval);
 		break;
 	case ITIMER_PROF:
-		cputime_to_timeval(current->it_prof_value, &value->it_value);
-		cputime_to_timeval(current->it_prof_incr, &value->it_interval);
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		cval = tsk->signal->it_prof_expires;
+		cinterval = tsk->signal->it_prof_incr;
+		if (!cputime_eq(cval, cputime_zero)) {
+			struct task_struct *t = tsk;
+			cputime_t ptime = cputime_add(tsk->signal->utime,
+						      tsk->signal->stime);
+			do {
+				ptime = cputime_add(ptime,
+						    cputime_add(t->utime,
+								t->stime));
+				t = next_thread(t);
+			} while (t != tsk);
+			if (cputime_le(cval, ptime)) { /* about to fire */
+				cval = jiffies_to_cputime(1);
+			} else {
+				cval = cputime_sub(cval, ptime);
+			}
+		}
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+		cputime_to_timeval(cval, &value->it_value);
+		cputime_to_timeval(cinterval, &value->it_interval);
 		break;
 	default:
 		return(-EINVAL);
@@ -101,57 +145,75 @@ int do_setitimer(int which, struct itime
 {
 	struct task_struct *tsk = current;
  	unsigned long val, interval;
-	cputime_t cputime;
+	cputime_t cval, cinterval, nval, ninterval;
 
 	switch (which) {
-		case ITIMER_REAL:
- 			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);
- 			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));
-			tsk->it_virt_value = cputime;
-			cputime = timeval_to_cputime(&value->it_interval);
-			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));
-			tsk->it_prof_value = cputime;
-			cputime = timeval_to_cputime(&value->it_interval);
-			tsk->it_prof_incr = cputime;
-			break;
-		default:
-			return -EINVAL;
+	case ITIMER_REAL:
+		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);
+		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:
+		nval = timeval_to_cputime(&value->it_value);
+		ninterval = timeval_to_cputime(&value->it_interval);
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		cval = tsk->signal->it_virt_expires;
+		cinterval = tsk->signal->it_virt_incr;
+		if (!cputime_eq(cval, cputime_zero) ||
+		    !cputime_eq(nval, cputime_zero)) {
+			if (cputime_gt(nval, cputime_zero))
+				nval = cputime_add(nval,
+						   jiffies_to_cputime(1));
+			set_process_cpu_timer(tsk, CPUCLOCK_VIRT,
+					      &nval, &cval);
+		}
+		tsk->signal->it_virt_expires = nval;
+		tsk->signal->it_virt_incr = ninterval;
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+		if (ovalue) {
+			cputime_to_timeval(cval, &ovalue->it_value);
+			cputime_to_timeval(cinterval, &ovalue->it_interval);
+		}
+		break;
+	case ITIMER_PROF:
+		nval = timeval_to_cputime(&value->it_value);
+		ninterval = timeval_to_cputime(&value->it_interval);
+		read_lock(&tasklist_lock);
+		spin_lock_irq(&tsk->sighand->siglock);
+		cval = tsk->signal->it_prof_expires;
+		cinterval = tsk->signal->it_prof_incr;
+		if (!cputime_eq(cval, cputime_zero) ||
+		    !cputime_eq(nval, cputime_zero)) {
+			if (cputime_gt(nval, cputime_zero))
+				nval = cputime_add(nval,
+						   jiffies_to_cputime(1));
+			set_process_cpu_timer(tsk, CPUCLOCK_PROF,
+					      &nval, &cval);
+		}
+		tsk->signal->it_prof_expires = nval;
+		tsk->signal->it_prof_incr = ninterval;
+		spin_unlock_irq(&tsk->sighand->siglock);
+		read_unlock(&tasklist_lock);
+		if (ovalue) {
+			cputime_to_timeval(cval, &ovalue->it_value);
+			cputime_to_timeval(cinterval, &ovalue->it_interval);
+		}
+		break;
+	default:
+		return -EINVAL;
 	}
 	return 0;
 }
diff -puN kernel/posix-cpu-timers.c~make-itimer_prof-itimer_virtual-per-process kernel/posix-cpu-timers.c
--- 25/kernel/posix-cpu-timers.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/posix-cpu-timers.c	2005-03-07 21:14:14.000000000 -0800
@@ -190,36 +190,31 @@ static int cpu_clock_sample(clockid_t wh
 /*
  * Sample a process (thread group) clock for the given group_leader task.
  * Must be called with tasklist_lock held for reading.
+ * Must be called with tasklist_lock held for reading, and p->sighand->siglock.
  */
-static int cpu_clock_sample_group(clockid_t which_clock,
-				  struct task_struct *p,
-				  union cpu_time_count *cpu)
+static int cpu_clock_sample_group_locked(unsigned int clock_idx,
+					 struct task_struct *p,
+					 union cpu_time_count *cpu)
 {
 	struct task_struct *t = p;
-	unsigned long flags;
-	switch (CPUCLOCK_WHICH(which_clock)) {
+ 	switch (clock_idx) {
 	default:
 		return -EINVAL;
 	case CPUCLOCK_PROF:
-		spin_lock_irqsave(&p->sighand->siglock, flags);
 		cpu->cpu = cputime_add(p->signal->utime, p->signal->stime);
 		do {
 			cpu->cpu = cputime_add(cpu->cpu, prof_ticks(t));
 			t = next_thread(t);
 		} while (t != p);
-		spin_unlock_irqrestore(&p->sighand->siglock, flags);
 		break;
 	case CPUCLOCK_VIRT:
-		spin_lock_irqsave(&p->sighand->siglock, flags);
 		cpu->cpu = p->signal->utime;
 		do {
 			cpu->cpu = cputime_add(cpu->cpu, virt_ticks(t));
 			t = next_thread(t);
 		} while (t != p);
-		spin_unlock_irqrestore(&p->sighand->siglock, flags);
 		break;
 	case CPUCLOCK_SCHED:
-		spin_lock_irqsave(&p->sighand->siglock, flags);
 		cpu->sched = p->signal->sched_time;
 		/* Add in each other live thread.  */
 		while ((t = next_thread(t)) != p) {
@@ -237,12 +232,28 @@ static int cpu_clock_sample_group(clocki
 		} else {
 			cpu->sched += p->sched_time;
 		}
-		spin_unlock_irqrestore(&p->sighand->siglock, flags);
 		break;
 	}
 	return 0;
 }
 
+/*
+ * Sample a process (thread group) clock for the given group_leader task.
+ * Must be called with tasklist_lock held for reading.
+ */
+static int cpu_clock_sample_group(clockid_t which_clock,
+				  struct task_struct *p,
+				  union cpu_time_count *cpu)
+{
+	int ret;
+	unsigned long flags;
+	spin_lock_irqsave(&p->sighand->siglock, flags);
+	ret = cpu_clock_sample_group_locked(CPUCLOCK_WHICH(which_clock), p,
+					    cpu);
+	spin_unlock_irqrestore(&p->sighand->siglock, flags);
+	return ret;
+}
+
 
 int posix_cpu_clock_get(clockid_t which_clock, struct timespec *tp)
 {
@@ -453,20 +464,22 @@ void posix_cpu_timers_exit_group(struct 
  * Set the expiry times of all the threads in the process so one of them
  * will go off before the process cumulative expiry total is reached.
  */
-static void
-process_timer_rebalance(struct k_itimer *timer, union cpu_time_count val)
+static void process_timer_rebalance(struct task_struct *p,
+				    unsigned int clock_idx,
+				    union cpu_time_count expires,
+				    union cpu_time_count val)
 {
 	cputime_t ticks, left;
 	unsigned long long ns, nsleft;
-	struct task_struct *const p = timer->it.cpu.task, *t = p;
+ 	struct task_struct *t = p;
 	unsigned int nthreads = atomic_read(&p->signal->live);
 
-	switch (CPUCLOCK_WHICH(timer->it_clock)) {
+	switch (clock_idx) {
 	default:
 		BUG();
 		break;
 	case CPUCLOCK_PROF:
-		left = cputime_sub(timer->it.cpu.expires.cpu, val.cpu)
+		left = cputime_sub(expires.cpu, val.cpu)
 			/ nthreads;
 		do {
 			if (!unlikely(t->exit_state)) {
@@ -481,7 +494,7 @@ process_timer_rebalance(struct k_itimer 
 		} while (t != p);
 		break;
 	case CPUCLOCK_VIRT:
-		left = cputime_sub(timer->it.cpu.expires.cpu, val.cpu)
+		left = cputime_sub(expires.cpu, val.cpu)
 			/ nthreads;
 		do {
 			if (!unlikely(t->exit_state)) {
@@ -496,7 +509,7 @@ process_timer_rebalance(struct k_itimer 
 		} while (t != p);
 		break;
 	case CPUCLOCK_SCHED:
-		nsleft = timer->it.cpu.expires.sched - val.sched;
+		nsleft = expires.sched - val.sched;
 		do_div(nsleft, nthreads);
 		do {
 			if (!unlikely(t->exit_state)) {
@@ -590,7 +603,31 @@ static void arm_timer(struct k_itimer *t
 			 * For a process timer, we must balance
 			 * all the live threads' expirations.
 			 */
-			process_timer_rebalance(timer, now);
+			switch (CPUCLOCK_WHICH(timer->it_clock)) {
+			default:
+				BUG();
+			case CPUCLOCK_VIRT:
+				if (!cputime_eq(p->signal->it_virt_expires,
+						cputime_zero) &&
+				    cputime_lt(p->signal->it_virt_expires,
+					       timer->it.cpu.expires.cpu))
+					break;
+				goto rebalance;
+			case CPUCLOCK_PROF:
+				if (!cputime_eq(p->signal->it_prof_expires,
+						cputime_zero) &&
+				    cputime_lt(p->signal->it_prof_expires,
+					       timer->it.cpu.expires.cpu))
+					break;
+				goto rebalance;
+			case CPUCLOCK_SCHED:
+			rebalance:
+				process_timer_rebalance(
+					timer->it.cpu.task,
+					CPUCLOCK_WHICH(timer->it_clock),
+					timer->it.cpu.expires, now);
+				break;
+			}
 		}
 	}
 
@@ -952,7 +989,9 @@ static void check_process_timers(struct 
 	 * Don't sample the current process CPU clocks if there are no timers.
 	 */
 	if (list_empty(&timers[CPUCLOCK_PROF]) &&
+	    cputime_eq(sig->it_prof_expires, cputime_zero) &&
 	    list_empty(&timers[CPUCLOCK_VIRT]) &&
+	    cputime_eq(sig->it_virt_expires, cputime_zero) &&
 	    list_empty(&timers[CPUCLOCK_SCHED]))
 		return;
 
@@ -1012,6 +1051,42 @@ static void check_process_timers(struct 
 		list_move_tail(&t->entry, firing);
 	}
 
+	/*
+	 * Check for the special case process timers.
+	 */
+	if (!cputime_eq(sig->it_prof_expires, cputime_zero)) {
+		if (cputime_ge(ptime, sig->it_prof_expires)) {
+			/* ITIMER_PROF fires and reloads.  */
+			sig->it_prof_expires = sig->it_prof_incr;
+			if (!cputime_eq(sig->it_prof_expires, cputime_zero)) {
+				sig->it_prof_expires = cputime_add(
+					sig->it_prof_expires, ptime);
+			}
+			__group_send_sig_info(SIGPROF, SEND_SIG_PRIV, tsk);
+		}
+		if (!cputime_eq(sig->it_prof_expires, cputime_zero) &&
+		    (cputime_eq(prof_expires, cputime_zero) ||
+		     cputime_lt(sig->it_prof_expires, prof_expires))) {
+			prof_expires = sig->it_prof_expires;
+		}
+	}
+	if (!cputime_eq(sig->it_virt_expires, cputime_zero)) {
+		if (cputime_ge(utime, sig->it_virt_expires)) {
+			/* ITIMER_VIRTUAL fires and reloads.  */
+			sig->it_virt_expires = sig->it_virt_incr;
+			if (!cputime_eq(sig->it_virt_expires, cputime_zero)) {
+				sig->it_virt_expires = cputime_add(
+					sig->it_virt_expires, utime);
+			}
+			__group_send_sig_info(SIGVTALRM, SEND_SIG_PRIV, tsk);
+		}
+		if (!cputime_eq(sig->it_virt_expires, cputime_zero) &&
+		    (cputime_eq(virt_expires, cputime_zero) ||
+		     cputime_lt(sig->it_virt_expires, virt_expires))) {
+			virt_expires = sig->it_virt_expires;
+		}
+	}
+
 	if (!cputime_eq(prof_expires, cputime_zero) ||
 	    !cputime_eq(virt_expires, cputime_zero) ||
 	    sched_expires != 0) {
@@ -1197,6 +1272,50 @@ void run_posix_cpu_timers(struct task_st
 	}
 }
 
+/*
+ * Set one of the process-wide special case CPU timers.
+ * The tasklist_lock and tsk->sighand->siglock must be held by the caller.
+ */
+void set_process_cpu_timer(struct task_struct *tsk, unsigned int clock_idx,
+			   cputime_t *newval, cputime_t *oldval)
+{
+	union cpu_time_count now;
+	struct list_head *head;
+
+	BUG_ON(clock_idx == CPUCLOCK_SCHED);
+	cpu_clock_sample_group_locked(clock_idx, tsk, &now);
+
+	if (oldval && !cputime_eq(*oldval, cputime_zero)) {
+		if (cputime_le(*oldval, now.cpu)) { /* Just about to fire. */
+			*oldval = jiffies_to_cputime(1);
+		} else {
+			*oldval = cputime_sub(*oldval, now.cpu);
+		}
+	}
+
+	if (cputime_eq(*newval, cputime_zero))
+		return;
+	*newval = cputime_add(*newval, now.cpu);
+
+	/*
+	 * Check whether there are any process timers already set to fire
+	 * before this one.  If so, we don't have anything more to do.
+	 */
+	head = &tsk->signal->cpu_timers[clock_idx];
+	if (list_empty(head) ||
+	    cputime_ge(list_entry(head->next,
+				  struct cpu_timer_list, entry)->expires.cpu,
+		       *newval)) {
+		/*
+		 * Rejigger each thread's expiry time so that one will
+		 * notice before we hit the process-cumulative expiry time.
+		 */
+		union cpu_time_count expires = { .sched = 0 };
+		expires.cpu = *newval;
+		process_timer_rebalance(tsk, clock_idx, expires, now);
+	}
+}
+
 static long posix_cpu_clock_nanosleep_restart(struct restart_block *);
 
 int posix_cpu_nsleep(clockid_t which_clock, int flags,
diff -puN kernel/sched.c~make-itimer_prof-itimer_virtual-per-process kernel/sched.c
--- 25/kernel/sched.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/sched.c	2005-03-07 21:14:14.000000000 -0800
@@ -2284,46 +2284,6 @@ unsigned long long current_sched_time(co
 			((rq)->curr->static_prio > (rq)->best_expired_prio))
 
 /*
- * Do the virtual cpu time signal calculations.
- * @p: the process that the cpu time gets accounted to
- * @cputime: the cpu time spent in user space since the last update
- */
-static inline void account_it_virt(struct task_struct * p, cputime_t cputime)
-{
-	cputime_t it_virt = p->it_virt_value;
-
-	if (cputime_gt(it_virt, cputime_zero) &&
-	    cputime_gt(cputime, cputime_zero)) {
-		if (cputime_ge(cputime, it_virt)) {
-			it_virt = cputime_add(it_virt, p->it_virt_incr);
-			send_sig(SIGVTALRM, p, 1);
-		}
-		it_virt = cputime_sub(it_virt, cputime);
-		p->it_virt_value = it_virt;
-	}
-}
-
-/*
- * Do the virtual profiling signal calculations.
- * @p: the process that the cpu time gets accounted to
- * @cputime: the cpu time spent in user and kernel space since the last update
- */
-static void account_it_prof(struct task_struct *p, cputime_t cputime)
-{
-	cputime_t it_prof = p->it_prof_value;
-
-	if (cputime_gt(it_prof, cputime_zero) &&
-	    cputime_gt(cputime, cputime_zero)) {
-		if (cputime_ge(cputime, it_prof)) {
-			it_prof = cputime_add(it_prof, p->it_prof_incr);
-			send_sig(SIGPROF, p, 1);
-		}
-		it_prof = cputime_sub(it_prof, cputime);
-		p->it_prof_value = it_prof;
-	}
-}
-
-/*
  * Check if the process went over its cputime resource limit after
  * some cpu time got added to utime/stime.
  * @p: the process that the cpu time gets accounted to
@@ -2360,10 +2320,8 @@ void account_user_time(struct task_struc
 
 	p->utime = cputime_add(p->utime, cputime);
 
-	/* Check for signals (SIGVTALRM, SIGPROF, SIGXCPU & SIGKILL). */
+	/* Check for signals (SIGXCPU & SIGKILL). */
 	check_rlimit(p, cputime);
-	account_it_virt(p, cputime);
-	account_it_prof(p, cputime);
 
 	/* Add user time to cpustat. */
 	tmp = cputime_to_cputime64(cputime);
@@ -2388,10 +2346,9 @@ void account_system_time(struct task_str
 
 	p->stime = cputime_add(p->stime, cputime);
 
-	/* Check for signals (SIGPROF, SIGXCPU & SIGKILL). */
+	/* Check for signals (SIGXCPU & SIGKILL). */
 	if (likely(p->signal && p->exit_state < EXIT_ZOMBIE)) {
 		check_rlimit(p, cputime);
-		account_it_prof(p, cputime);
 	}
 
 	/* Add system time to cpustat. */
diff -puN kernel/signal.c~make-itimer_prof-itimer_virtual-per-process kernel/signal.c
--- 25/kernel/signal.c~make-itimer_prof-itimer_virtual-per-process	2005-03-07 20:41:53.000000000 -0800
+++ 25-akpm/kernel/signal.c	2005-03-07 21:14:14.000000000 -0800
@@ -1048,7 +1048,7 @@ __group_complete_signal(int sig, struct 
 	return;
 }
 
-static int
+int
 __group_send_sig_info(int sig, struct siginfo *info, struct task_struct *p)
 {
 	int ret = 0;
_
