version 1.54, 2000/09/19 23:26:25 |
version 1.54.2.11, 2002/02/28 04:14:45 |
|
|
* @(#)kern_time.c 8.4 (Berkeley) 5/26/95 |
* @(#)kern_time.c 8.4 (Berkeley) 5/26/95 |
*/ |
*/ |
|
|
|
#include <sys/cdefs.h> |
|
__KERNEL_RCSID(0, "$NetBSD$"); |
|
|
#include "fs_nfs.h" |
#include "fs_nfs.h" |
#include "opt_nfs.h" |
#include "opt_nfs.h" |
#include "opt_nfsserver.h" |
#include "opt_nfsserver.h" |
|
|
#include <sys/resourcevar.h> |
#include <sys/resourcevar.h> |
#include <sys/kernel.h> |
#include <sys/kernel.h> |
#include <sys/systm.h> |
#include <sys/systm.h> |
|
#include <sys/lwp.h> |
|
#include <sys/malloc.h> |
#include <sys/proc.h> |
#include <sys/proc.h> |
|
#include <sys/sa.h> |
|
#include <sys/savar.h> |
#include <sys/vnode.h> |
#include <sys/vnode.h> |
#include <sys/signalvar.h> |
#include <sys/signalvar.h> |
#include <sys/syslog.h> |
#include <sys/syslog.h> |
|
|
|
|
#include <machine/cpu.h> |
#include <machine/cpu.h> |
|
|
/* |
static void realtimerupcall(struct lwp *, void *); |
* Time of day and interval timer support. |
|
|
|
|
/* Time of day and interval timer support. |
* |
* |
* These routines provide the kernel entry points to get and set |
* These routines provide the kernel entry points to get and set |
* the time-of-day and per-process interval timers. Subroutines |
* the time-of-day and per-process interval timers. Subroutines |
|
|
|
|
/* This function is used by clock_settime and settimeofday */ |
/* This function is used by clock_settime and settimeofday */ |
int |
int |
settime(tv) |
settime(struct timeval *tv) |
struct timeval *tv; |
|
{ |
{ |
struct timeval delta; |
struct timeval delta; |
struct cpu_info *ci; |
struct cpu_info *ci; |
|
|
/* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */ |
/* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */ |
s = splclock(); |
s = splclock(); |
timersub(tv, &time, &delta); |
timersub(tv, &time, &delta); |
if ((delta.tv_sec < 0 || delta.tv_usec < 0) && securelevel > 1) |
if ((delta.tv_sec < 0 || delta.tv_usec < 0) && securelevel > 1) { |
|
splx(s); |
return (EPERM); |
return (EPERM); |
|
} |
#ifdef notyet |
#ifdef notyet |
if ((delta.tv_sec < 86400) && securelevel > 0) |
if ((delta.tv_sec < 86400) && securelevel > 0) { |
|
splx(s); |
return (EPERM); |
return (EPERM); |
|
} |
#endif |
#endif |
time = *tv; |
time = *tv; |
(void) spllowersoftclock(); |
(void) spllowersoftclock(); |
|
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_clock_gettime(p, v, retval) |
sys_clock_gettime(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_clock_gettime_args /* { |
struct sys_clock_gettime_args /* { |
syscallarg(clockid_t) clock_id; |
syscallarg(clockid_t) clock_id; |
Line 159 sys_clock_gettime(p, v, retval) |
|
Line 168 sys_clock_gettime(p, v, retval) |
|
clockid_t clock_id; |
clockid_t clock_id; |
struct timeval atv; |
struct timeval atv; |
struct timespec ats; |
struct timespec ats; |
|
int s; |
|
|
clock_id = SCARG(uap, clock_id); |
clock_id = SCARG(uap, clock_id); |
if (clock_id != CLOCK_REALTIME) |
switch (clock_id) { |
|
case CLOCK_REALTIME: |
|
microtime(&atv); |
|
TIMEVAL_TO_TIMESPEC(&atv,&ats); |
|
break; |
|
case CLOCK_MONOTONIC: |
|
/* XXX "hz" granularity */ |
|
s = splclock(); |
|
atv = mono_time; |
|
splx(s); |
|
TIMEVAL_TO_TIMESPEC(&atv,&ats); |
|
break; |
|
default: |
return (EINVAL); |
return (EINVAL); |
|
} |
microtime(&atv); |
|
TIMEVAL_TO_TIMESPEC(&atv,&ats); |
|
|
|
return copyout(&ats, SCARG(uap, tp), sizeof(ats)); |
return copyout(&ats, SCARG(uap, tp), sizeof(ats)); |
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_clock_settime(p, v, retval) |
sys_clock_settime(l, v, retval) |
struct proc *p; |
struct lwp *l; |
void *v; |
void *v; |
register_t *retval; |
register_t *retval; |
{ |
{ |
Line 181 sys_clock_settime(p, v, retval) |
|
Line 201 sys_clock_settime(p, v, retval) |
|
syscallarg(clockid_t) clock_id; |
syscallarg(clockid_t) clock_id; |
syscallarg(const struct timespec *) tp; |
syscallarg(const struct timespec *) tp; |
} */ *uap = v; |
} */ *uap = v; |
clockid_t clock_id; |
struct proc *p = l->l_proc; |
struct timeval atv; |
|
struct timespec ats; |
|
int error; |
int error; |
|
|
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
return (error); |
return (error); |
|
|
clock_id = SCARG(uap, clock_id); |
return (clock_settime1(SCARG(uap, clock_id), SCARG(uap, tp))); |
if (clock_id != CLOCK_REALTIME) |
} |
return (EINVAL); |
|
|
|
if ((error = copyin(SCARG(uap, tp), &ats, sizeof(ats))) != 0) |
|
return (error); |
|
|
|
TIMESPEC_TO_TIMEVAL(&atv,&ats); |
int |
if ((error = settime(&atv))) |
clock_settime1(clock_id, tp) |
|
clockid_t clock_id; |
|
const struct timespec *tp; |
|
{ |
|
struct timespec ats; |
|
struct timeval atv; |
|
int error; |
|
|
|
if ((error = copyin(tp, &ats, sizeof(ats))) != 0) |
return (error); |
return (error); |
|
|
|
switch (clock_id) { |
|
case CLOCK_REALTIME: |
|
TIMESPEC_TO_TIMEVAL(&atv, &ats); |
|
if ((error = settime(&atv)) != 0) |
|
return (error); |
|
break; |
|
case CLOCK_MONOTONIC: |
|
return (EINVAL); /* read-only clock */ |
|
default: |
|
return (EINVAL); |
|
} |
|
|
return 0; |
return 0; |
} |
} |
|
|
int |
int |
sys_clock_getres(p, v, retval) |
sys_clock_getres(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_clock_getres_args /* { |
struct sys_clock_getres_args /* { |
syscallarg(clockid_t) clock_id; |
syscallarg(clockid_t) clock_id; |
Line 218 sys_clock_getres(p, v, retval) |
|
Line 250 sys_clock_getres(p, v, retval) |
|
int error = 0; |
int error = 0; |
|
|
clock_id = SCARG(uap, clock_id); |
clock_id = SCARG(uap, clock_id); |
if (clock_id != CLOCK_REALTIME) |
switch (clock_id) { |
return (EINVAL); |
case CLOCK_REALTIME: |
|
case CLOCK_MONOTONIC: |
if (SCARG(uap, tp)) { |
|
ts.tv_sec = 0; |
ts.tv_sec = 0; |
ts.tv_nsec = 1000000000 / hz; |
ts.tv_nsec = 1000000000 / hz; |
|
break; |
|
default: |
|
return (EINVAL); |
|
} |
|
|
|
if (SCARG(uap, tp)) |
error = copyout(&ts, SCARG(uap, tp), sizeof(ts)); |
error = copyout(&ts, SCARG(uap, tp), sizeof(ts)); |
} |
|
|
|
return error; |
return error; |
} |
} |
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_nanosleep(p, v, retval) |
sys_nanosleep(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
static int nanowait; |
static int nanowait; |
struct sys_nanosleep_args/* { |
struct sys_nanosleep_args/* { |
Line 254 sys_nanosleep(p, v, retval) |
|
Line 286 sys_nanosleep(p, v, retval) |
|
return (error); |
return (error); |
|
|
TIMESPEC_TO_TIMEVAL(&atv,&rqt) |
TIMESPEC_TO_TIMEVAL(&atv,&rqt) |
if (itimerfix(&atv)) |
if (itimerfix(&atv) || atv.tv_sec > 1000000000) |
return (EINVAL); |
return (EINVAL); |
|
|
s = splclock(); |
s = splclock(); |
Line 296 sys_nanosleep(p, v, retval) |
|
Line 328 sys_nanosleep(p, v, retval) |
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_gettimeofday(p, v, retval) |
sys_gettimeofday(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_gettimeofday_args /* { |
struct sys_gettimeofday_args /* { |
syscallarg(struct timeval *) tp; |
syscallarg(struct timeval *) tp; |
Line 329 sys_gettimeofday(p, v, retval) |
|
Line 358 sys_gettimeofday(p, v, retval) |
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_settimeofday(p, v, retval) |
sys_settimeofday(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_settimeofday_args /* { |
struct sys_settimeofday_args /* { |
syscallarg(const struct timeval *) tv; |
syscallarg(const struct timeval *) tv; |
syscallarg(const struct timezone *) tzp; |
syscallarg(const struct timezone *) tzp; |
} */ *uap = v; |
} */ *uap = v; |
struct timeval atv; |
struct proc *p = l->l_proc; |
struct timezone atz; |
|
int error; |
int error; |
|
|
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
return (error); |
return (error); |
|
|
|
return settimeofday1(SCARG(uap, tv), SCARG(uap, tzp), p); |
|
} |
|
|
|
int |
|
settimeofday1(utv, utzp, p) |
|
const struct timeval *utv; |
|
const struct timezone *utzp; |
|
struct proc *p; |
|
{ |
|
struct timeval atv; |
|
struct timezone atz; |
|
struct timeval *tv = NULL; |
|
struct timezone *tzp = NULL; |
|
int error; |
|
|
/* Verify all parameters before changing time. */ |
/* Verify all parameters before changing time. */ |
if (SCARG(uap, tv) && (error = copyin(SCARG(uap, tv), |
if (utv) { |
&atv, sizeof(atv)))) |
if ((error = copyin(utv, &atv, sizeof(atv))) != 0) |
return (error); |
return (error); |
|
tv = &atv; |
|
} |
/* XXX since we don't use tz, probably no point in doing copyin. */ |
/* XXX since we don't use tz, probably no point in doing copyin. */ |
if (SCARG(uap, tzp) && (error = copyin(SCARG(uap, tzp), |
if (utzp) { |
&atz, sizeof(atz)))) |
if ((error = copyin(utzp, &atz, sizeof(atz))) != 0) |
return (error); |
return (error); |
if (SCARG(uap, tv)) |
tzp = &atz; |
if ((error = settime(&atv))) |
} |
|
|
|
if (tv) |
|
if ((error = settime(tv)) != 0) |
return (error); |
return (error); |
/* |
/* |
* NetBSD has no kernel notion of time zone, and only an |
* NetBSD has no kernel notion of time zone, and only an |
* obsolete program would try to set it, so we log a warning. |
* obsolete program would try to set it, so we log a warning. |
*/ |
*/ |
if (SCARG(uap, tzp)) |
if (tzp) |
log(LOG_WARNING, "pid %d attempted to set the " |
log(LOG_WARNING, "pid %d attempted to set the " |
"(obsolete) kernel time zone\n", p->p_pid); |
"(obsolete) kernel time zone\n", p->p_pid); |
return (0); |
return (0); |
Line 371 long bigadj = 1000000; /* use 10x skew |
|
Line 417 long bigadj = 1000000; /* use 10x skew |
|
|
|
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_adjtime(p, v, retval) |
sys_adjtime(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_adjtime_args /* { |
struct sys_adjtime_args /* { |
syscallarg(const struct timeval *) delta; |
syscallarg(const struct timeval *) delta; |
syscallarg(struct timeval *) olddelta; |
syscallarg(struct timeval *) olddelta; |
} */ *uap = v; |
} */ *uap = v; |
struct timeval atv; |
struct proc *p = l->l_proc; |
long ndelta, ntickdelta, odelta; |
int error; |
int s, error; |
|
|
|
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) |
return (error); |
return (error); |
|
|
error = copyin(SCARG(uap, delta), &atv, sizeof(struct timeval)); |
return adjtime1(SCARG(uap, delta), SCARG(uap, olddelta), p); |
|
} |
|
|
|
int |
|
adjtime1(delta, olddelta, p) |
|
const struct timeval *delta; |
|
struct timeval *olddelta; |
|
struct proc *p; |
|
{ |
|
struct timeval atv; |
|
struct timeval *oatv = NULL; |
|
long ndelta, ntickdelta, odelta; |
|
int error; |
|
int s; |
|
|
|
error = copyin(delta, &atv, sizeof(struct timeval)); |
if (error) |
if (error) |
return (error); |
return (error); |
if (SCARG(uap, olddelta) != NULL && |
|
uvm_useracc((caddr_t)SCARG(uap, olddelta), sizeof(struct timeval), |
if (olddelta != NULL) { |
B_WRITE) == FALSE) |
if (uvm_useracc((caddr_t)olddelta, |
return (EFAULT); |
sizeof(struct timeval), B_WRITE) == FALSE) |
|
return (EFAULT); |
|
oatv = olddelta; |
|
} |
|
|
/* |
/* |
* Compute the total correction and the rate at which to apply it. |
* Compute the total correction and the rate at which to apply it. |
Line 423 sys_adjtime(p, v, retval) |
|
Line 483 sys_adjtime(p, v, retval) |
|
tickdelta = ntickdelta; |
tickdelta = ntickdelta; |
splx(s); |
splx(s); |
|
|
if (SCARG(uap, olddelta)) { |
if (olddelta) { |
atv.tv_sec = odelta / 1000000; |
atv.tv_sec = odelta / 1000000; |
atv.tv_usec = odelta % 1000000; |
atv.tv_usec = odelta % 1000000; |
(void) copyout(&atv, SCARG(uap, olddelta), |
(void) copyout(&atv, olddelta, sizeof(struct timeval)); |
sizeof(struct timeval)); |
|
} |
} |
return (0); |
return (0); |
} |
} |
|
|
/* |
/* |
* Get value of an interval timer. The process virtual and |
* Interval timer support. Both the BSD getitimer() family and the POSIX |
* profiling virtual time timers are kept in the p_stats area, since |
* timer_*() family of routines are supported. |
* they can be swapped out. These are kept internally in the |
* |
* way they are specified externally: in time until they expire. |
* All timers are kept in an array pointed to by p_timers, which is |
|
* allocated on demand - many processes don't use timers at all. The |
|
* first three elements in this array are reserved for the BSD timers: |
|
* element 0 is ITIMER_REAL, element 1 is ITIMER_VIRTUAL, and element |
|
* 2 is ITIMER_PROF. The rest may be allocated by the timer_create() |
|
* syscall. |
* |
* |
* The real time interval timer is kept in the process table slot |
* Realtime timers are kept in the ptimer structure as an absolute |
* for the process, and its value (it_value) is kept as an |
* time; virtual time timers are kept as deltas. Virtual time timers |
* absolute time rather than as a delta, so that it is easy to keep |
* are processed in the hardclock() routine of kern_clock.c. The real |
* periodic real-time signals from drifting. |
* time timer is processed by a callout routine, called from the |
* |
* softclock() routine. Since a callout may be delayed in real time |
* Virtual time timers are processed in the hardclock() routine of |
* due to interrupt processing in the system, it is possible for the |
* kern_clock.c. The real time timer is processed by a timeout |
* real time timeout routine (realtimeexpire, given below), to be |
* routine, called from the softclock() routine. Since a callout |
* delayed in real time past when it is supposed to occur. It does |
* may be delayed in real time due to interrupt processing in the system, |
* not suffice, therefore, to reload the real timer .it_value from the |
* it is possible for the real time timeout routine (realitexpire, given below), |
|
* to be delayed in real time past when it is supposed to occur. It |
|
* does not suffice, therefore, to reload the real timer .it_value from the |
|
* real time timers .it_interval. Rather, we compute the next time in |
* real time timers .it_interval. Rather, we compute the next time in |
* absolute time the timer should go off. |
* absolute time the timer should go off. |
*/ |
*/ |
/* ARGSUSED */ |
|
|
/* Allocate a POSIX realtime timer. */ |
|
int |
|
sys_timer_create(struct lwp *l, void *v, register_t *retval) |
|
{ |
|
struct sys_timer_create_args /* { |
|
syscallarg(clockid_t) clock_id; |
|
syscallarg(struct sigevent *) evp; |
|
syscallarg(timer_t *) timerid; |
|
} */ *uap = v; |
|
struct proc *p = l->l_proc; |
|
clockid_t id; |
|
struct sigevent *evp; |
|
struct ptimer *pt; |
|
int timerid, error; |
|
|
|
id = SCARG(uap, clock_id); |
|
if (id != CLOCK_REALTIME) |
|
return (EINVAL); |
|
|
|
if (p->p_timers == NULL) |
|
timers_alloc(p); |
|
|
|
for (timerid = 3; timerid < TIMER_MAX; timerid++) |
|
if (p->p_timers[timerid] == NULL) |
|
break; |
|
|
|
if (timerid == TIMER_MAX) |
|
return EAGAIN; |
|
|
|
pt = pool_get(&ptimer_pool, PR_WAITOK); |
|
evp = SCARG(uap, evp); |
|
if (evp) { |
|
if (((error = |
|
copyin(evp, &pt->pt_ev, sizeof (pt->pt_ev))) != 0) || |
|
((pt->pt_ev.sigev_notify < SIGEV_NONE) || |
|
(pt->pt_ev.sigev_notify > SIGEV_SA))) { |
|
pool_put(&ptimer_pool, pt); |
|
return (error ? error : EINVAL); |
|
} |
|
} else { |
|
pt->pt_ev.sigev_notify = SIGEV_SIGNAL; |
|
pt->pt_ev.sigev_signo = SIGALRM; |
|
pt->pt_ev.sigev_value.sival_int = timerid; |
|
} |
|
pt->pt_info.si_signo = pt->pt_ev.sigev_signo; |
|
pt->pt_info.si_errno = 0; |
|
pt->pt_info.si_code = 0; |
|
pt->pt_info.si_pid = p->p_pid; |
|
pt->pt_info.si_uid = p->p_cred->p_ruid; |
|
pt->pt_info.si_addr = NULL; |
|
pt->pt_info.si_status = 0; |
|
pt->pt_info.si_value = pt->pt_ev.sigev_value; |
|
|
|
callout_init(&pt->pt_ch); |
|
pt->pt_type = CLOCK_REALTIME; |
|
pt->pt_proc = p; |
|
pt->pt_overruns = 0; |
|
|
|
p->p_timers[timerid] = pt; |
|
|
|
return copyout(&timerid, SCARG(uap, timerid), sizeof(timerid)); |
|
} |
|
|
|
|
|
/* Delete a POSIX realtime timer */ |
|
int |
|
sys_timer_delete(struct lwp *l, void *v, register_t *retval) |
|
{ |
|
struct sys_timer_delete_args /* { |
|
syscallarg(timer_t) timerid; |
|
} */ *uap = v; |
|
struct proc *p = l->l_proc; |
|
int timerid; |
|
struct ptimer *pt; |
|
|
|
timerid = SCARG(uap, timerid); |
|
|
|
if ((p->p_timers == NULL) || |
|
(timerid < 2) || (timerid >= TIMER_MAX) || |
|
((pt = p->p_timers[timerid]) == NULL)) |
|
return (EINVAL); |
|
|
|
callout_stop(&pt->pt_ch); |
|
p->p_timers[timerid] = NULL; |
|
pool_put(&ptimer_pool, pt); |
|
|
|
return (0); |
|
} |
|
|
|
/* Set and arm a POSIX realtime timer */ |
|
int |
|
sys_timer_settime(struct lwp *l, void *v, register_t *retval) |
|
{ |
|
struct sys_timer_settime_args /* { |
|
syscallarg(timer_t) timerid; |
|
syscallarg(int) flags; |
|
syscallarg(const struct itimerspec *) value; |
|
syscallarg(struct itimerspec *) ovalue; |
|
} */ *uap = v; |
|
struct proc *p = l->l_proc; |
|
int error, s, timerid; |
|
struct itimerval val, oval; |
|
struct itimerspec value, ovalue; |
|
struct ptimer *pt; |
|
|
|
timerid = SCARG(uap, timerid); |
|
|
|
if ((p->p_timers == NULL) || |
|
(timerid < 2) || (timerid >= TIMER_MAX) || |
|
((pt = p->p_timers[timerid]) == NULL)) |
|
return (EINVAL); |
|
|
|
if ((error = copyin(SCARG(uap, value), &value, |
|
sizeof(struct itimerspec))) != 0) |
|
return (error); |
|
|
|
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); |
|
|
|
oval = pt->pt_time; |
|
pt->pt_time = val; |
|
|
|
s = splclock(); |
|
callout_stop(&pt->pt_ch); |
|
if (timerisset(&pt->pt_time.it_value)) { |
|
if ((SCARG(uap, flags) & TIMER_ABSTIME) == 0) |
|
timeradd(&pt->pt_time.it_value, &time, |
|
&pt->pt_time.it_value); |
|
/* |
|
* Don't need to check hzto() return value, here. |
|
* callout_reset() does it for us. |
|
*/ |
|
callout_reset(&pt->pt_ch, hzto(&pt->pt_time.it_value), |
|
realtimerexpire, pt); |
|
} |
|
splx(s); |
|
|
|
if (SCARG(uap, ovalue)) { |
|
TIMEVAL_TO_TIMESPEC(&oval.it_value, &ovalue.it_value); |
|
TIMEVAL_TO_TIMESPEC(&oval.it_interval, &ovalue.it_interval); |
|
return copyout(&ovalue, SCARG(uap, ovalue), |
|
sizeof(struct itimerspec)); |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
/* Return the time remaining until a POSIX timer fires. */ |
int |
int |
sys_getitimer(p, v, retval) |
sys_timer_gettime(struct lwp *l, void *v, register_t *retval) |
|
{ |
|
struct sys_timer_gettime_args /* { |
|
syscallarg(timer_t) timerid; |
|
syscallarg(struct itimerspec *) value; |
|
} */ *uap = v; |
|
struct itimerval aitv; |
|
struct itimerspec its; |
|
struct proc *p = l->l_proc; |
|
int timerid; |
|
struct ptimer *pt; |
|
|
|
timerid = SCARG(uap, timerid); |
|
|
|
if ((p->p_timers == NULL) || |
|
(timerid < 2) || (timerid >= TIMER_MAX) || |
|
((pt = p->p_timers[timerid]) == NULL)) |
|
return (EINVAL); |
|
|
|
aitv = pt->pt_time; |
|
|
|
/* |
|
* Real-time timers are kept in absolute time, but this interface |
|
* is supposed to return a relative time. |
|
*/ |
|
if (timerisset(&aitv.it_value)) { |
|
if (timercmp(&aitv.it_value, &time, <)) |
|
timerclear(&aitv.it_value); |
|
else |
|
timersub(&aitv.it_value, &time, &aitv.it_value); |
|
} |
|
|
|
TIMEVAL_TO_TIMESPEC(&aitv.it_interval, &its.it_interval); |
|
TIMEVAL_TO_TIMESPEC(&aitv.it_value, &its.it_value); |
|
|
|
return copyout(&its, SCARG(uap, value), sizeof(its)); |
|
} |
|
|
|
/* |
|
* Return the count of the number of times a periodic timer expired |
|
* while a notification was already pending. The counter is reset when |
|
* a timer expires and a notification can be posted. |
|
*/ |
|
int |
|
sys_timer_getoverrun(struct lwp *l, void *v, register_t *retval) |
|
{ |
|
struct sys_timer_getoverrun_args /* { |
|
syscallarg(timer_t) timerid; |
|
} */ *uap = v; |
|
struct proc *p = l->l_proc; |
|
int timerid; |
|
struct ptimer *pt; |
|
|
|
timerid = SCARG(uap, timerid); |
|
|
|
if ((p->p_timers == NULL) || |
|
(timerid < 2) || (timerid >= TIMER_MAX) || |
|
((pt = p->p_timers[timerid]) == NULL)) |
|
return (EINVAL); |
|
|
|
*retval = pt->pt_overruns; |
|
|
|
return (0); |
|
} |
|
|
|
/* Glue function that triggers an upcall; called from userret(). */ |
|
static void |
|
realtimerupcall(struct lwp *l, void *arg) |
|
{ |
|
struct ptimer *pt; |
|
|
|
pt = (struct ptimer *)arg; |
|
sa_upcall(l, SA_UPCALL_SIGEV, NULL, l, sizeof(siginfo_t), |
|
&pt->pt_info); |
|
|
|
/* The upcall should only be generated once. */ |
|
l->l_proc->p_userret = NULL; |
|
} |
|
|
|
|
|
/* |
|
* Real interval timer expired: |
|
* send process whose timer expired an alarm signal. |
|
* If time is not set up to reload, then just return. |
|
* Else compute next time timer should go off which is > current time. |
|
* This is where delay in processing this timeout causes multiple |
|
* SIGALRM calls to be compressed into one. |
|
*/ |
|
void |
|
realtimerexpire(void *arg) |
|
{ |
|
struct ptimer *pt; |
struct proc *p; |
struct proc *p; |
void *v; |
int s; |
register_t *retval; |
|
|
pt = (struct ptimer *)arg; |
|
p = pt->pt_proc; |
|
if (pt->pt_ev.sigev_notify == SIGEV_SIGNAL) { |
|
/* |
|
* No RT signal infrastructure exists at this time; |
|
* just post the signal number and throw away the |
|
* value. |
|
*/ |
|
if (sigismember(&p->p_sigctx.ps_siglist, pt->pt_ev.sigev_signo)) |
|
pt->pt_overruns++; |
|
else { |
|
pt->pt_overruns = 0; |
|
psignal(p, pt->pt_ev.sigev_signo); |
|
} |
|
} else if (pt->pt_ev.sigev_notify == SIGEV_SA && (p->p_flag & P_SA)) { |
|
int notified = 0; |
|
/* Cause the process to generate an upcall when it returns. */ |
|
|
|
if (p->p_nrlwps == 0) { |
|
struct sadata_upcall *sd; |
|
struct lwp *l2; |
|
int s, ret; |
|
|
|
SCHED_LOCK(s); |
|
l2 = sa_getcachelwp(p); |
|
if (l2 != NULL) { |
|
sd = sadata_upcall_alloc(0); |
|
cpu_setfunc(l2, sa_switchcall, NULL); |
|
ret = sa_upcall0(l2, SA_UPCALL_SIGEV, |
|
NULL, NULL, sizeof(siginfo_t), |
|
&pt->pt_info, sd); |
|
if (ret == 0) { |
|
p->p_nrlwps++; |
|
l2->l_priority = l2->l_usrpri; |
|
PRELE(l2); |
|
setrunnable(l2); |
|
notified = 1; |
|
} else |
|
sa_putcachelwp(p, l2); |
|
} |
|
SCHED_UNLOCK(s); |
|
} else if (p->p_userret == NULL) { |
|
pt->pt_overruns = 0; |
|
p->p_userret = realtimerupcall; |
|
p->p_userret_arg = pt; |
|
notified = 1; |
|
} |
|
if (notified == 0) |
|
pt->pt_overruns++; |
|
} |
|
if (!timerisset(&pt->pt_time.it_interval)) { |
|
timerclear(&pt->pt_time.it_value); |
|
return; |
|
} |
|
for (;;) { |
|
s = splclock(); |
|
timeradd(&pt->pt_time.it_value, |
|
&pt->pt_time.it_interval, &pt->pt_time.it_value); |
|
if (timercmp(&pt->pt_time.it_value, &time, >)) { |
|
/* |
|
* Don't need to check hzto() return value, here. |
|
* callout_reset() does it for us. |
|
*/ |
|
callout_reset(&pt->pt_ch, hzto(&pt->pt_time.it_value), |
|
realtimerexpire, pt); |
|
splx(s); |
|
return; |
|
} |
|
splx(s); |
|
pt->pt_overruns++; |
|
} |
|
} |
|
|
|
/* BSD routine to get the value of an interval timer. */ |
|
/* ARGSUSED */ |
|
int |
|
sys_getitimer(struct lwp *l, void *v, register_t *retval) |
{ |
{ |
struct sys_getitimer_args /* { |
struct sys_getitimer_args /* { |
syscallarg(int) which; |
syscallarg(int) which; |
syscallarg(struct itimerval *) itv; |
syscallarg(struct itimerval *) itv; |
} */ *uap = v; |
} */ *uap = v; |
int which = SCARG(uap, which); |
struct proc *p = l->l_proc; |
struct itimerval aitv; |
struct itimerval aitv; |
int s; |
int s, which; |
|
|
|
which = SCARG(uap, which); |
|
|
if ((u_int)which > ITIMER_PROF) |
if ((u_int)which > ITIMER_PROF) |
return (EINVAL); |
return (EINVAL); |
s = splclock(); |
|
if (which == ITIMER_REAL) { |
if ((p->p_timers == NULL) || (p->p_timers[which] == NULL)) { |
/* |
timerclear(&aitv.it_value); |
* Convert from absolute to relative time in .it_value |
timerclear(&aitv.it_interval); |
* part of real time timer. If time for real time timer |
} else { |
* has passed return 0, else return difference between |
s = splclock(); |
* current time and time for the timer to go off. |
if (which == ITIMER_REAL) { |
*/ |
/* |
aitv = p->p_realtimer; |
* Convert from absolute to relative time in |
if (timerisset(&aitv.it_value)) { |
* .it_value part of real time timer. If time |
if (timercmp(&aitv.it_value, &time, <)) |
* for real time timer has passed return 0, |
timerclear(&aitv.it_value); |
* else return difference between current time |
else |
* and time for the timer to go off. |
timersub(&aitv.it_value, &time, &aitv.it_value); |
*/ |
} |
aitv = p->p_timers[ITIMER_REAL]->pt_time; |
} else |
if (timerisset(&aitv.it_value)) { |
aitv = p->p_stats->p_timer[which]; |
if (timercmp(&aitv.it_value, &time, <)) |
splx(s); |
timerclear(&aitv.it_value); |
|
else |
|
timersub(&aitv.it_value, &time, &aitv.it_value); |
|
} |
|
} else |
|
aitv = p->p_timers[which]->pt_time; |
|
splx(s); |
|
} |
|
|
return (copyout(&aitv, SCARG(uap, itv), sizeof(struct itimerval))); |
return (copyout(&aitv, SCARG(uap, itv), sizeof(struct itimerval))); |
|
|
} |
} |
|
|
|
/* BSD routine to set/arm an interval timer. */ |
/* ARGSUSED */ |
/* ARGSUSED */ |
int |
int |
sys_setitimer(p, v, retval) |
sys_setitimer(struct lwp *l, void *v, register_t *retval) |
struct proc *p; |
|
void *v; |
|
register_t *retval; |
|
{ |
{ |
struct sys_setitimer_args /* { |
struct sys_setitimer_args /* { |
syscallarg(int) which; |
syscallarg(int) which; |
syscallarg(const struct itimerval *) itv; |
syscallarg(const struct itimerval *) itv; |
syscallarg(struct itimerval *) oitv; |
syscallarg(struct itimerval *) oitv; |
} */ *uap = v; |
} */ *uap = v; |
|
struct proc *p = l->l_proc; |
int which = SCARG(uap, which); |
int which = SCARG(uap, which); |
struct sys_getitimer_args getargs; |
struct sys_getitimer_args getargs; |
struct itimerval aitv; |
struct itimerval aitv; |
const struct itimerval *itvp; |
const struct itimerval *itvp; |
|
struct ptimer *pt; |
int s, error; |
int s, error; |
|
|
if ((u_int)which > ITIMER_PROF) |
if ((u_int)which > ITIMER_PROF) |
return (EINVAL); |
return (EINVAL); |
itvp = SCARG(uap, itv); |
itvp = SCARG(uap, itv); |
if (itvp && (error = copyin(itvp, &aitv, sizeof(struct itimerval)))) |
if (itvp && |
|
(error = copyin(itvp, &aitv, sizeof(struct itimerval)) != 0)) |
return (error); |
return (error); |
if (SCARG(uap, oitv) != NULL) { |
if (SCARG(uap, oitv) != NULL) { |
SCARG(&getargs, which) = which; |
SCARG(&getargs, which) = which; |
SCARG(&getargs, itv) = SCARG(uap, oitv); |
SCARG(&getargs, itv) = SCARG(uap, oitv); |
if ((error = sys_getitimer(p, &getargs, retval)) != 0) |
if ((error = sys_getitimer(l, &getargs, retval)) != 0) |
return (error); |
return (error); |
} |
} |
if (itvp == 0) |
if (itvp == 0) |
return (0); |
return (0); |
if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval)) |
if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval)) |
return (EINVAL); |
return (EINVAL); |
s = splclock(); |
|
|
/* |
|
* Don't bother allocating data structures if the process just |
|
* wants to clear the timer. |
|
*/ |
|
if (!timerisset(&aitv.it_value) && |
|
((p->p_timers == NULL) || (p->p_timers[which] == NULL))) |
|
return (0); |
|
|
|
if (p->p_timers == NULL) |
|
timers_alloc(p); |
|
if (p->p_timers[which] == NULL) { |
|
pt = pool_get(&ptimer_pool, PR_WAITOK); |
|
callout_init(&pt->pt_ch); |
|
pt->pt_ev.sigev_notify = SIGEV_SIGNAL; |
|
pt->pt_overruns = 0; |
|
pt->pt_proc = p; |
|
pt->pt_type = which; |
|
switch (which) { |
|
case ITIMER_REAL: |
|
pt->pt_ev.sigev_signo = SIGALRM; |
|
break; |
|
case ITIMER_VIRTUAL: |
|
pt->pt_ev.sigev_signo = SIGVTALRM; |
|
break; |
|
case ITIMER_PROF: |
|
pt->pt_ev.sigev_signo = SIGPROF; |
|
break; |
|
} |
|
} else |
|
pt = p->p_timers[which]; |
|
|
|
pt->pt_time = aitv; |
|
p->p_timers[which] = pt; |
if (which == ITIMER_REAL) { |
if (which == ITIMER_REAL) { |
callout_stop(&p->p_realit_ch); |
s = splclock(); |
if (timerisset(&aitv.it_value)) { |
callout_stop(&pt->pt_ch); |
|
if (timerisset(&pt->pt_time.it_value)) { |
|
timeradd(&pt->pt_time.it_value, &time, |
|
&pt->pt_time.it_value); |
/* |
/* |
* Don't need to check hzto() return value, here. |
* Don't need to check hzto() return value, here. |
* callout_reset() does it for us. |
* callout_reset() does it for us. |
*/ |
*/ |
timeradd(&aitv.it_value, &time, &aitv.it_value); |
callout_reset(&pt->pt_ch, hzto(&pt->pt_time.it_value), |
callout_reset(&p->p_realit_ch, hzto(&aitv.it_value), |
realtimerexpire, pt); |
realitexpire, p); |
|
} |
} |
p->p_realtimer = aitv; |
splx(s); |
} else |
} |
p->p_stats->p_timer[which] = aitv; |
|
splx(s); |
|
return (0); |
return (0); |
} |
} |
|
|
/* |
/* Utility routines to manage the array of pointers to timers. */ |
* Real interval timer expired: |
|
* send process whose timer expired an alarm signal. |
|
* If time is not set up to reload, then just return. |
|
* Else compute next time timer should go off which is > current time. |
|
* This is where delay in processing this timeout causes multiple |
|
* SIGALRM calls to be compressed into one. |
|
*/ |
|
void |
void |
realitexpire(arg) |
timers_alloc(struct proc *p) |
void *arg; |
|
{ |
{ |
struct proc *p; |
int i; |
int s; |
struct ptimer **pts; |
|
|
p = (struct proc *)arg; |
pts = malloc(TIMER_MAX * sizeof(struct timer *), M_SUBPROC, 0); |
psignal(p, SIGALRM); |
for (i = 0; i < TIMER_MAX; i++) |
if (!timerisset(&p->p_realtimer.it_interval)) { |
pts[i] = NULL; |
timerclear(&p->p_realtimer.it_value); |
p->p_timers = pts; |
return; |
} |
} |
|
for (;;) { |
void |
s = splclock(); |
timers_free(struct proc *p) |
timeradd(&p->p_realtimer.it_value, |
{ |
&p->p_realtimer.it_interval, &p->p_realtimer.it_value); |
int i; |
if (timercmp(&p->p_realtimer.it_value, &time, >)) { |
struct ptimer *pt, **pts; |
/* |
|
* Don't need to check hzto() return value, here. |
if (p->p_timers) { |
* callout_reset() does it for us. |
pts = p->p_timers; |
*/ |
p->p_timers = NULL; |
callout_reset(&p->p_realit_ch, |
for (i = 0; i < TIMER_MAX; i++) |
hzto(&p->p_realtimer.it_value), realitexpire, p); |
if ((pt = pts[i]) != NULL) { |
splx(s); |
if (pt->pt_type == CLOCK_REALTIME) |
return; |
callout_stop(&pt->pt_ch); |
} |
pool_put(&ptimer_pool, pt); |
splx(s); |
} |
|
free(pts, M_SUBPROC); |
} |
} |
} |
} |
|
|
Line 589 realitexpire(arg) |
|
Line 1007 realitexpire(arg) |
|
* than the resolution of the clock, round it up.) |
* than the resolution of the clock, round it up.) |
*/ |
*/ |
int |
int |
itimerfix(tv) |
itimerfix(struct timeval *tv) |
struct timeval *tv; |
|
{ |
{ |
|
|
if (tv->tv_sec < 0 || tv->tv_sec > 100000000 || |
if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) |
tv->tv_usec < 0 || tv->tv_usec >= 1000000) |
|
return (EINVAL); |
return (EINVAL); |
if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick) |
if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick) |
tv->tv_usec = tick; |
tv->tv_usec = tick; |
|
|
* on which it is operating cannot change in value. |
* on which it is operating cannot change in value. |
*/ |
*/ |
int |
int |
itimerdecr(itp, usec) |
itimerdecr(struct itimerval *itp, int usec) |
struct itimerval *itp; |
|
int usec; |
|
{ |
{ |
|
|
if (itp->it_value.tv_usec < usec) { |
if (itp->it_value.tv_usec < usec) { |
|
|
* for usage and rationale. |
* for usage and rationale. |
*/ |
*/ |
int |
int |
ratecheck(lasttime, mininterval) |
ratecheck(struct timeval *lasttime, const struct timeval *mininterval) |
struct timeval *lasttime; |
|
const struct timeval *mininterval; |
|
{ |
{ |
struct timeval tv, delta; |
struct timeval tv, delta; |
int s, rv = 0; |
int s, rv = 0; |
Line 679 ratecheck(lasttime, mininterval) |
|
Line 1091 ratecheck(lasttime, mininterval) |
|
* ppsratecheck(): packets (or events) per second limitation. |
* ppsratecheck(): packets (or events) per second limitation. |
*/ |
*/ |
int |
int |
ppsratecheck(lasttime, curpps, maxpps) |
ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps) |
struct timeval *lasttime; |
|
int *curpps; |
|
int maxpps; /* maximum pps allowed */ |
|
{ |
{ |
struct timeval tv, delta; |
struct timeval tv, delta; |
int s, rv; |
int s, rv; |