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>
index | next in thread | raw e-mail
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
help
Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?20030215154024.A53601>
