timer fixes for PR 37093:

- Fix serious concurrency problems, making the code MT and MP safe in
  the process.
- Don't allocate memory or inspect process state from hardclock().
This commit is contained in:
ad 2008-04-21 00:13:46 +00:00
parent 160b1e8f05
commit 08b44dd8b9
6 changed files with 344 additions and 210 deletions

View File

@ -1,7 +1,7 @@
/* $NetBSD: linux_misc_notalpha.c,v 1.100 2007/12/26 13:48:53 njoly Exp $ */
/* $NetBSD: linux_misc_notalpha.c,v 1.101 2008/04/21 00:13:46 ad Exp $ */
/*-
* Copyright (c) 1995, 1998 The NetBSD Foundation, Inc.
* Copyright (c) 1995, 1998, 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -38,7 +38,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: linux_misc_notalpha.c,v 1.100 2007/12/26 13:48:53 njoly Exp $");
__KERNEL_RCSID(0, "$NetBSD: linux_misc_notalpha.c,v 1.101 2008/04/21 00:13:46 ad Exp $");
#include <sys/param.h>
#include <sys/systm.h>
@ -94,6 +94,8 @@ static void bsd_to_linux_statfs64(const struct statvfs *,
/*
* Alarm. This is a libc call which uses setitimer(2) in NetBSD.
* Fiddle with the timers to make it work.
*
* XXX This shouldn't be dicking about with the ptimer stuff directly.
*/
int
linux_sys_alarm(struct lwp *l, const struct linux_sys_alarm_args *uap, register_t *retval)
@ -104,19 +106,25 @@ linux_sys_alarm(struct lwp *l, const struct linux_sys_alarm_args *uap, register_
struct proc *p = l->l_proc;
struct timeval now;
struct itimerval *itp, it;
struct ptimer *ptp;
int s;
struct ptimer *ptp, *spare;
extern kmutex_t timer_lock;
struct ptimers *pts;
if (p->p_timers && p->p_timers->pts_timers[ITIMER_REAL])
itp = &p->p_timers->pts_timers[ITIMER_REAL]->pt_time;
if ((pts = p->p_timers) == NULL)
pts = timers_alloc(p);
spare = NULL;
retry:
mutex_spin_enter(&timer_lock);
if (pts && pts->pts_timers[ITIMER_REAL])
itp = &pts->pts_timers[ITIMER_REAL]->pt_time;
else
itp = NULL;
s = splclock();
/*
* Clear any pending timer alarms.
*/
if (itp) {
callout_stop(&p->p_timers->pts_timers[ITIMER_REAL]->pt_ch);
callout_stop(&pts->pts_timers[ITIMER_REAL]->pt_ch);
timerclear(&itp->it_interval);
getmicrotime(&now);
if (timerisset(&itp->it_value) &&
@ -138,7 +146,7 @@ linux_sys_alarm(struct lwp *l, const struct linux_sys_alarm_args *uap, register_
if (SCARG(uap, secs) == 0) {
if (itp)
timerclear(&itp->it_value);
splx(s);
mutex_spin_exit(&timer_lock);
return 0;
}
@ -149,23 +157,29 @@ linux_sys_alarm(struct lwp *l, const struct linux_sys_alarm_args *uap, register_
it.it_value.tv_sec = SCARG(uap, secs);
it.it_value.tv_usec = 0;
if (itimerfix(&it.it_value) || itimerfix(&it.it_interval)) {
splx(s);
mutex_spin_exit(&timer_lock);
return (EINVAL);
}
if (p->p_timers == NULL)
timers_alloc(p);
ptp = p->p_timers->pts_timers[ITIMER_REAL];
ptp = pts->pts_timers[ITIMER_REAL];
if (ptp == NULL) {
ptp = pool_get(&ptimer_pool, PR_WAITOK);
if (spare == NULL) {
mutex_spin_exit(&timer_lock);
spare = pool_get(&ptimer_pool, PR_WAITOK);
goto retry;
}
ptp = spare;
spare = NULL;
ptp->pt_ev.sigev_notify = SIGEV_SIGNAL;
ptp->pt_ev.sigev_signo = SIGALRM;
ptp->pt_overruns = 0;
ptp->pt_proc = p;
ptp->pt_type = CLOCK_REALTIME;
ptp->pt_entry = CLOCK_REALTIME;
callout_init(&ptp->pt_ch, 0);
p->p_timers->pts_timers[ITIMER_REAL] = ptp;
ptp->pt_active = 0;
ptp->pt_queued = 0;
callout_init(&ptp->pt_ch, CALLOUT_MPSAFE);
pts->pts_timers[ITIMER_REAL] = ptp;
}
if (timerisset(&it.it_value)) {
@ -179,7 +193,7 @@ linux_sys_alarm(struct lwp *l, const struct linux_sys_alarm_args *uap, register_
realtimerexpire, ptp);
}
ptp->pt_time = it;
splx(s);
mutex_spin_exit(&timer_lock);
return 0;
}

View File

@ -1,4 +1,4 @@
/* $NetBSD: init_main.c,v 1.349 2008/04/14 18:07:51 ad Exp $ */
/* $NetBSD: init_main.c,v 1.350 2008/04/21 00:13:46 ad Exp $ */
/*-
* Copyright (c) 2008 The NetBSD Foundation, Inc.
@ -104,7 +104,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.349 2008/04/14 18:07:51 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: init_main.c,v 1.350 2008/04/21 00:13:46 ad Exp $");
#include "opt_ipsec.h"
#include "opt_ntp.h"
@ -419,6 +419,9 @@ main(void)
error = mi_cpu_attach(curcpu());
KASSERT(error == 0);
/* Initialize timekeeping, part 2. */
time_init2();
/*
* Initialize mbuf's. Do this now because we might attempt to
* allocate mbufs or mbuf clusters during autoconfiguration.

View File

@ -1,4 +1,4 @@
/* $NetBSD: kern_clock.c,v 1.119 2008/03/11 02:26:47 ad Exp $ */
/* $NetBSD: kern_clock.c,v 1.120 2008/04/21 00:13:46 ad Exp $ */
/*-
* Copyright (c) 2000, 2004, 2006, 2007, 2008 The NetBSD Foundation, Inc.
@ -76,7 +76,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.119 2008/03/11 02:26:47 ad Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_clock.c,v 1.120 2008/04/21 00:13:46 ad Exp $");
#include "opt_ntp.h"
#include "opt_multiprocessor.h"
@ -201,25 +201,12 @@ void
hardclock(struct clockframe *frame)
{
struct lwp *l;
struct proc *p;
struct cpu_info *ci = curcpu();
struct ptimer *pt;
struct cpu_info *ci;
ci = curcpu();
l = ci->ci_data.cpu_onproc;
if (!CURCPU_IDLE_P()) {
p = l->l_proc;
/*
* Run current process's virtual and profile time, as needed.
*/
if (CLKF_USERMODE(frame) && p->p_timers &&
(pt = LIST_FIRST(&p->p_timers->pts_virtual)) != NULL)
if (itimerdecr(pt, tick) == 0)
itimerfire(pt);
if (p->p_timers &&
(pt = LIST_FIRST(&p->p_timers->pts_prof)) != NULL)
if (itimerdecr(pt, tick) == 0)
itimerfire(pt);
}
timer_tick(l, CLKF_USERMODE(frame));
/*
* If no separate statistics clock is available, run it from here.

View File

@ -1,7 +1,7 @@
/* $NetBSD: kern_time.c,v 1.141 2008/02/25 12:25:03 yamt Exp $ */
/* $NetBSD: kern_time.c,v 1.142 2008/04/21 00:13:46 ad Exp $ */
/*-
* Copyright (c) 2000, 2004, 2005, 2007 The NetBSD Foundation, Inc.
* Copyright (c) 2000, 2004, 2005, 2007, 2008 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
@ -68,7 +68,7 @@
*/
#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: kern_time.c,v 1.141 2008/02/25 12:25:03 yamt Exp $");
__KERNEL_RCSID(0, "$NetBSD: kern_time.c,v 1.142 2008/04/21 00:13:46 ad Exp $");
#include <sys/param.h>
#include <sys/resourcevar.h>
@ -88,7 +88,15 @@ __KERNEL_RCSID(0, "$NetBSD: kern_time.c,v 1.141 2008/02/25 12:25:03 yamt Exp $")
#include <sys/cpu.h>
static void timer_intr(void *);
static void itimerfire(struct ptimer *);
static void itimerfree(struct ptimers *, int);
kmutex_t time_lock;
kmutex_t timer_lock;
static void *timer_sih;
static TAILQ_HEAD(, ptimer) timer_queue;
POOL_INIT(ptimer_pool, sizeof(struct ptimer), 0, 0, 0, "ptimerpl",
&pool_allocator_nointr, IPL_NONE);
@ -105,6 +113,16 @@ time_init(void)
mutex_init(&time_lock, MUTEX_DEFAULT, IPL_NONE);
}
void
time_init2(void)
{
TAILQ_INIT(&timer_queue);
mutex_init(&timer_lock, MUTEX_DEFAULT, IPL_SCHED);
timer_sih = softint_establish(SOFTINT_CLOCK | SOFTINT_MPSAFE,
timer_intr, NULL);
}
/* Time of day and interval timer support.
*
* These routines provide the kernel entry points to get and set
@ -517,28 +535,20 @@ timer_create1(timer_t *tid, clockid_t id, struct sigevent *evp,
{
int error;
timer_t timerid;
struct ptimers *pts;
struct ptimer *pt;
struct proc *p;
p = l->l_proc;
if (id < CLOCK_REALTIME ||
id > CLOCK_PROF)
if (id < CLOCK_REALTIME || id > CLOCK_PROF)
return (EINVAL);
if (p->p_timers == NULL)
timers_alloc(p);
/* Find a free timer slot, skipping those reserved for setitimer(). */
for (timerid = 3; timerid < TIMER_MAX; timerid++)
if (p->p_timers->pts_timers[timerid] == NULL)
break;
if (timerid == TIMER_MAX)
return EAGAIN;
if ((pts = p->p_timers) == NULL)
pts = timers_alloc(p);
pt = pool_get(&ptimer_pool, PR_WAITOK);
if (evp) {
if (evp != NULL) {
if (((error =
(*fetch_event)(evp, &pt->pt_ev, sizeof(pt->pt_ev))) != 0) ||
((pt->pt_ev.sigev_notify < SIGEV_NONE) ||
@ -546,7 +556,19 @@ timer_create1(timer_t *tid, clockid_t id, struct sigevent *evp,
pool_put(&ptimer_pool, pt);
return (error ? error : EINVAL);
}
} else {
}
/* Find a free timer slot, skipping those reserved for setitimer(). */
mutex_spin_enter(&timer_lock);
for (timerid = 3; timerid < TIMER_MAX; timerid++)
if (pts->pts_timers[timerid] == NULL)
break;
if (timerid == TIMER_MAX) {
mutex_spin_exit(&timer_lock);
pool_put(&ptimer_pool, pt);
return EAGAIN;
}
if (evp == NULL) {
pt->pt_ev.sigev_notify = SIGEV_SIGNAL;
switch (id) {
case CLOCK_REALTIME:
@ -567,19 +589,17 @@ timer_create1(timer_t *tid, clockid_t id, struct sigevent *evp,
pt->pt_info.ksi_pid = p->p_pid;
pt->pt_info.ksi_uid = kauth_cred_getuid(l->l_cred);
pt->pt_info.ksi_value = pt->pt_ev.sigev_value;
pt->pt_type = id;
pt->pt_proc = p;
pt->pt_overruns = 0;
pt->pt_poverruns = 0;
pt->pt_entry = timerid;
pt->pt_queued = false;
pt->pt_active = 0;
timerclear(&pt->pt_time.it_value);
if (id == CLOCK_REALTIME)
callout_init(&pt->pt_ch, 0);
else
pt->pt_active = 0;
p->p_timers->pts_timers[timerid] = pt;
callout_init(&pt->pt_ch, 0);
pts->pts_timers[timerid] = pt;
mutex_spin_exit(&timer_lock);
return copyout(&timerid, tid, sizeof(timerid));
}
@ -594,31 +614,29 @@ sys_timer_delete(struct lwp *l, const struct sys_timer_delete_args *uap,
} */
struct proc *p = l->l_proc;
timer_t timerid;
struct ptimers *pts;
struct ptimer *pt, *ptn;
int s;
timerid = SCARG(uap, timerid);
if ((p->p_timers == NULL) ||
(timerid < 2) || (timerid >= TIMER_MAX) ||
((pt = p->p_timers->pts_timers[timerid]) == NULL))
pts = p->p_timers;
if (pts == NULL || timerid < 2 || timerid >= TIMER_MAX)
return (EINVAL);
if (pt->pt_type == CLOCK_REALTIME) {
callout_stop(&pt->pt_ch);
callout_destroy(&pt->pt_ch);
} else if (pt->pt_active) {
s = splclock();
mutex_spin_enter(&timer_lock);
if ((pt = pts->pts_timers[timerid]) == NULL) {
mutex_spin_exit(&timer_lock);
return (EINVAL);
}
if (pt->pt_active) {
ptn = LIST_NEXT(pt, pt_list);
LIST_REMOVE(pt, pt_list);
for ( ; ptn; ptn = LIST_NEXT(ptn, pt_list))
timeradd(&pt->pt_time.it_value, &ptn->pt_time.it_value,
&ptn->pt_time.it_value);
splx(s);
pt->pt_active = 0;
}
p->p_timers->pts_timers[timerid] = NULL;
pool_put(&ptimer_pool, pt);
itimerfree(pts, timerid);
return (0);
}
@ -635,6 +653,8 @@ timer_settime(struct ptimer *pt)
struct ptimer *ptn, *pptn;
struct ptlist *ptl;
KASSERT(mutex_owned(&timer_lock));
if (pt->pt_type == CLOCK_REALTIME) {
callout_stop(&pt->pt_ch);
if (timerisset(&pt->pt_time.it_value)) {
@ -690,6 +710,8 @@ timer_gettime(struct ptimer *pt, struct itimerval *aitv)
struct timeval now;
struct ptimer *ptn;
KASSERT(mutex_owned(&timer_lock));
*aitv = pt->pt_time;
if (pt->pt_type == CLOCK_REALTIME) {
/*
@ -759,23 +781,27 @@ dotimer_settime(int timerid, struct itimerspec *value,
{
struct timeval now;
struct itimerval val, oval;
struct ptimers *pts;
struct ptimer *pt;
int s;
if ((p->p_timers == NULL) ||
(timerid < 2) || (timerid >= TIMER_MAX) ||
((pt = p->p_timers->pts_timers[timerid]) == NULL))
return (EINVAL);
pts = p->p_timers;
if (pts == NULL || timerid < 2 || timerid >= TIMER_MAX)
return EINVAL;
TIMESPEC_TO_TIMEVAL(&val.it_value, &value->it_value);
TIMESPEC_TO_TIMEVAL(&val.it_interval, &value->it_interval);
if (itimerfix(&val.it_value) || itimerfix(&val.it_interval))
return (EINVAL);
mutex_spin_enter(&timer_lock);
if ((pt = pts->pts_timers[timerid]) == NULL) {
mutex_spin_exit(&timer_lock);
return (EINVAL);
}
oval = pt->pt_time;
pt->pt_time = val;
s = splclock();
/*
* If we've been passed a relative time for a realtime timer,
* convert it to absolute; if an absolute time for a virtual
@ -805,7 +831,7 @@ dotimer_settime(int timerid, struct itimerspec *value,
}
timer_settime(pt);
splx(s);
mutex_spin_exit(&timer_lock);
if (ovalue) {
TIMEVAL_TO_TIMESPEC(&oval.it_value, &ovalue->it_value);
@ -837,18 +863,20 @@ sys_timer_gettime(struct lwp *l, const struct sys_timer_gettime_args *uap,
int
dotimer_gettime(int timerid, struct proc *p, struct itimerspec *its)
{
int s;
struct ptimer *pt;
struct ptimers *pts;
struct itimerval aitv;
if ((p->p_timers == NULL) ||
(timerid < 2) || (timerid >= TIMER_MAX) ||
((pt = p->p_timers->pts_timers[timerid]) == NULL))
pts = p->p_timers;
if (pts == NULL || timerid < 2 || timerid >= TIMER_MAX)
return (EINVAL);
s = splclock();
mutex_spin_enter(&timer_lock);
if ((pt = pts->pts_timers[timerid]) == NULL) {
mutex_spin_exit(&timer_lock);
return (EINVAL);
}
timer_gettime(pt, &aitv);
splx(s);
mutex_spin_exit(&timer_lock);
TIMEVAL_TO_TIMESPEC(&aitv.it_interval, &its->it_interval);
TIMEVAL_TO_TIMESPEC(&aitv.it_value, &its->it_value);
@ -869,17 +897,22 @@ sys_timer_getoverrun(struct lwp *l, const struct sys_timer_getoverrun_args *uap,
syscallarg(timer_t) timerid;
} */
struct proc *p = l->l_proc;
struct ptimers *pts;
int timerid;
struct ptimer *pt;
timerid = SCARG(uap, timerid);
if ((p->p_timers == NULL) ||
(timerid < 2) || (timerid >= TIMER_MAX) ||
((pt = p->p_timers->pts_timers[timerid]) == NULL))
pts = p->p_timers;
if (pts == NULL || timerid < 2 || timerid >= TIMER_MAX)
return (EINVAL);
mutex_spin_enter(&timer_lock);
if ((pt = pts->pts_timers[timerid]) == NULL) {
mutex_spin_exit(&timer_lock);
return (EINVAL);
}
*retval = pt->pt_poverruns;
mutex_spin_exit(&timer_lock);
return (0);
}
@ -897,18 +930,18 @@ realtimerexpire(void *arg)
{
struct timeval now;
struct ptimer *pt;
int s;
pt = (struct ptimer *)arg;
pt = arg;
mutex_spin_enter(&timer_lock);
itimerfire(pt);
if (!timerisset(&pt->pt_time.it_interval)) {
timerclear(&pt->pt_time.it_value);
mutex_spin_exit(&timer_lock);
return;
}
for (;;) {
s = splclock(); /* XXX need spl now? */
timeradd(&pt->pt_time.it_value,
&pt->pt_time.it_interval, &pt->pt_time.it_value);
getmicrotime(&now);
@ -919,11 +952,12 @@ realtimerexpire(void *arg)
*/
callout_reset(&pt->pt_ch, hzto(&pt->pt_time.it_value),
realtimerexpire, pt);
splx(s);
mutex_spin_exit(&timer_lock);
return;
}
splx(s);
mutex_spin_exit(&timer_lock);
pt->pt_overruns++;
mutex_spin_enter(&timer_lock);
}
}
@ -950,19 +984,20 @@ sys_getitimer(struct lwp *l, const struct sys_getitimer_args *uap,
int
dogetitimer(struct proc *p, int which, struct itimerval *itvp)
{
int s;
struct ptimers *pts;
struct ptimer *pt;
if ((u_int)which > ITIMER_PROF)
return (EINVAL);
if ((p->p_timers == NULL) || (p->p_timers->pts_timers[which] == NULL)){
mutex_spin_enter(&timer_lock);
pts = p->p_timers;
if (pts == NULL || (pt = pts->pts_timers[which]) == NULL) {
timerclear(&itvp->it_value);
timerclear(&itvp->it_interval);
} else {
s = splclock();
timer_gettime(p->p_timers->pts_timers[which], itvp);
splx(s);
}
} else
timer_gettime(pt, itvp);
mutex_spin_exit(&timer_lock);
return 0;
}
@ -1007,8 +1042,8 @@ int
dosetitimer(struct proc *p, int which, struct itimerval *itvp)
{
struct timeval now;
struct ptimer *pt;
int s;
struct ptimers *pts;
struct ptimer *pt, *spare;
if (itimerfix(&itvp->it_value) || itimerfix(&itvp->it_interval))
return (EINVAL);
@ -1017,41 +1052,48 @@ dosetitimer(struct proc *p, int which, struct itimerval *itvp)
* Don't bother allocating data structures if the process just
* wants to clear the timer.
*/
if (!timerisset(&itvp->it_value) &&
((p->p_timers == NULL) ||(p->p_timers->pts_timers[which] == NULL)))
spare = NULL;
pts = p->p_timers;
retry:
if (!timerisset(&itvp->it_value) && (pts == NULL ||
pts->pts_timers[which] == NULL))
return (0);
if (p->p_timers == NULL)
timers_alloc(p);
if (p->p_timers->pts_timers[which] == NULL) {
pt = pool_get(&ptimer_pool, PR_WAITOK);
if (pts == NULL)
pts = timers_alloc(p);
mutex_spin_enter(&timer_lock);
pt = pts->pts_timers[which];
if (pt == NULL) {
if (spare == NULL) {
mutex_spin_exit(&timer_lock);
spare = pool_get(&ptimer_pool, PR_WAITOK);
goto retry;
}
pt = spare;
spare = NULL;
pt->pt_ev.sigev_notify = SIGEV_SIGNAL;
pt->pt_ev.sigev_value.sival_int = which;
pt->pt_overruns = 0;
pt->pt_proc = p;
pt->pt_type = which;
pt->pt_entry = which;
pt->pt_active = 0;
pt->pt_queued = false;
callout_init(&pt->pt_ch, CALLOUT_MPSAFE);
switch (which) {
case ITIMER_REAL:
callout_init(&pt->pt_ch, 0);
pt->pt_ev.sigev_signo = SIGALRM;
break;
case ITIMER_VIRTUAL:
pt->pt_active = 0;
pt->pt_ev.sigev_signo = SIGVTALRM;
break;
case ITIMER_PROF:
pt->pt_active = 0;
pt->pt_ev.sigev_signo = SIGPROF;
break;
}
} else
pt = p->p_timers->pts_timers[which];
pts->pts_timers[which] = pt;
}
pt->pt_time = *itvp;
p->p_timers->pts_timers[which] = pt;
s = splclock();
if ((which == ITIMER_REAL) && timerisset(&pt->pt_time.it_value)) {
/* Convert to absolute time */
/* XXX need to wrap in splclock for timecounters case? */
@ -1059,17 +1101,19 @@ dosetitimer(struct proc *p, int which, struct itimerval *itvp)
timeradd(&pt->pt_time.it_value, &now, &pt->pt_time.it_value);
}
timer_settime(pt);
splx(s);
mutex_spin_exit(&timer_lock);
if (spare != NULL)
pool_put(&ptimer_pool, spare);
return (0);
}
/* Utility routines to manage the array of pointers to timers. */
void
struct ptimers *
timers_alloc(struct proc *p)
{
int i;
struct ptimers *pts;
int i;
pts = pool_get(&ptimers_pool, PR_WAITOK);
LIST_INIT(&pts->pts_virtual);
@ -1077,7 +1121,15 @@ timers_alloc(struct proc *p)
for (i = 0; i < TIMER_MAX; i++)
pts->pts_timers[i] = NULL;
pts->pts_fired = 0;
p->p_timers = pts;
mutex_spin_enter(&timer_lock);
if (p->p_timers == NULL) {
p->p_timers = pts;
mutex_spin_exit(&timer_lock);
return pts;
}
mutex_spin_exit(&timer_lock);
pool_put(&ptimers_pool, pts);
return p->p_timers;
}
/*
@ -1090,61 +1142,78 @@ timers_alloc(struct proc *p)
void
timers_free(struct proc *p, int which)
{
int i, s;
struct ptimers *pts;
struct ptimer *pt, *ptn;
struct ptimer *ptn;
struct timeval tv;
int i;
if (p->p_timers) {
pts = p->p_timers;
if (which == TIMERS_ALL)
i = 0;
else {
s = splclock();
timerclear(&tv);
for (ptn = LIST_FIRST(&p->p_timers->pts_virtual);
ptn && ptn != pts->pts_timers[ITIMER_VIRTUAL];
ptn = LIST_NEXT(ptn, pt_list))
timeradd(&tv, &ptn->pt_time.it_value, &tv);
LIST_FIRST(&p->p_timers->pts_virtual) = NULL;
if (ptn) {
timeradd(&tv, &ptn->pt_time.it_value,
&ptn->pt_time.it_value);
LIST_INSERT_HEAD(&p->p_timers->pts_virtual,
ptn, pt_list);
}
if (p->p_timers == NULL)
return;
timerclear(&tv);
for (ptn = LIST_FIRST(&p->p_timers->pts_prof);
ptn && ptn != pts->pts_timers[ITIMER_PROF];
ptn = LIST_NEXT(ptn, pt_list))
timeradd(&tv, &ptn->pt_time.it_value, &tv);
LIST_FIRST(&p->p_timers->pts_prof) = NULL;
if (ptn) {
timeradd(&tv, &ptn->pt_time.it_value,
&ptn->pt_time.it_value);
LIST_INSERT_HEAD(&p->p_timers->pts_prof, ptn,
pt_list);
}
splx(s);
i = 3;
pts = p->p_timers;
mutex_spin_enter(&timer_lock);
if (which == TIMERS_ALL) {
p->p_timers = NULL;
i = 0;
} else {
timerclear(&tv);
for (ptn = LIST_FIRST(&pts->pts_virtual);
ptn && ptn != pts->pts_timers[ITIMER_VIRTUAL];
ptn = LIST_NEXT(ptn, pt_list))
timeradd(&tv, &ptn->pt_time.it_value, &tv);
LIST_FIRST(&pts->pts_virtual) = NULL;
if (ptn) {
timeradd(&tv, &ptn->pt_time.it_value,
&ptn->pt_time.it_value);
LIST_INSERT_HEAD(&pts->pts_virtual, ptn, pt_list);
}
for ( ; i < TIMER_MAX; i++)
if ((pt = pts->pts_timers[i]) != NULL) {
if (pt->pt_type == CLOCK_REALTIME) {
callout_stop(&pt->pt_ch);
callout_destroy(&pt->pt_ch);
}
pts->pts_timers[i] = NULL;
pool_put(&ptimer_pool, pt);
}
if ((pts->pts_timers[0] == NULL) &&
(pts->pts_timers[1] == NULL) &&
(pts->pts_timers[2] == NULL)) {
p->p_timers = NULL;
pool_put(&ptimers_pool, pts);
timerclear(&tv);
for (ptn = LIST_FIRST(&pts->pts_prof);
ptn && ptn != pts->pts_timers[ITIMER_PROF];
ptn = LIST_NEXT(ptn, pt_list))
timeradd(&tv, &ptn->pt_time.it_value, &tv);
LIST_FIRST(&pts->pts_prof) = NULL;
if (ptn) {
timeradd(&tv, &ptn->pt_time.it_value,
&ptn->pt_time.it_value);
LIST_INSERT_HEAD(&pts->pts_prof, ptn, pt_list);
}
i = 3;
}
for ( ; i < TIMER_MAX; i++) {
if (pts->pts_timers[i] != NULL) {
itimerfree(pts, i);
mutex_spin_enter(&timer_lock);
}
}
if (pts->pts_timers[0] == NULL && pts->pts_timers[1] == NULL &&
pts->pts_timers[2] == NULL) {
p->p_timers = NULL;
mutex_spin_exit(&timer_lock);
pool_put(&ptimers_pool, pts);
} else
mutex_spin_exit(&timer_lock);
}
static void
itimerfree(struct ptimers *pts, int index)
{
struct ptimer *pt;
KASSERT(mutex_owned(&timer_lock));
pt = pts->pts_timers[index];
pts->pts_timers[index] = NULL;
if (pt->pt_type == CLOCK_REALTIME) {
mutex_spin_exit(&timer_lock);
callout_halt(&pt->pt_ch);
} else if (pt->pt_queued) {
TAILQ_REMOVE(&timer_queue, pt, pt_chain);
mutex_spin_exit(&timer_lock);
} else
mutex_spin_exit(&timer_lock);
callout_destroy(&pt->pt_ch);
pool_put(&ptimer_pool, pt);
}
/*
@ -1157,11 +1226,13 @@ timers_free(struct proc *p, int which)
* that it is called in a context where the timers
* on which it is operating cannot change in value.
*/
int
static int
itimerdecr(struct ptimer *pt, int usec)
{
struct itimerval *itp;
KASSERT(mutex_owned(&timer_lock));
itp = &pt->pt_time;
if (itp->it_value.tv_usec < usec) {
if (itp->it_value.tv_sec == 0) {
@ -1191,32 +1262,89 @@ expire:
return (0);
}
void
static void
itimerfire(struct ptimer *pt)
{
struct proc *p = pt->pt_proc;
if (pt->pt_ev.sigev_notify == SIGEV_SIGNAL) {
KASSERT(mutex_owned(&timer_lock));
/*
* XXX Can overrun, but we don't do signal queueing yet, anyway.
* XXX Relying on the clock interrupt is stupid.
*/
if (pt->pt_ev.sigev_notify != SIGEV_SIGNAL || pt->pt_queued)
return;
TAILQ_INSERT_TAIL(&timer_queue, pt, pt_chain);
pt->pt_queued = true;
softint_schedule(timer_sih);
}
void
timer_tick(lwp_t *l, bool user)
{
struct ptimers *pts;
struct ptimer *pt;
proc_t *p;
p = l->l_proc;
if (p->p_timers == NULL)
return;
mutex_spin_enter(&timer_lock);
if ((pts = l->l_proc->p_timers) != NULL) {
/*
* No RT signal infrastructure exists at this time;
* just post the signal number and throw away the
* value.
* Run current process's virtual and profile time, as needed.
*/
if (sigismember(&p->p_sigpend.sp_set, pt->pt_ev.sigev_signo))
pt->pt_overruns++;
else {
ksiginfo_t ksi;
KSI_INIT(&ksi);
ksi.ksi_signo = pt->pt_ev.sigev_signo;
ksi.ksi_code = SI_TIMER;
ksi.ksi_value = pt->pt_ev.sigev_value;
pt->pt_poverruns = pt->pt_overruns;
pt->pt_overruns = 0;
mutex_enter(&proclist_mutex);
kpsignal(p, &ksi, NULL);
mutex_exit(&proclist_mutex);
}
if (user && (pt = LIST_FIRST(&pts->pts_virtual)) != NULL)
if (itimerdecr(pt, tick) == 0)
itimerfire(pt);
if ((pt = LIST_FIRST(&pts->pts_prof)) != NULL)
if (itimerdecr(pt, tick) == 0)
itimerfire(pt);
}
mutex_spin_exit(&timer_lock);
}
static void
timer_intr(void *cookie)
{
ksiginfo_t ksi;
struct ptimer *pt;
proc_t *p;
mutex_spin_enter(&timer_lock);
while ((pt = TAILQ_FIRST(&timer_queue)) != NULL) {
TAILQ_REMOVE(&timer_queue, pt, pt_chain);
KASSERT(pt->pt_queued);
pt->pt_queued = false;
if (pt->pt_ev.sigev_notify != SIGEV_SIGNAL)
continue;
p = pt->pt_proc;
if (pt->pt_proc->p_timers == NULL) {
/* Process is dying. */
continue;
}
if (sigismember(&p->p_sigpend.sp_set, pt->pt_ev.sigev_signo)) {
pt->pt_overruns++;
continue;
}
KSI_INIT(&ksi);
ksi.ksi_signo = pt->pt_ev.sigev_signo;
ksi.ksi_code = SI_TIMER;
ksi.ksi_value = pt->pt_ev.sigev_value;
pt->pt_poverruns = pt->pt_overruns;
pt->pt_overruns = 0;
mutex_spin_exit(&timer_lock);
mutex_enter(&proclist_mutex);
kpsignal(p, &ksi, NULL);
mutex_exit(&proclist_mutex);
mutex_spin_enter(&timer_lock);
}
mutex_spin_exit(&timer_lock);
}
/*

View File

@ -1,4 +1,4 @@
$NetBSD: syscalls.master,v 1.196 2008/03/27 17:13:25 ad Exp $
$NetBSD: syscalls.master,v 1.197 2008/04/21 00:13:46 ad Exp $
; @(#)syscalls.master 8.2 (Berkeley) 1/13/94
@ -197,12 +197,12 @@
const gid_t *gidset); }
81 STD MPSAFE { int sys_getpgrp(void); }
82 STD MPSAFE { int sys_setpgid(int pid, int pgid); }
83 STD { int sys_setitimer(int which, \
83 STD MPSAFE { int sys_setitimer(int which, \
const struct itimerval *itv, \
struct itimerval *oitv); }
84 COMPAT_43 MPSAFE { int sys_wait(void); } owait
85 COMPAT_12 MPSAFE { int sys_swapon(const char *name); } oswapon
86 STD { int sys_getitimer(int which, \
86 STD MPSAFE { int sys_getitimer(int which, \
struct itimerval *itv); }
87 COMPAT_43 MPSAFE { int sys_gethostname(char *hostname, u_int len); } \
ogethostname
@ -496,15 +496,15 @@
const struct timespec *tp); }
234 STD MPSAFE { int sys_clock_getres(clockid_t clock_id, \
struct timespec *tp); }
235 STD { int sys_timer_create(clockid_t clock_id, \
235 STD MPSAFE { int sys_timer_create(clockid_t clock_id, \
struct sigevent *evp, timer_t *timerid); }
236 STD { int sys_timer_delete(timer_t timerid); }
237 STD { int sys_timer_settime(timer_t timerid, int flags, \
236 STD MPSAFE { int sys_timer_delete(timer_t timerid); }
237 STD MPSAFE { int sys_timer_settime(timer_t timerid, int flags, \
const struct itimerspec *value, \
struct itimerspec *ovalue); }
238 STD { int sys_timer_gettime(timer_t timerid, struct \
238 STD MPSAFE { int sys_timer_gettime(timer_t timerid, struct \
itimerspec *value); }
239 STD { int sys_timer_getoverrun(timer_t timerid); }
239 STD MPSAFE { int sys_timer_getoverrun(timer_t timerid); }
;
; Syscalls 240-269 are reserved for other IEEE Std1003.1b syscalls
;

View File

@ -1,7 +1,7 @@
/* $NetBSD: timevar.h,v 1.20 2008/01/20 18:09:13 joerg Exp $ */
/* $NetBSD: timevar.h,v 1.21 2008/04/21 00:13:46 ad Exp $ */
/*
* Copyright (c) 2005 The NetBSD Foundation.
* Copyright (c) 2005, 2008 The NetBSD Foundation.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@ -86,7 +86,9 @@ struct ptimer {
int pt_poverruns; /* Overruns associated w/ a delivery */
int pt_type;
int pt_entry;
int pt_queued;
struct proc *pt_proc;
TAILQ_ENTRY(ptimer) pt_chain;
};
#define pt_ch pt_data.pt_ch
@ -155,8 +157,6 @@ int dotimer_settime(int, struct itimerspec *, struct itimerspec *, int,
struct proc *);
int hzto(struct timeval *);
void inittimecounter(void);
int itimerdecr(struct ptimer *, int);
void itimerfire(struct ptimer *);
int itimerfix(struct timeval *);
int itimespecfix(struct timespec *);
int ppsratecheck(struct timeval *, int *, int);
@ -170,14 +170,16 @@ int timer_create1(timer_t *, clockid_t, struct sigevent *, copyin_t,
struct lwp *);
void timer_gettime(struct ptimer *, struct itimerval *);
void timer_settime(struct ptimer *);
void timers_alloc(struct proc *);
struct ptimers *timers_alloc(struct proc *);
void timers_free(struct proc *, int);
void timer_tick(struct lwp *, bool);
int tstohz(struct timespec *);
int tvtohz(struct timeval *);
int inittimeleft(struct timeval *, struct timeval *);
int gettimeleft(struct timeval *, struct timeval *);
void timerupcall(struct lwp *);
void time_init(void);
void time_init2(void);
extern time_t time_second; /* current second in the epoch */
extern time_t time_uptime; /* system uptime in seconds */