Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 15 Feb 2003 15:40:24 +1100
From:      Tim Robbins <tjr@freebsd.org>
To:        freebsd-smp@freebsd.org
Subject:   Locking down itimers
Message-ID:  <20030215154024.A53601@dilbert.robbins.dropbear.id.au>

next in thread | raw e-mail | index | archive | help
This patch protects p_realtimer with the proc mutex instead of Giant,
and obtains sched_lock around accesses to p_stats->p_timer[] to avoid
a potential race with hardclock. It makes getitimer(), setitimer()
and the realitexpire() callout MP-safe.

It was necessary to restructure setitimer() a bit to guarantee that
the swap:
	ovalue = p_realtimer, p_realtimer = value;
occurs atomically with respect to other threads in the same process
calling setitimer() and the callout.

Does the patch seem reasonable? Any comments?


Tim

Index: sys/proc.h
===================================================================
RCS file: /x/freebsd/src/sys/sys/proc.h,v
retrieving revision 1.293
diff -U5 -p -r1.293 proc.h
--- sys/proc.h	9 Feb 2003 08:49:02 -0000	1.293
+++ sys/proc.h	12 Feb 2003 07:44:15 -0000
@@ -565,11 +565,11 @@ struct proc {
 /* The following fields are all zeroed upon creation in fork. */
 #define	p_startzero	p_oppid
 	pid_t		p_oppid; 	/* (c + e) Save ppid in ptrace. XXX */
 	struct vmspace	*p_vmspace;	/* (b) Address space. */
 	u_int		p_swtime;	/* (j) Time swapped in or out. */
-	struct itimerval p_realtimer;	/* (h?/k?) Alarm timer. */
+	struct itimerval p_realtimer;	/* (c) Alarm timer. */
 	struct bintime	p_runtime;	/* (j) Real time. */
 	int		p_profthreads;	/* (c) Num threads in addupc_task */
 	int		p_traceflag;	/* (o) Kernel trace points. */
 	struct vnode	*p_tracep;	/* (c + o) Trace to vnode. */
 	sigset_t	p_siglist;	/* (c) Sigs arrived, not delivered. */
Index: kern/kern_time.c
===================================================================
RCS file: /x/freebsd/src/sys/kern/kern_time.c,v
retrieving revision 1.96
diff -U5 -p -r1.96 kern_time.c
--- kern/kern_time.c	3 Feb 2003 19:49:34 -0000	1.96
+++ kern/kern_time.c	12 Feb 2003 14:07:00 -0000
@@ -406,45 +406,42 @@ struct getitimer_args {
 };
 #endif
 /*
  * MPSAFE
  */
-/* ARGSUSED */
 int
 getitimer(struct thread *td, struct getitimer_args *uap)
 {
 	struct proc *p = td->td_proc;
 	struct timeval ctv;
 	struct itimerval aitv;
-	int s;
 
 	if (uap->which > ITIMER_PROF)
 		return (EINVAL);
 
-	mtx_lock(&Giant);
-
-	s = splclock(); /* XXX still needed ? */
 	if (uap->which == ITIMER_REAL) {
 		/*
 		 * Convert from absolute to relative time in .it_value
 		 * part of real time timer.  If time for real time timer
 		 * has passed return 0, else return difference between
 		 * current time and time for the timer to go off.
 		 */
+		PROC_LOCK(p);
 		aitv = p->p_realtimer;
+		PROC_UNLOCK(p);
 		if (timevalisset(&aitv.it_value)) {
 			getmicrouptime(&ctv);
 			if (timevalcmp(&aitv.it_value, &ctv, <))
 				timevalclear(&aitv.it_value);
 			else
 				timevalsub(&aitv.it_value, &ctv);
 		}
 	} else {
+		mtx_lock_spin(&sched_lock);
 		aitv = p->p_stats->p_timer[uap->which];
+		mtx_unlock_spin(&sched_lock);
 	}
-	splx(s);
-	mtx_unlock(&Giant);
 	return (copyout(&aitv, uap->itv, sizeof (struct itimerval)));
 }
 
 #ifndef _SYS_SYSPROTO_H_
 struct setitimer_args {
@@ -453,63 +450,61 @@ struct setitimer_args {
 };
 #endif
 /*
  * MPSAFE
  */
-/* ARGSUSED */
 int
 setitimer(struct thread *td, struct setitimer_args *uap)
 {
 	struct proc *p = td->td_proc;
-	struct itimerval aitv;
+	struct itimerval aitv, oitv;
 	struct timeval ctv;
-	struct itimerval *itvp;
-	int s, error = 0;
+	int error;
+
+	if (uap->itv == NULL) {
+		uap->itv = uap->oitv;
+		return (getitimer(td, (struct getitimer_args *)uap));
+	}
 
 	if (uap->which > ITIMER_PROF)
 		return (EINVAL);
-	itvp = uap->itv;
-	if (itvp && (error = copyin(itvp, &aitv, sizeof(struct itimerval))))
+	if ((error = copyin(uap->itv, &aitv, sizeof(struct itimerval))))
 		return (error);
-
-	mtx_lock(&Giant);
-
-	if ((uap->itv = uap->oitv) &&
-	    (error = getitimer(td, (struct getitimer_args *)uap))) {
-		goto done2;
-	}
-	if (itvp == 0) {
-		error = 0;
-		goto done2;
-	}
-	if (itimerfix(&aitv.it_value)) {
-		error = EINVAL;
-		goto done2;
-	}
-	if (!timevalisset(&aitv.it_value)) {
+	if (itimerfix(&aitv.it_value))
+		return (EINVAL);
+	if (!timevalisset(&aitv.it_value))
 		timevalclear(&aitv.it_interval);
-	} else if (itimerfix(&aitv.it_interval)) {
-		error = EINVAL;
-		goto done2;
-	}
-	s = splclock(); /* XXX: still needed ? */
+	else if (itimerfix(&aitv.it_interval))
+		return (EINVAL);
+
 	if (uap->which == ITIMER_REAL) {
+		PROC_LOCK(p);
 		if (timevalisset(&p->p_realtimer.it_value))
 			callout_stop(&p->p_itcallout);
 		if (timevalisset(&aitv.it_value)) 
 			callout_reset(&p->p_itcallout, tvtohz(&aitv.it_value),
 			    realitexpire, p);
 		getmicrouptime(&ctv);
 		timevaladd(&aitv.it_value, &ctv);
+		oitv = p->p_realtimer;
 		p->p_realtimer = aitv;
+		PROC_UNLOCK(p);
+		if (timevalisset(&oitv.it_value)) {
+			if (timevalcmp(&oitv.it_value, &ctv, <))
+				timevalclear(&oitv.it_value);
+			else
+				timevalsub(&oitv.it_value, &ctv);
+		}
 	} else {
+		mtx_lock_spin(&sched_lock);
+		oitv = p->p_stats->p_timer[uap->which];
 		p->p_stats->p_timer[uap->which] = aitv;
+		mtx_unlock_spin(&sched_lock);
 	}
-	splx(s);
-done2:
-	mtx_unlock(&Giant);
-	return (error);
+	if (uap->oitv == NULL)
+		return (0);
+	return (copyout(&oitv, uap->oitv, sizeof(struct itimerval)));
 }
 
 /*
  * Real interval timer expired:
  * send process whose timer expired an alarm signal.
@@ -525,35 +520,31 @@ done2:
 void
 realitexpire(void *arg)
 {
 	struct proc *p;
 	struct timeval ctv, ntv;
-	int s;
 
 	p = (struct proc *)arg;
 	PROC_LOCK(p);
 	psignal(p, SIGALRM);
 	if (!timevalisset(&p->p_realtimer.it_interval)) {
 		timevalclear(&p->p_realtimer.it_value);
 		PROC_UNLOCK(p);
 		return;
 	}
 	for (;;) {
-		s = splclock(); /* XXX: still neeeded ? */
 		timevaladd(&p->p_realtimer.it_value,
 		    &p->p_realtimer.it_interval);
 		getmicrouptime(&ctv);
 		if (timevalcmp(&p->p_realtimer.it_value, &ctv, >)) {
 			ntv = p->p_realtimer.it_value;
 			timevalsub(&ntv, &ctv);
 			callout_reset(&p->p_itcallout, tvtohz(&ntv) - 1,
 			    realitexpire, p);
-			splx(s);
 			PROC_UNLOCK(p);
 			return;
 		}
-		splx(s);
 	}
 	/*NOTREACHED*/
 }
 
 /*
Index: kern/init_main.c
===================================================================
RCS file: /x/freebsd/src/sys/kern/init_main.c,v
retrieving revision 1.224
diff -U5 -p -r1.224 init_main.c
--- kern/init_main.c	4 Feb 2003 18:47:17 -0000	1.224
+++ kern/init_main.c	12 Feb 2003 08:18:51 -0000
@@ -384,11 +384,11 @@ proc0_init(void *dummy __unused)
 	p->p_leader = p;
 
 
 	bcopy("swapper", p->p_comm, sizeof ("swapper"));
 
-	callout_init(&p->p_itcallout, 0);
+	callout_init(&p->p_itcallout, 1);
 	callout_init(&td->td_slpcallout, 1);
 
 	/* Create credentials. */
 	p->p_ucred = crget();
 	p->p_ucred->cr_ngroups = 1;	/* group 0 */
Index: compat/linux/linux_misc.c
===================================================================
RCS file: /x/freebsd/src/sys/compat/linux/linux_misc.c,v
retrieving revision 1.134
diff -U5 -p -r1.134 linux_misc.c
--- compat/linux/linux_misc.c	17 Oct 2002 22:00:30 -0000	1.134
+++ compat/linux/linux_misc.c	13 Feb 2003 05:06:13 -0000
@@ -162,11 +162,10 @@ linux_sysinfo(struct thread *td, struct 
 int
 linux_alarm(struct thread *td, struct linux_alarm_args *args)
 {
 	struct itimerval it, old_it;
 	struct timeval tv;
-	int s;
 
 #ifdef DEBUG
 	if (ldebug(alarm))
 		printf(ARGS(alarm, "%u"), args->secs);
 #endif
@@ -176,22 +175,22 @@ linux_alarm(struct thread *td, struct li
 
 	it.it_value.tv_sec = (long)args->secs;
 	it.it_value.tv_usec = 0;
 	it.it_interval.tv_sec = 0;
 	it.it_interval.tv_usec = 0;
-	s = splsoftclock();
+	PROC_LOCK(td->td_proc);
 	old_it = td->td_proc->p_realtimer;
 	getmicrouptime(&tv);
 	if (timevalisset(&old_it.it_value))
 		callout_stop(&td->td_proc->p_itcallout);
 	if (it.it_value.tv_sec != 0) {
 		callout_reset(&td->td_proc->p_itcallout, tvtohz(&it.it_value),
 		    realitexpire, td->td_proc);
 		timevaladd(&it.it_value, &tv);
 	}
 	td->td_proc->p_realtimer = it;
-	splx(s);
+	PROC_UNLOCK(td->td_proc);
 	if (timevalcmp(&old_it.it_value, &tv, >)) {
 		timevalsub(&old_it.it_value, &tv);
 		if (old_it.it_value.tv_usec != 0)
 			old_it.it_value.tv_sec++;
 		td->td_retval[0] = old_it.it_value.tv_sec;

To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-smp" in the body of the message




Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030215154024.A53601>