Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 1911

kernel-2.6.18-238.el5.src.rpm

From: Stanislaw Gruszka <sgruszka@redhat.com>
Date: Mon, 14 Dec 2009 10:17:33 -0500
Subject: [misc] fix itimers periodic tics precision
Message-id: <20091214111733.7004eea2@dhcp-lab-109.englab.brq.redhat.com>
Patchwork-id: 21915
O-Subject: [RHEL5.5 PATCH V2] BZ441134: Fix itimers periodic tics precision
Bugzilla: 441134
RH-Acked-by: Michal Schmidt <mschmidt@redhat.com>
RH-Acked-by: Oleg Nesterov <oleg@redhat.com>

BZ#441134
https://bugzilla.redhat.com/show_bug.cgi?id=441134

Description:
Customers needs this patch for increase gprof accuracy. Patch works as
described on upstream commit:

    Measure ITIMER_PROF and ITIMER_VIRT timers interval error
    between real ticks and requested by user. Take it into account
    when scheduling next tick.

    This patch introduce possibility where time between two
    consecutive tics is smaller then requested interval, it
    preserve however dependency that n tick is generated not
    earlier than n*interval time - counting from the beginning of
    periodic signal generation.

    Changes V1 -> V2
    Fix kABI issues.

Upstream Status of the patch:
commit 8356b5f9c424e5831715abbce747197c30d1fd71
Author: Stanislaw Gruszka <sgruszka@redhat.com>
Date:   Wed Jul 29 12:15:27 2009 +0200

    itimers: Fix periodic tics precision

kABI:
Upstream patch change signal_struct, RHEL port use signal_struct_aux for
adding new fields.

Brew build:
https://brewweb.devel.redhat.com/taskinfo?taskID=2149298

Test Status:
Patch was tested by me and customer on i686, x86_64, powerpc, ia64 using
reproducers from bugzilla. Version V2 of the patch I tested only on my
workstation.


diff --git a/include/linux/sched.h b/include/linux/sched.h
index 75bd351..49cfe5e 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -539,6 +539,8 @@ struct signal_struct {
 struct signal_struct_aux {
 	u64 rchar, wchar, syscr, syscw;
 	struct task_io_accounting ioac;
+	u32 it_prof_error, it_prof_incr_error;
+	u32 it_virt_error, it_virt_incr_error;
 };
 
 static inline void init_signal_aux(struct signal_struct_aux *aux)
diff --git a/kernel/itimer.c b/kernel/itimer.c
index 46ede01..587cf43 100644
--- a/kernel/itimer.c
+++ b/kernel/itimer.c
@@ -199,12 +199,24 @@ static void check_itimerval(struct itimerval *value) {
 		fixup_timeval(&value->it_interval, 1);
 }
 
+static inline u32 cputime_sub_ns(cputime_t ct, s64 real_ns)
+{
+	struct timespec ts;
+	s64 cpu_ns;
+
+	cputime_to_timespec(ct, &ts);
+	cpu_ns = timespec_to_ns(&ts);
+
+	return (cpu_ns <= real_ns) ? 0 : cpu_ns - real_ns;
+}
+
 int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
 {
 	struct task_struct *tsk = current;
 	struct hrtimer *timer;
 	ktime_t expires;
 	cputime_t cval, cinterval, nval, ninterval;
+	s64 ns_ninterval, ns_nval;
 
 	/*
 	 * Validate the timevals in value.
@@ -243,9 +255,15 @@ again:
 		break;
 	case ITIMER_VIRTUAL:
 		nval = timeval_to_cputime(&value->it_value);
+		ns_nval = timeval_to_ns(&value->it_value);
 		ninterval = timeval_to_cputime(&value->it_interval);
+		ns_ninterval = timeval_to_ns(&value->it_interval);
 		read_lock(&tasklist_lock);
 		spin_lock_irq(&tsk->sighand->siglock);
+		signal_aux(tsk->signal)->it_virt_incr_error =
+				cputime_sub_ns(ninterval, ns_ninterval);
+		signal_aux(tsk->signal)->it_virt_error =
+				cputime_sub_ns(nval, ns_nval);
 		cval = tsk->signal->it_virt_expires;
 		cinterval = tsk->signal->it_virt_incr;
 		if (!cputime_eq(cval, cputime_zero) ||
@@ -268,9 +286,15 @@ again:
 		break;
 	case ITIMER_PROF:
 		nval = timeval_to_cputime(&value->it_value);
+		ns_nval = timeval_to_ns(&value->it_value);
 		ninterval = timeval_to_cputime(&value->it_interval);
+		ns_ninterval = timeval_to_ns(&value->it_interval);
 		read_lock(&tasklist_lock);
 		spin_lock_irq(&tsk->sighand->siglock);
+		signal_aux(tsk->signal)->it_prof_incr_error =
+				cputime_sub_ns(ninterval, ns_ninterval);
+		signal_aux(tsk->signal)->it_prof_error =
+				cputime_sub_ns(nval, ns_nval);
 		cval = tsk->signal->it_prof_expires;
 		cinterval = tsk->signal->it_prof_incr;
 		if (!cputime_eq(cval, cputime_zero) ||
diff --git a/kernel/posix-cpu-timers.c b/kernel/posix-cpu-timers.c
index 0779b48..37e4223 100644
--- a/kernel/posix-cpu-timers.c
+++ b/kernel/posix-cpu-timers.c
@@ -1008,6 +1008,8 @@ static void check_thread_timers(struct task_struct *tsk,
 	}
 }
 
+static u32 onecputick;
+
 /*
  * Check for any per-thread CPU timers that have fired and move them
  * off the tsk->*_timers list onto the firing list.  Per-thread timers
@@ -1099,11 +1101,21 @@ static void check_process_timers(struct task_struct *tsk,
 	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);
-			}
+			if (!cputime_eq(sig->it_prof_incr, cputime_zero)) {
+				sig->it_prof_expires =
+					cputime_add(sig->it_prof_expires,
+						    sig->it_prof_incr);
+				signal_aux(sig)->it_prof_error +=
+					signal_aux(sig)->it_prof_incr_error;
+				if (signal_aux(sig)->it_prof_error >= onecputick) {
+					sig->it_prof_expires = cputime_sub(
+						sig->it_prof_expires,
+						jiffies_to_cputime(1));
+					signal_aux(sig)->it_prof_error -= onecputick;
+				}
+			} else
+				sig->it_prof_expires = cputime_zero;
+
 			__group_send_sig_info(SIGPROF, SEND_SIG_PRIV, tsk);
 		}
 		if (!cputime_eq(sig->it_prof_expires, cputime_zero) &&
@@ -1115,11 +1127,21 @@ static void check_process_timers(struct task_struct *tsk,
 	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);
-			}
+			if (!cputime_eq(sig->it_virt_incr, cputime_zero)) {
+				sig->it_virt_expires =
+					cputime_add(sig->it_virt_expires,
+						    sig->it_virt_incr);
+				signal_aux(sig)->it_virt_error +=
+					signal_aux(sig)->it_virt_incr_error;
+				if (signal_aux(sig)->it_virt_error >= onecputick) {
+					sig->it_virt_expires = cputime_sub(
+						sig->it_virt_expires,
+						jiffies_to_cputime(1));
+					signal_aux(sig)->it_virt_error -= onecputick;
+				}
+			} else
+				sig->it_virt_expires = cputime_zero;
+
 			__group_send_sig_info(SIGVTALRM, SEND_SIG_PRIV, tsk);
 		}
 		if (!cputime_eq(sig->it_virt_expires, cputime_zero) &&
@@ -1576,6 +1598,11 @@ static __init int init_posix_cpu_timers(void)
 		.timer_create = thread_cpu_timer_create,
 		.nsleep = thread_cpu_nsleep,
 	};
+	struct timespec ts;
+
+	cputime_to_timespec(jiffies_to_cputime(1), &ts);
+	onecputick = ts.tv_nsec;
+	WARN_ON(ts.tv_sec != 0);
 
 	register_posix_clock(CLOCK_PROCESS_CPUTIME_ID, &process);
 	register_posix_clock(CLOCK_THREAD_CPUTIME_ID, &thread);