[Armadillo:04098] Re: Armadillo-220 でのsetitimer

Yasushi SHOJI email@hidden
2009年 3月 6日 (金) 15:38:46 JST


At Tue, 03 Mar 2009 10:32:44 +0900,
Yasushi SHOJI wrote:
> 
> At Mon, 2 Mar 2009 16:46:52 +0900 (JST),
> nakamura wrote:
> > 
> > Armadillo-220で、setitimer()で次のような現象がでています。
> > このような現象について、何か情報をお持ちの方はいらっしゃいますか?
> > 
> > ●現象
> > 10〜19msecを指定すると30msec間隔でシグナル発生。
> 
> 再現しました。
> 
> 報告ありがとうございます。調べてみたいと思います。

原因わかりました。setitimer()のコードに off by oneのバグがあったようで
す。パッチを添付します。

http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=d912d1ff218195c248c770eb677726695e07aa40

ちなみに、現在 a220が採用しているカーネルではタイマーの精度が 10msなの
で 10-19msを指定すると 10ms分繰り上がって 20msになります。9999usまでの
指定であれば、10msでシグナルが発生します。

最近の x86では精度の高いタイマーを使っているので、上記の制限はありません。

テストに使ったコードは、以下の通りです。検証してもらえると助かります。

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>

/* #define USEC (9999) */
#define USEC (10000)

void action(int sig, siginfo_t *info, void *context)
{
	struct timeval tv;
	memset(&tv, 0, sizeof(tv));
	gettimeofday(&tv, NULL);
	printf("%lu:%06lu\n", tv.tv_sec, tv.tv_usec);
}

int main()
{
	struct itimerval val;
	struct sigaction act;

	memset(&val, 0, sizeof(val));
	memset(&act, 0, sizeof(act));

	act.sa_sigaction = action;
	sigaction(SIGALRM, &act, NULL);

	val.it_value.tv_usec = USEC;
	val.it_interval.tv_usec = USEC;
	action(0, NULL, NULL);
	setitimer(ITIMER_REAL, &val, NULL);
	while (1)
		sleep(1);

	return 0;
}
-- 
            yashi

commit d912d1ff218195c248c770eb677726695e07aa40
Author: George Anzinger <email@hidden>
Date:   Wed Jul 27 11:43:44 2005 -0700

    [PATCH] itimer fixes
    
    Fix the recent off-by-one fix in the itimer code:
    
    1. The repeating timer is figured using the requested time
    	(not +1 as we know where we are in the jiffie).
    
    2. The tests for interval too large are left to the time_val to jiffie code.
    
    Signed-off-by: George Anzinger <email@hidden>
    Signed-off-by: Andrew Morton <email@hidden>
    Signed-off-by: Linus Torvalds <email@hidden>

diff --git a/kernel/itimer.c b/kernel/itimer.c
index a72cb0e..7c1b25e 100644
--- a/kernel/itimer.c
+++ b/kernel/itimer.c
@@ -112,28 +112,11 @@ 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;
-	/* the "+ 1" below makes sure that the timer doesn't go off before
-	 * the interval requested. This could happen if
-	 * time requested % (usecs per jiffy) is more than the usecs left
-	 * in the current jiffy */
-	p->signal->real_timer.expires = jiffies + interval + 1;
-	add_timer(&p->signal->real_timer);
-}
 
 void it_real_fn(unsigned long __data)
 {
 	struct task_struct * p = (struct task_struct *) __data;
+	unsigned long inc = p->signal->it_real_incr;
 
 	send_group_sig_info(SIGALRM, SEND_SIG_PRIV, p);
 
@@ -141,14 +124,23 @@ void it_real_fn(unsigned long __data)
 	 * 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.
+	 * Note, we KNOW we are (or should be) at a jiffie edge here so
+	 * we don't need the +1 stuff.  Also, we want to use the prior
+	 * expire value so as to not "slip" a jiffie if we are late.
+	 * Deal with requesting a time prior to "now" here rather than
+	 * in add_timer.
 	 */
-	it_real_arm(p, p->signal->it_real_incr);
+	if (!inc)
+		return;
+	while (time_before_eq(p->signal->real_timer.expires, jiffies))
+		p->signal->real_timer.expires += inc;
+	add_timer(&p->signal->real_timer);
 }
 
 int do_setitimer(int which, struct itimerval *value, struct itimerval *ovalue)
 {
 	struct task_struct *tsk = current;
- 	unsigned long val, interval;
+ 	unsigned long val, interval, expires;
 	cputime_t cval, cinterval, nval, ninterval;
 
 	switch (which) {
@@ -164,7 +156,10 @@ again:
 		}
 		tsk->signal->it_real_incr =
 			timeval_to_jiffies(&value->it_interval);
-		it_real_arm(tsk, timeval_to_jiffies(&value->it_value));
+		expires = timeval_to_jiffies(&value->it_value);
+		if (expires)
+			mod_timer(&tsk->signal->real_timer,
+				  jiffies + 1 + expires);
 		spin_unlock_irq(&tsk->sighand->siglock);
 		if (ovalue) {
 			jiffies_to_timeval(val, &ovalue->it_value);



armadillo メーリングリストの案内