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:
parent
160b1e8f05
commit
08b44dd8b9
@ -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;
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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
|
||||
;
|
||||
|
@ -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 */
|
||||
|
Loading…
x
Reference in New Issue
Block a user