Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/kern_time.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/kern_time.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.54 retrieving revision 1.54.2.11 diff -u -p -r1.54 -r1.54.2.11 --- src/sys/kern/kern_time.c 2000/09/19 23:26:25 1.54 +++ src/sys/kern/kern_time.c 2002/02/28 04:14:45 1.54.2.11 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_time.c,v 1.54 2000/09/19 23:26:25 bjh21 Exp $ */ +/* $NetBSD: kern_time.c,v 1.54.2.11 2002/02/28 04:14:45 nathanw Exp $ */ /*- * Copyright (c) 2000 The NetBSD Foundation, Inc. @@ -71,6 +71,9 @@ * @(#)kern_time.c 8.4 (Berkeley) 5/26/95 */ +#include +__KERNEL_RCSID(0, "$NetBSD: kern_time.c,v 1.54.2.11 2002/02/28 04:14:45 nathanw Exp $"); + #include "fs_nfs.h" #include "opt_nfs.h" #include "opt_nfsserver.h" @@ -79,7 +82,11 @@ #include #include #include +#include +#include #include +#include +#include #include #include #include @@ -97,8 +104,10 @@ #include -/* - * Time of day and interval timer support. +static void realtimerupcall(struct lwp *, void *); + + +/* Time of day and interval timer support. * * These routines provide the kernel entry points to get and set * the time-of-day and per-process interval timers. Subroutines @@ -109,8 +118,7 @@ /* This function is used by clock_settime and settimeofday */ int -settime(tv) - struct timeval *tv; +settime(struct timeval *tv) { struct timeval delta; struct cpu_info *ci; @@ -119,11 +127,15 @@ settime(tv) /* WHAT DO WE DO ABOUT PENDING REAL-TIME TIMEOUTS??? */ s = splclock(); 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); + } #ifdef notyet - if ((delta.tv_sec < 86400) && securelevel > 0) + if ((delta.tv_sec < 86400) && securelevel > 0) { + splx(s); return (EPERM); + } #endif time = *tv; (void) spllowersoftclock(); @@ -147,10 +159,7 @@ settime(tv) /* ARGSUSED */ int -sys_clock_gettime(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_clock_gettime(struct lwp *l, void *v, register_t *retval) { struct sys_clock_gettime_args /* { syscallarg(clockid_t) clock_id; @@ -159,21 +168,32 @@ sys_clock_gettime(p, v, retval) clockid_t clock_id; struct timeval atv; struct timespec ats; + int s; 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); - - microtime(&atv); - TIMEVAL_TO_TIMESPEC(&atv,&ats); + } return copyout(&ats, SCARG(uap, tp), sizeof(ats)); } /* ARGSUSED */ int -sys_clock_settime(p, v, retval) - struct proc *p; +sys_clock_settime(l, v, retval) + struct lwp *l; void *v; register_t *retval; { @@ -181,33 +201,45 @@ sys_clock_settime(p, v, retval) syscallarg(clockid_t) clock_id; syscallarg(const struct timespec *) tp; } */ *uap = v; - clockid_t clock_id; - struct timeval atv; - struct timespec ats; + struct proc *p = l->l_proc; int error; if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) return (error); - clock_id = SCARG(uap, clock_id); - if (clock_id != CLOCK_REALTIME) - return (EINVAL); + return (clock_settime1(SCARG(uap, clock_id), SCARG(uap, tp))); +} - if ((error = copyin(SCARG(uap, tp), &ats, sizeof(ats))) != 0) - return (error); - TIMESPEC_TO_TIMEVAL(&atv,&ats); - if ((error = settime(&atv))) +int +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); + 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; } int -sys_clock_getres(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_clock_getres(struct lwp *l, void *v, register_t *retval) { struct sys_clock_getres_args /* { syscallarg(clockid_t) clock_id; @@ -218,25 +250,25 @@ sys_clock_getres(p, v, retval) int error = 0; clock_id = SCARG(uap, clock_id); - if (clock_id != CLOCK_REALTIME) - return (EINVAL); - - if (SCARG(uap, tp)) { + switch (clock_id) { + case CLOCK_REALTIME: + case CLOCK_MONOTONIC: ts.tv_sec = 0; ts.tv_nsec = 1000000000 / hz; + break; + default: + return (EINVAL); + } + if (SCARG(uap, tp)) error = copyout(&ts, SCARG(uap, tp), sizeof(ts)); - } return error; } /* ARGSUSED */ int -sys_nanosleep(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_nanosleep(struct lwp *l, void *v, register_t *retval) { static int nanowait; struct sys_nanosleep_args/* { @@ -254,7 +286,7 @@ sys_nanosleep(p, v, retval) return (error); TIMESPEC_TO_TIMEVAL(&atv,&rqt) - if (itimerfix(&atv)) + if (itimerfix(&atv) || atv.tv_sec > 1000000000) return (EINVAL); s = splclock(); @@ -296,10 +328,7 @@ sys_nanosleep(p, v, retval) /* ARGSUSED */ int -sys_gettimeofday(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_gettimeofday(struct lwp *l, void *v, register_t *retval) { struct sys_gettimeofday_args /* { syscallarg(struct timeval *) tp; @@ -329,37 +358,54 @@ sys_gettimeofday(p, v, retval) /* ARGSUSED */ int -sys_settimeofday(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_settimeofday(struct lwp *l, void *v, register_t *retval) { struct sys_settimeofday_args /* { syscallarg(const struct timeval *) tv; syscallarg(const struct timezone *) tzp; } */ *uap = v; - struct timeval atv; - struct timezone atz; + struct proc *p = l->l_proc; int error; if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 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. */ - if (SCARG(uap, tv) && (error = copyin(SCARG(uap, tv), - &atv, sizeof(atv)))) - return (error); + if (utv) { + if ((error = copyin(utv, &atv, sizeof(atv))) != 0) + return (error); + tv = &atv; + } /* XXX since we don't use tz, probably no point in doing copyin. */ - if (SCARG(uap, tzp) && (error = copyin(SCARG(uap, tzp), - &atz, sizeof(atz)))) - return (error); - if (SCARG(uap, tv)) - if ((error = settime(&atv))) + if (utzp) { + if ((error = copyin(utzp, &atz, sizeof(atz))) != 0) + return (error); + tzp = &atz; + } + + if (tv) + if ((error = settime(tv)) != 0) return (error); /* * NetBSD has no kernel notion of time zone, and only an * 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 " "(obsolete) kernel time zone\n", p->p_pid); return (0); @@ -371,29 +417,43 @@ long bigadj = 1000000; /* use 10x skew /* ARGSUSED */ int -sys_adjtime(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_adjtime(struct lwp *l, void *v, register_t *retval) { struct sys_adjtime_args /* { syscallarg(const struct timeval *) delta; syscallarg(struct timeval *) olddelta; } */ *uap = v; - struct timeval atv; - long ndelta, ntickdelta, odelta; - int s, error; + struct proc *p = l->l_proc; + int error; if ((error = suser(p->p_ucred, &p->p_acflag)) != 0) 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) return (error); - if (SCARG(uap, olddelta) != NULL && - uvm_useracc((caddr_t)SCARG(uap, olddelta), sizeof(struct timeval), - B_WRITE) == FALSE) - return (EFAULT); + + if (olddelta != NULL) { + if (uvm_useracc((caddr_t)olddelta, + sizeof(struct timeval), B_WRITE) == FALSE) + return (EFAULT); + oatv = olddelta; + } /* * Compute the total correction and the rate at which to apply it. @@ -423,162 +483,520 @@ sys_adjtime(p, v, retval) tickdelta = ntickdelta; splx(s); - if (SCARG(uap, olddelta)) { + if (olddelta) { atv.tv_sec = odelta / 1000000; atv.tv_usec = odelta % 1000000; - (void) copyout(&atv, SCARG(uap, olddelta), - sizeof(struct timeval)); + (void) copyout(&atv, olddelta, sizeof(struct timeval)); } return (0); } /* - * Get value of an interval timer. The process virtual and - * profiling virtual time timers are kept in the p_stats area, since - * they can be swapped out. These are kept internally in the - * way they are specified externally: in time until they expire. + * Interval timer support. Both the BSD getitimer() family and the POSIX + * timer_*() family of routines are supported. + * + * 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 - * for the process, and its value (it_value) is kept as an - * absolute time rather than as a delta, so that it is easy to keep - * periodic real-time signals from drifting. - * - * Virtual time timers are processed in the hardclock() routine of - * kern_clock.c. The real time timer is processed by a timeout - * routine, called from the softclock() routine. Since a callout - * may be delayed in real time due to interrupt processing in the system, - * 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 + * Realtime timers are kept in the ptimer structure as an absolute + * time; virtual time timers are kept as deltas. Virtual time timers + * are processed in the hardclock() routine of kern_clock.c. The real + * time timer is processed by a callout routine, called from the + * softclock() routine. Since a callout may be delayed in real time + * due to interrupt processing in the system, it is possible for the + * real time timeout routine (realtimeexpire, 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 - * 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 -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; - void *v; - register_t *retval; + int s; + + 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 /* { syscallarg(int) which; syscallarg(struct itimerval *) itv; } */ *uap = v; - int which = SCARG(uap, which); + struct proc *p = l->l_proc; struct itimerval aitv; - int s; + int s, which; + + which = SCARG(uap, which); if ((u_int)which > ITIMER_PROF) return (EINVAL); - s = splclock(); - if (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. - */ - aitv = p->p_realtimer; - if (timerisset(&aitv.it_value)) { - if (timercmp(&aitv.it_value, &time, <)) - timerclear(&aitv.it_value); - else - timersub(&aitv.it_value, &time, &aitv.it_value); - } - } else - aitv = p->p_stats->p_timer[which]; - splx(s); + + if ((p->p_timers == NULL) || (p->p_timers[which] == NULL)) { + timerclear(&aitv.it_value); + timerclear(&aitv.it_interval); + } else { + s = splclock(); + if (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. + */ + aitv = p->p_timers[ITIMER_REAL]->pt_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); + } + } else + aitv = p->p_timers[which]->pt_time; + splx(s); + } + return (copyout(&aitv, SCARG(uap, itv), sizeof(struct itimerval))); + } +/* BSD routine to set/arm an interval timer. */ /* ARGSUSED */ int -sys_setitimer(p, v, retval) - struct proc *p; - void *v; - register_t *retval; +sys_setitimer(struct lwp *l, void *v, register_t *retval) { struct sys_setitimer_args /* { syscallarg(int) which; syscallarg(const struct itimerval *) itv; syscallarg(struct itimerval *) oitv; } */ *uap = v; + struct proc *p = l->l_proc; int which = SCARG(uap, which); struct sys_getitimer_args getargs; struct itimerval aitv; const struct itimerval *itvp; + struct ptimer *pt; int s, error; if ((u_int)which > ITIMER_PROF) return (EINVAL); 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); if (SCARG(uap, oitv) != NULL) { SCARG(&getargs, which) = which; SCARG(&getargs, itv) = SCARG(uap, oitv); - if ((error = sys_getitimer(p, &getargs, retval)) != 0) + if ((error = sys_getitimer(l, &getargs, retval)) != 0) return (error); } if (itvp == 0) return (0); if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval)) 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) { - callout_stop(&p->p_realit_ch); - if (timerisset(&aitv.it_value)) { + s = splclock(); + 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. * callout_reset() does it for us. */ - timeradd(&aitv.it_value, &time, &aitv.it_value); - callout_reset(&p->p_realit_ch, hzto(&aitv.it_value), - realitexpire, p); + callout_reset(&pt->pt_ch, hzto(&pt->pt_time.it_value), + realtimerexpire, pt); } - p->p_realtimer = aitv; - } else - p->p_stats->p_timer[which] = aitv; - splx(s); + splx(s); + } + return (0); } -/* - * 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. - */ +/* Utility routines to manage the array of pointers to timers. */ void -realitexpire(arg) - void *arg; +timers_alloc(struct proc *p) { - struct proc *p; - int s; + int i; + struct ptimer **pts; - p = (struct proc *)arg; - psignal(p, SIGALRM); - if (!timerisset(&p->p_realtimer.it_interval)) { - timerclear(&p->p_realtimer.it_value); - return; - } - for (;;) { - s = splclock(); - timeradd(&p->p_realtimer.it_value, - &p->p_realtimer.it_interval, &p->p_realtimer.it_value); - if (timercmp(&p->p_realtimer.it_value, &time, >)) { - /* - * Don't need to check hzto() return value, here. - * callout_reset() does it for us. - */ - callout_reset(&p->p_realit_ch, - hzto(&p->p_realtimer.it_value), realitexpire, p); - splx(s); - return; - } - splx(s); + pts = malloc(TIMER_MAX * sizeof(struct timer *), M_SUBPROC, 0); + for (i = 0; i < TIMER_MAX; i++) + pts[i] = NULL; + p->p_timers = pts; +} + +void +timers_free(struct proc *p) +{ + int i; + struct ptimer *pt, **pts; + + if (p->p_timers) { + pts = p->p_timers; + p->p_timers = NULL; + for (i = 0; i < TIMER_MAX; i++) + if ((pt = pts[i]) != NULL) { + if (pt->pt_type == CLOCK_REALTIME) + callout_stop(&pt->pt_ch); + pool_put(&ptimer_pool, pt); + } + free(pts, M_SUBPROC); } } @@ -589,12 +1007,10 @@ realitexpire(arg) * than the resolution of the clock, round it up.) */ int -itimerfix(tv) - struct timeval *tv; +itimerfix(struct timeval *tv) { - if (tv->tv_sec < 0 || tv->tv_sec > 100000000 || - tv->tv_usec < 0 || tv->tv_usec >= 1000000) + if (tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) return (EINVAL); if (tv->tv_sec == 0 && tv->tv_usec != 0 && tv->tv_usec < tick) tv->tv_usec = tick; @@ -612,9 +1028,7 @@ itimerfix(tv) * on which it is operating cannot change in value. */ int -itimerdecr(itp, usec) - struct itimerval *itp; - int usec; +itimerdecr(struct itimerval *itp, int usec) { if (itp->it_value.tv_usec < usec) { @@ -649,9 +1063,7 @@ expire: * for usage and rationale. */ int -ratecheck(lasttime, mininterval) - struct timeval *lasttime; - const struct timeval *mininterval; +ratecheck(struct timeval *lasttime, const struct timeval *mininterval) { struct timeval tv, delta; int s, rv = 0; @@ -679,10 +1091,7 @@ ratecheck(lasttime, mininterval) * ppsratecheck(): packets (or events) per second limitation. */ int -ppsratecheck(lasttime, curpps, maxpps) - struct timeval *lasttime; - int *curpps; - int maxpps; /* maximum pps allowed */ +ppsratecheck(struct timeval *lasttime, int *curpps, int maxpps) { struct timeval tv, delta; int s, rv;