/FreeBSD/mac_settime/trunk/bin/clear_src.sh |
---|
File deleted |
Property changes: |
Deleted: svn:executable |
-* |
\ No newline at end of property |
/FreeBSD/mac_settime/trunk/bin/update_src.sh |
---|
4,10 → 4,8 |
# copy our version of files to system |
# |
# to update the system run as root |
# ./clear_src.sh && cvsup src_update && ./copy_orig.sh && ./update_src.sh |
# cvsup src_update && ( ./compare_after_update.sh | less ) && ./update_src.sh |
# |
# then run ./get_diffs.sh as user and examine the diffs |
# |
BIN_DIR=`dirname $0` |
BASE_DIR="${BIN_DIR}/.." |
/FreeBSD/mac_settime/trunk/bin/compare_after_update.sh |
---|
0,0 → 1,19 |
#!/bin/sh |
# |
# compare the saved original sources na dsources after update |
# |
BIN_DIR=`dirname $0` |
SRC_DIR=/usr/src |
SAVED_DIR="${BIN_DIR}/../origins" |
echo "================ sys/kern/kern_time.c ============================ " |
diff "${SAVED_DIR}/kern/kern_time.c" "${SRC_DIR}/sys/kern/kern_time.c" |
echo "================ sys/kern/kern_ntptime.c ========================= " |
diff "${SAVED_DIR}/kern/kern_ntptime.c" "${SRC_DIR}/sys/kern/kern_ntptime.c" |
echo "================ contrib/ntp/ntpd/ntpd.c ========================= " |
diff "${SAVED_DIR}/ntp/ntpd.c" "${SRC_DIR}/contrib/ntp/ntpd/ntpd.c" |
Property changes: |
Added: svn:executable |
+* |
\ No newline at end of property |
/FreeBSD/mac_settime/trunk/bin/copy_orig.sh |
---|
4,23 → 4,12 |
# make copy of all standard files in /usr/src |
# which we want to work on to get diffs later |
# |
# to update the system run as root |
# ./clear_src.sh && cvsup src_update && ./copy_orig.sh && ./update_src.sh |
# |
# then run ./get_diffs.sh as user and examine the diffs |
# |
PATCHED_FILES_KERN="sys/kern/kern_time.c sys/kern/kern_ntptime.c" |
PATCHED_FILES_NTP="contrib/ntp/ntpd/ntpd.c" |
BIN_DIR=`dirname $0` |
SRCDIR=/usr/src |
SAVED_DIR="${BIN_DIR}/../origins" |
for f in ${PATCHED_FILES_KERN} ; do |
cp "${SRCDIR}/$f" "${SRCDIR}/$f.orig" |
done |
cp "${SRCDIR}/sys/kern/kern_time.c" "${SAVED_DIR}/kern" |
cp "${SRCDIR}/sys/kern/kern_ntptime.c" "${SAVED_DIR}/kern" |
cp "${SRCDIR}/contrib/ntp/ntpd/ntpd.c" "${SAVED_DIR}/ntp" |
for f in ${PATCHED_FILES_NTP} ; do |
cp "${SRCDIR}/$f" "${SRCDIR}/$f.orig" |
done |
/FreeBSD/mac_settime/trunk/patched/kern/kern_time.c |
---|
30,7 → 30,7 |
*/ |
#include <sys/cdefs.h> |
__FBSDID("$FreeBSD: src/sys/kern/kern_time.c,v 1.116 2005/03/31 22:51:18 jhb Exp $"); |
__FBSDID("$FreeBSD: src/sys/kern/kern_time.c,v 1.123 2005/11/04 09:39:17 davidxu Exp $"); |
#include "opt_mac.h" |
49,6 → 49,7 |
#include <sys/sysent.h> |
#include <sys/proc.h> |
#include <sys/time.h> |
#include <sys/timers.h> |
#include <sys/timetc.h> |
#include <sys/vnode.h> |
55,9 → 56,14 |
#include <vm/vm.h> |
#include <vm/vm_extern.h> |
#define MAX_CLOCKS (CLOCK_MONOTONIC+1) |
int tz_minuteswest; |
int tz_dsttime; |
static struct kclock posix_clocks[MAX_CLOCKS]; |
static uma_zone_t itimer_zone = NULL; |
/* |
* Time of day and interval timer support. |
* |
72,6 → 78,34 |
static void timevalfix(struct timeval *); |
static void no_lease_updatetime(int); |
static void itimer_start(void); |
static int itimer_init(void *, int, int); |
static void itimer_fini(void *, int); |
static void itimer_enter(struct itimer *); |
static void itimer_leave(struct itimer *); |
static struct itimer *itimer_find(struct proc *, timer_t, int); |
static void itimers_alloc(struct proc *); |
static int realtimer_create(struct itimer *); |
static int realtimer_gettime(struct itimer *, struct itimerspec *); |
static int realtimer_settime(struct itimer *, int, |
struct itimerspec *, struct itimerspec *); |
static int realtimer_delete(struct itimer *); |
static void realtimer_clocktime(clockid_t, struct timespec *); |
static void realtimer_expire(void *); |
static void realtimer_event_hook(struct proc *, clockid_t, int event); |
static int kern_timer_create(struct thread *, clockid_t, |
struct sigevent *, timer_t *, timer_t); |
static int kern_timer_delete(struct thread *, timer_t); |
int register_posix_clock(int, struct kclock *); |
void itimer_fire(struct itimer *it); |
int itimespecfix(struct timespec *ts); |
#define CLOCK_CALL(clock, call, arglist) \ |
((*posix_clocks[clock].call) arglist) |
SYSINIT(posix_timer, SI_SUB_P1003_1B, SI_ORDER_FIRST+4, itimer_start, NULL); |
static int cf_usersettime; |
static int cf_jailsettime; |
SYSCTL_INT(_kern, OID_AUTO, usersettime, CTLFLAG_RW, &cf_usersettime, 0, |
164,19 → 198,31 |
clock_gettime(struct thread *td, struct clock_gettime_args *uap) |
{ |
struct timespec ats; |
int error; |
error = kern_clock_gettime(td, uap->clock_id, &ats); |
if (error == 0) |
error = copyout(&ats, uap->tp, sizeof(ats)); |
return (error); |
} |
int |
kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats) |
{ |
struct timeval sys, user; |
struct proc *p; |
p = td->td_proc; |
switch (uap->clock_id) { |
switch (clock_id) { |
case CLOCK_REALTIME: |
nanotime(&ats); |
nanotime(ats); |
break; |
case CLOCK_VIRTUAL: |
PROC_LOCK(p); |
calcru(p, &user, &sys); |
PROC_UNLOCK(p); |
TIMEVAL_TO_TIMESPEC(&user, &ats); |
TIMEVAL_TO_TIMESPEC(&user, ats); |
break; |
case CLOCK_PROF: |
PROC_LOCK(p); |
183,15 → 229,15 |
calcru(p, &user, &sys); |
PROC_UNLOCK(p); |
timevaladd(&user, &sys); |
TIMEVAL_TO_TIMESPEC(&user, &ats); |
TIMEVAL_TO_TIMESPEC(&user, ats); |
break; |
case CLOCK_MONOTONIC: |
nanouptime(&ats); |
nanouptime(ats); |
break; |
default: |
return (EINVAL); |
} |
return (copyout(&ats, uap->tp, sizeof(ats))); |
return (0); |
} |
#ifndef _SYS_SYSPROTO_H_ |
208,10 → 254,20 |
int |
clock_settime(struct thread *td, struct clock_settime_args *uap) |
{ |
struct timeval atv; |
struct timespec ats; |
int error; |
if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0) |
return (error); |
return (kern_clock_settime(td, uap->clock_id, &ats)); |
} |
int |
kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats) |
{ |
struct timeval atv; |
int error; |
#ifdef MAC |
error = mac_check_system_settime(td->td_ucred); |
if (error) |
220,16 → 276,13 |
if (!cf_jailsettime && jailed(td->td_ucred)) |
return (EPERM); |
if (!cf_usersettime && (error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL)) != 0) |
return (error); /* jail is already checked */ |
if (uap->clock_id != CLOCK_REALTIME) |
return (error); /* jail is already checked */ |
if (clock_id != CLOCK_REALTIME) |
return (EINVAL); |
if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0) |
return (error); |
if (ats.tv_nsec < 0 || ats.tv_nsec >= 1000000000) |
if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000) |
return (EINVAL); |
/* XXX Don't convert nsec->usec and back */ |
TIMESPEC_TO_TIMEVAL(&atv, &ats); |
TIMESPEC_TO_TIMEVAL(&atv, ats); |
error = settime(td, &atv); |
return (error); |
} |
245,9 → 298,23 |
clock_getres(struct thread *td, struct clock_getres_args *uap) |
{ |
struct timespec ts; |
int error; |
ts.tv_sec = 0; |
switch (uap->clock_id) { |
if (uap->tp == NULL) |
return (0); |
error = kern_clock_getres(td, uap->clock_id, &ts); |
if (error == 0) |
error = copyout(&ts, uap->tp, sizeof(ts)); |
return (error); |
} |
int |
kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts) |
{ |
ts->tv_sec = 0; |
switch (clock_id) { |
case CLOCK_REALTIME: |
case CLOCK_MONOTONIC: |
/* |
255,19 → 322,17 |
* Rounding up is especially important if rounding down |
* would give 0. Perfect rounding is unimportant. |
*/ |
ts.tv_nsec = 1000000000 / tc_getfrequency() + 1; |
ts->tv_nsec = 1000000000 / tc_getfrequency() + 1; |
break; |
case CLOCK_VIRTUAL: |
case CLOCK_PROF: |
/* Accurately round up here because we can do so cheaply. */ |
ts.tv_nsec = (1000000000 + hz - 1) / hz; |
ts->tv_nsec = (1000000000 + hz - 1) / hz; |
break; |
default: |
return (EINVAL); |
} |
if (uap->tp == NULL) |
return (0); |
return (copyout(&ts, uap->tp, sizeof(ts))); |
return (0); |
} |
static int nanowait; |
410,7 → 475,7 |
int |
kern_settimeofday(struct thread *td, struct timeval *tv, struct timezone *tzp) |
{ |
int error = 0; |
int error; |
#ifdef MAC |
error = mac_check_system_settime(td->td_ucred); |
420,8 → 485,9 |
if (!cf_jailsettime && jailed(td->td_ucred)) |
return (EPERM); |
if (!cf_usersettime && (error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL)) != 0) |
return (error); /* jail is already checked */ |
return (error); /* jail is already checked */ |
else |
error = 0; |
/* Verify all parameters before changing time. */ |
if (tv) { |
if (tv->tv_usec < 0 || tv->tv_usec >= 1000000) |
641,8 → 707,7 |
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; |
789,3 → 854,668 |
return (maxpps < 0 || *curpps < maxpps); |
} |
} |
static void |
itimer_start(void) |
{ |
struct kclock rt_clock = { |
.timer_create = realtimer_create, |
.timer_delete = realtimer_delete, |
.timer_settime = realtimer_settime, |
.timer_gettime = realtimer_gettime, |
.event_hook = realtimer_event_hook |
}; |
itimer_zone = uma_zcreate("itimer", sizeof(struct itimer), |
NULL, NULL, itimer_init, itimer_fini, UMA_ALIGN_PTR, 0); |
register_posix_clock(CLOCK_REALTIME, &rt_clock); |
register_posix_clock(CLOCK_MONOTONIC, &rt_clock); |
} |
int |
register_posix_clock(int clockid, struct kclock *clk) |
{ |
if ((unsigned)clockid >= MAX_CLOCKS) { |
printf("%s: invalid clockid\n", __func__); |
return (0); |
} |
posix_clocks[clockid] = *clk; |
return (1); |
} |
static int |
itimer_init(void *mem, int size, int flags) |
{ |
struct itimer *it; |
it = (struct itimer *)mem; |
mtx_init(&it->it_mtx, "itimer lock", NULL, MTX_DEF); |
return (0); |
} |
static void |
itimer_fini(void *mem, int size) |
{ |
struct itimer *it; |
it = (struct itimer *)mem; |
mtx_destroy(&it->it_mtx); |
} |
static void |
itimer_enter(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
it->it_usecount++; |
} |
static void |
itimer_leave(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
KASSERT(it->it_usecount > 0, ("invalid it_usecount")); |
if (--it->it_usecount == 0 && (it->it_flags & ITF_WANTED) != 0) |
wakeup(it); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_create_args { |
clockid_t clock_id; |
struct sigevent * evp; |
timer_t * timerid; |
}; |
#endif |
int |
timer_create(struct thread *td, struct timer_create_args *uap) |
{ |
struct sigevent *evp1, ev; |
timer_t id; |
int error; |
if (uap->evp != NULL) { |
error = copyin(uap->evp, &ev, sizeof(ev)); |
if (error != 0) |
return (error); |
evp1 = &ev; |
} else |
evp1 = NULL; |
error = kern_timer_create(td, uap->clock_id, evp1, &id, -1); |
if (error == 0) { |
error = copyout(&id, uap->timerid, sizeof(timer_t)); |
if (error != 0) |
kern_timer_delete(td, id); |
} |
return (error); |
} |
static int |
kern_timer_create(struct thread *td, clockid_t clock_id, |
struct sigevent *evp, timer_t *timerid, timer_t preset_id) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
int id; |
int error; |
if (clock_id < 0 || clock_id >= MAX_CLOCKS) |
return (EINVAL); |
if (posix_clocks[clock_id].timer_create == NULL) |
return (EINVAL); |
if (evp != NULL) { |
if (evp->sigev_notify != SIGEV_NONE && |
evp->sigev_notify != SIGEV_SIGNAL && |
evp->sigev_notify != SIGEV_THREAD_ID) |
return (EINVAL); |
if ((evp->sigev_notify == SIGEV_SIGNAL || |
evp->sigev_notify == SIGEV_THREAD_ID) && |
!_SIG_VALID(evp->sigev_signo)) |
return (EINVAL); |
} |
if (p->p_itimers == NULL) |
itimers_alloc(p); |
it = uma_zalloc(itimer_zone, M_WAITOK); |
it->it_flags = 0; |
it->it_usecount = 0; |
it->it_active = 0; |
timespecclear(&it->it_time.it_value); |
timespecclear(&it->it_time.it_interval); |
it->it_overrun = 0; |
it->it_overrun_last = 0; |
it->it_clockid = clock_id; |
it->it_timerid = -1; |
it->it_proc = p; |
ksiginfo_init(&it->it_ksi); |
it->it_ksi.ksi_flags |= KSI_INS | KSI_EXT; |
error = CLOCK_CALL(clock_id, timer_create, (it)); |
if (error != 0) |
goto out; |
PROC_LOCK(p); |
if (preset_id != -1) { |
KASSERT(preset_id >= 0 && preset_id < 3, ("invalid preset_id")); |
id = preset_id; |
if (p->p_itimers->its_timers[id] != NULL) { |
PROC_UNLOCK(p); |
error = 0; |
goto out; |
} |
} else { |
/* |
* Find a free timer slot, skipping those reserved |
* for setitimer(). |
*/ |
for (id = 3; id < TIMER_MAX; id++) |
if (p->p_itimers->its_timers[id] == NULL) |
break; |
if (id == TIMER_MAX) { |
PROC_UNLOCK(p); |
error = EAGAIN; |
goto out; |
} |
} |
it->it_timerid = id; |
p->p_itimers->its_timers[id] = it; |
if (evp != NULL) |
it->it_sigev = *evp; |
else { |
it->it_sigev.sigev_notify = SIGEV_SIGNAL; |
switch (clock_id) { |
default: |
case CLOCK_REALTIME: |
it->it_sigev.sigev_signo = SIGALRM; |
break; |
case CLOCK_VIRTUAL: |
it->it_sigev.sigev_signo = SIGVTALRM; |
break; |
case CLOCK_PROF: |
it->it_sigev.sigev_signo = SIGPROF; |
break; |
} |
it->it_sigev.sigev_value.sival_int = id; |
} |
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || |
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { |
it->it_ksi.ksi_signo = it->it_sigev.sigev_signo; |
it->it_ksi.ksi_code = SI_TIMER; |
it->it_ksi.ksi_value = it->it_sigev.sigev_value; |
it->it_ksi.ksi_timerid = id; |
} |
PROC_UNLOCK(p); |
*timerid = id; |
return (0); |
out: |
ITIMER_LOCK(it); |
CLOCK_CALL(it->it_clockid, timer_delete, (it)); |
ITIMER_UNLOCK(it); |
uma_zfree(itimer_zone, it); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_delete_args { |
timer_t timerid; |
}; |
#endif |
int |
timer_delete(struct thread *td, struct timer_delete_args *uap) |
{ |
return (kern_timer_delete(td, uap->timerid)); |
} |
static struct itimer * |
itimer_find(struct proc *p, timer_t timerid, int include_deleting) |
{ |
struct itimer *it; |
PROC_LOCK_ASSERT(p, MA_OWNED); |
if ((p->p_itimers == NULL) || (timerid >= TIMER_MAX) || |
(it = p->p_itimers->its_timers[timerid]) == NULL) { |
return (NULL); |
} |
ITIMER_LOCK(it); |
if (!include_deleting && (it->it_flags & ITF_DELETING) != 0) { |
ITIMER_UNLOCK(it); |
it = NULL; |
} |
return (it); |
} |
static int |
kern_timer_delete(struct thread *td, timer_t timerid) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
PROC_LOCK(p); |
it = itimer_find(p, timerid, 0); |
if (it == NULL) { |
PROC_UNLOCK(p); |
return (EINVAL); |
} |
PROC_UNLOCK(p); |
it->it_flags |= ITF_DELETING; |
while (it->it_usecount > 0) { |
it->it_flags |= ITF_WANTED; |
msleep(it, &it->it_mtx, PPAUSE, "itimer", 0); |
} |
it->it_flags &= ~ITF_WANTED; |
CLOCK_CALL(it->it_clockid, timer_delete, (it)); |
ITIMER_UNLOCK(it); |
PROC_LOCK(p); |
if (KSI_ONQ(&it->it_ksi)) |
sigqueue_take(&it->it_ksi); |
p->p_itimers->its_timers[timerid] = NULL; |
PROC_UNLOCK(p); |
uma_zfree(itimer_zone, it); |
return (0); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_settime_args { |
timer_t timerid; |
int flags; |
const struct itimerspec * value; |
struct itimerspec * ovalue; |
}; |
#endif |
int |
timer_settime(struct thread *td, struct timer_settime_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
struct itimerspec val, oval, *ovalp; |
int error; |
error = copyin(uap->value, &val, sizeof(val)); |
if (error != 0) |
return (error); |
if (uap->ovalue != NULL) |
ovalp = &oval; |
else |
ovalp = NULL; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
PROC_UNLOCK(p); |
itimer_enter(it); |
error = CLOCK_CALL(it->it_clockid, timer_settime, |
(it, uap->flags, &val, ovalp)); |
itimer_leave(it); |
ITIMER_UNLOCK(it); |
} |
if (error == 0 && uap->ovalue != NULL) |
error = copyout(ovalp, uap->ovalue, sizeof(*ovalp)); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_gettime_args { |
timer_t timerid; |
struct itimerspec * value; |
}; |
#endif |
int |
timer_gettime(struct thread *td, struct timer_gettime_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
struct itimerspec val; |
int error; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
PROC_UNLOCK(p); |
itimer_enter(it); |
error = CLOCK_CALL(it->it_clockid, timer_gettime, |
(it, &val)); |
itimer_leave(it); |
ITIMER_UNLOCK(it); |
} |
if (error == 0) |
error = copyout(&val, uap->value, sizeof(val)); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_getoverrun_args { |
timer_t timerid; |
}; |
#endif |
int |
timer_getoverrun(struct thread *td, struct timer_getoverrun_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
int error ; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
td->td_retval[0] = it->it_overrun_last; |
ITIMER_UNLOCK(it); |
PROC_UNLOCK(p); |
error = 0; |
} |
return (error); |
} |
static int |
realtimer_create(struct itimer *it) |
{ |
callout_init_mtx(&it->it_callout, &it->it_mtx, 0); |
return (0); |
} |
static int |
realtimer_delete(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
callout_stop(&it->it_callout); |
return (0); |
} |
static int |
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue) |
{ |
struct timespec cts; |
mtx_assert(&it->it_mtx, MA_OWNED); |
realtimer_clocktime(it->it_clockid, &cts); |
*ovalue = it->it_time; |
if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) { |
timespecsub(&ovalue->it_value, &cts); |
if (ovalue->it_value.tv_sec < 0 || |
(ovalue->it_value.tv_sec == 0 && |
ovalue->it_value.tv_nsec == 0)) { |
ovalue->it_value.tv_sec = 0; |
ovalue->it_value.tv_nsec = 1; |
} |
} |
return (0); |
} |
static int |
realtimer_settime(struct itimer *it, int flags, |
struct itimerspec *value, struct itimerspec *ovalue) |
{ |
struct timespec cts, ts; |
struct timeval tv; |
struct itimerspec val; |
mtx_assert(&it->it_mtx, MA_OWNED); |
val = *value; |
if (itimespecfix(&val.it_value)) |
return (EINVAL); |
if (timespecisset(&val.it_value)) { |
if (itimespecfix(&val.it_interval)) |
return (EINVAL); |
} else { |
timespecclear(&val.it_interval); |
} |
if (ovalue != NULL) |
realtimer_gettime(it, ovalue); |
it->it_time = val; |
if (timespecisset(&val.it_value)) { |
realtimer_clocktime(it->it_clockid, &cts); |
ts = val.it_value; |
if ((flags & TIMER_ABSTIME) == 0) { |
/* Convert to absolute time. */ |
timespecadd(&it->it_time.it_value, &cts); |
} else { |
timespecsub(&ts, &cts); |
/* |
* We don't care if ts is negative, tztohz will |
* fix it. |
*/ |
} |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), |
realtimer_expire, it); |
} else { |
callout_stop(&it->it_callout); |
} |
return (0); |
} |
static void |
realtimer_clocktime(clockid_t id, struct timespec *ts) |
{ |
if (id == CLOCK_REALTIME) |
getnanotime(ts); |
else /* CLOCK_MONOTONIC */ |
getnanouptime(ts); |
} |
int |
itimer_accept(struct proc *p, timer_t timerid, ksiginfo_t *ksi) |
{ |
struct itimer *it; |
PROC_LOCK_ASSERT(p, MA_OWNED); |
it = itimer_find(p, timerid, 0); |
if (it != NULL) { |
ksi->ksi_overrun = it->it_overrun; |
it->it_overrun_last = it->it_overrun; |
it->it_overrun = 0; |
ITIMER_UNLOCK(it); |
return (0); |
} |
return (EINVAL); |
} |
int |
itimespecfix(struct timespec *ts) |
{ |
if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) |
return (EINVAL); |
if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000) |
ts->tv_nsec = tick * 1000; |
return (0); |
} |
static void |
realtimer_event_hook(struct proc *p, clockid_t clock_id, int event) |
{ |
struct itimers *its; |
struct itimer *it; |
int i; |
/* |
* Timer 0 (ITIMER_REAL) is XSI interval timer, according to POSIX |
* specification, it should be inherited by new process image. |
*/ |
if (event == ITIMER_EV_EXEC) |
i = 1; |
else |
i = 0; |
its = p->p_itimers; |
for (; i < TIMER_MAX; i++) { |
if ((it = its->its_timers[i]) != NULL && |
it->it_clockid == clock_id) { |
ITIMER_LOCK(it); |
callout_stop(&it->it_callout); |
ITIMER_UNLOCK(it); |
} |
} |
} |
/* Timeout callback for realtime timer */ |
static void |
realtimer_expire(void *arg) |
{ |
struct timespec cts, ts; |
struct timeval tv; |
struct itimer *it; |
struct proc *p; |
it = (struct itimer *)arg; |
p = it->it_proc; |
realtimer_clocktime(it->it_clockid, &cts); |
/* Only fire if time is reached. */ |
if (timespeccmp(&cts, &it->it_time.it_value, >=)) { |
if (timespecisset(&it->it_time.it_interval)) { |
timespecadd(&it->it_time.it_value, |
&it->it_time.it_interval); |
while (timespeccmp(&cts, &it->it_time.it_value, >=)) { |
it->it_overrun++; |
timespecadd(&it->it_time.it_value, |
&it->it_time.it_interval); |
} |
} else { |
/* single shot timer ? */ |
timespecclear(&it->it_time.it_value); |
} |
if (timespecisset(&it->it_time.it_value)) { |
ts = it->it_time.it_value; |
timespecsub(&ts, &cts); |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), |
realtimer_expire, it); |
} |
ITIMER_UNLOCK(it); |
itimer_fire(it); |
ITIMER_LOCK(it); |
} else if (timespecisset(&it->it_time.it_value)) { |
ts = it->it_time.it_value; |
timespecsub(&ts, &cts); |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, |
it); |
} |
} |
void |
itimer_fire(struct itimer *it) |
{ |
struct proc *p = it->it_proc; |
int ret; |
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || |
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { |
PROC_LOCK(p); |
if (!KSI_ONQ(&it->it_ksi)) { |
ret = psignal_event(p, &it->it_sigev, &it->it_ksi); |
if (__predict_false(ret != 0)) { |
it->it_overrun++; |
/* |
* Broken userland code, thread went |
* away, disarm the timer. |
*/ |
if (ret == ESRCH) { |
ITIMER_LOCK(it); |
timespecclear(&it->it_time.it_value); |
timespecclear(&it->it_time.it_interval); |
callout_stop(&it->it_callout); |
ITIMER_UNLOCK(it); |
} |
} |
} else { |
it->it_overrun++; |
} |
PROC_UNLOCK(p); |
} |
} |
static void |
itimers_alloc(struct proc *p) |
{ |
struct itimers *its; |
int i; |
its = malloc(sizeof (struct itimers), M_SUBPROC, M_WAITOK | M_ZERO); |
LIST_INIT(&its->its_virtual); |
LIST_INIT(&its->its_prof); |
TAILQ_INIT(&its->its_worklist); |
for (i = 0; i < TIMER_MAX; i++) |
its->its_timers[i] = NULL; |
PROC_LOCK(p); |
if (p->p_itimers == NULL) { |
p->p_itimers = its; |
PROC_UNLOCK(p); |
} |
else { |
PROC_UNLOCK(p); |
free(its, M_SUBPROC); |
} |
} |
/* Clean up timers when some process events are being triggered. */ |
void |
itimers_event_hook(struct proc *p, int event) |
{ |
struct itimers *its; |
struct itimer *it; |
int i; |
if (p->p_itimers != NULL) { |
its = p->p_itimers; |
for (i = 0; i < MAX_CLOCKS; ++i) { |
if (posix_clocks[i].event_hook != NULL) |
CLOCK_CALL(i, event_hook, (p, i, event)); |
} |
/* |
* According to susv3, XSI interval timers should be inherited |
* by new image. |
*/ |
if (event == ITIMER_EV_EXEC) |
i = 3; |
else if (event == ITIMER_EV_EXIT) |
i = 0; |
else |
panic("unhandled event"); |
for (; i < TIMER_MAX; ++i) { |
if ((it = its->its_timers[i]) != NULL) { |
PROC_LOCK(p); |
if (KSI_ONQ(&it->it_ksi)) |
sigqueue_take(&it->it_ksi); |
PROC_UNLOCK(p); |
uma_zfree(itimer_zone, its->its_timers[i]); |
its->its_timers[i] = NULL; |
} |
} |
if (its->its_timers[0] == NULL && |
its->its_timers[1] == NULL && |
its->its_timers[2] == NULL) { |
free(its, M_SUBPROC); |
p->p_itimers = NULL; |
} |
} |
} |
/FreeBSD/mac_settime/trunk/patched/kern/kern_ntptime.c |
---|
968,7 → 968,7 |
kern_adjtime(struct thread *td, struct timeval *delta, struct timeval *olddelta) |
{ |
struct timeval atv; |
int error = 0; |
int error; |
#ifdef MAC |
error = mac_check_system_settime(td->td_ucred); |
979,6 → 979,8 |
return (EPERM); |
if (!cf_useradjtime && (error = suser_cred(td->td_ucred, SUSER_ALLOWJAIL)) != 0) |
return (error); /* jail is already checked */ |
else |
error = 0; |
mtx_lock(&Giant); |
if (olddelta) { |
/FreeBSD/mac_settime/trunk/origins/kern/kern_time.c |
---|
30,7 → 30,7 |
*/ |
#include <sys/cdefs.h> |
__FBSDID("$FreeBSD: src/sys/kern/kern_time.c,v 1.116 2005/03/31 22:51:18 jhb Exp $"); |
__FBSDID("$FreeBSD: src/sys/kern/kern_time.c,v 1.123 2005/11/04 09:39:17 davidxu Exp $"); |
#include "opt_mac.h" |
47,6 → 47,7 |
#include <sys/sysent.h> |
#include <sys/proc.h> |
#include <sys/time.h> |
#include <sys/timers.h> |
#include <sys/timetc.h> |
#include <sys/vnode.h> |
53,9 → 54,14 |
#include <vm/vm.h> |
#include <vm/vm_extern.h> |
#define MAX_CLOCKS (CLOCK_MONOTONIC+1) |
int tz_minuteswest; |
int tz_dsttime; |
static struct kclock posix_clocks[MAX_CLOCKS]; |
static uma_zone_t itimer_zone = NULL; |
/* |
* Time of day and interval timer support. |
* |
70,6 → 76,35 |
static void timevalfix(struct timeval *); |
static void no_lease_updatetime(int); |
static void itimer_start(void); |
static int itimer_init(void *, int, int); |
static void itimer_fini(void *, int); |
static void itimer_enter(struct itimer *); |
static void itimer_leave(struct itimer *); |
static struct itimer *itimer_find(struct proc *, timer_t, int); |
static void itimers_alloc(struct proc *); |
static int realtimer_create(struct itimer *); |
static int realtimer_gettime(struct itimer *, struct itimerspec *); |
static int realtimer_settime(struct itimer *, int, |
struct itimerspec *, struct itimerspec *); |
static int realtimer_delete(struct itimer *); |
static void realtimer_clocktime(clockid_t, struct timespec *); |
static void realtimer_expire(void *); |
static void realtimer_event_hook(struct proc *, clockid_t, int event); |
static int kern_timer_create(struct thread *, clockid_t, |
struct sigevent *, timer_t *, timer_t); |
static int kern_timer_delete(struct thread *, timer_t); |
int register_posix_clock(int, struct kclock *); |
void itimer_fire(struct itimer *it); |
int itimespecfix(struct timespec *ts); |
#define CLOCK_CALL(clock, call, arglist) \ |
((*posix_clocks[clock].call) arglist) |
SYSINIT(posix_timer, SI_SUB_P1003_1B, SI_ORDER_FIRST+4, itimer_start, NULL); |
static void |
no_lease_updatetime(deltat) |
int deltat; |
155,19 → 190,31 |
clock_gettime(struct thread *td, struct clock_gettime_args *uap) |
{ |
struct timespec ats; |
int error; |
error = kern_clock_gettime(td, uap->clock_id, &ats); |
if (error == 0) |
error = copyout(&ats, uap->tp, sizeof(ats)); |
return (error); |
} |
int |
kern_clock_gettime(struct thread *td, clockid_t clock_id, struct timespec *ats) |
{ |
struct timeval sys, user; |
struct proc *p; |
p = td->td_proc; |
switch (uap->clock_id) { |
switch (clock_id) { |
case CLOCK_REALTIME: |
nanotime(&ats); |
nanotime(ats); |
break; |
case CLOCK_VIRTUAL: |
PROC_LOCK(p); |
calcru(p, &user, &sys); |
PROC_UNLOCK(p); |
TIMEVAL_TO_TIMESPEC(&user, &ats); |
TIMEVAL_TO_TIMESPEC(&user, ats); |
break; |
case CLOCK_PROF: |
PROC_LOCK(p); |
174,15 → 221,15 |
calcru(p, &user, &sys); |
PROC_UNLOCK(p); |
timevaladd(&user, &sys); |
TIMEVAL_TO_TIMESPEC(&user, &ats); |
TIMEVAL_TO_TIMESPEC(&user, ats); |
break; |
case CLOCK_MONOTONIC: |
nanouptime(&ats); |
nanouptime(ats); |
break; |
default: |
return (EINVAL); |
} |
return (copyout(&ats, uap->tp, sizeof(ats))); |
return (0); |
} |
#ifndef _SYS_SYSPROTO_H_ |
199,10 → 246,20 |
int |
clock_settime(struct thread *td, struct clock_settime_args *uap) |
{ |
struct timeval atv; |
struct timespec ats; |
int error; |
if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0) |
return (error); |
return (kern_clock_settime(td, uap->clock_id, &ats)); |
} |
int |
kern_clock_settime(struct thread *td, clockid_t clock_id, struct timespec *ats) |
{ |
struct timeval atv; |
int error; |
#ifdef MAC |
error = mac_check_system_settime(td->td_ucred); |
if (error) |
210,14 → 267,12 |
#endif |
if ((error = suser(td)) != 0) |
return (error); |
if (uap->clock_id != CLOCK_REALTIME) |
if (clock_id != CLOCK_REALTIME) |
return (EINVAL); |
if ((error = copyin(uap->tp, &ats, sizeof(ats))) != 0) |
return (error); |
if (ats.tv_nsec < 0 || ats.tv_nsec >= 1000000000) |
if (ats->tv_nsec < 0 || ats->tv_nsec >= 1000000000) |
return (EINVAL); |
/* XXX Don't convert nsec->usec and back */ |
TIMESPEC_TO_TIMEVAL(&atv, &ats); |
TIMESPEC_TO_TIMEVAL(&atv, ats); |
error = settime(td, &atv); |
return (error); |
} |
233,9 → 288,23 |
clock_getres(struct thread *td, struct clock_getres_args *uap) |
{ |
struct timespec ts; |
int error; |
ts.tv_sec = 0; |
switch (uap->clock_id) { |
if (uap->tp == NULL) |
return (0); |
error = kern_clock_getres(td, uap->clock_id, &ts); |
if (error == 0) |
error = copyout(&ts, uap->tp, sizeof(ts)); |
return (error); |
} |
int |
kern_clock_getres(struct thread *td, clockid_t clock_id, struct timespec *ts) |
{ |
ts->tv_sec = 0; |
switch (clock_id) { |
case CLOCK_REALTIME: |
case CLOCK_MONOTONIC: |
/* |
243,19 → 312,17 |
* Rounding up is especially important if rounding down |
* would give 0. Perfect rounding is unimportant. |
*/ |
ts.tv_nsec = 1000000000 / tc_getfrequency() + 1; |
ts->tv_nsec = 1000000000 / tc_getfrequency() + 1; |
break; |
case CLOCK_VIRTUAL: |
case CLOCK_PROF: |
/* Accurately round up here because we can do so cheaply. */ |
ts.tv_nsec = (1000000000 + hz - 1) / hz; |
ts->tv_nsec = (1000000000 + hz - 1) / hz; |
break; |
default: |
return (EINVAL); |
} |
if (uap->tp == NULL) |
return (0); |
return (copyout(&ts, uap->tp, sizeof(ts))); |
return (0); |
} |
static int nanowait; |
627,8 → 694,7 |
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; |
775,3 → 841,668 |
return (maxpps < 0 || *curpps < maxpps); |
} |
} |
static void |
itimer_start(void) |
{ |
struct kclock rt_clock = { |
.timer_create = realtimer_create, |
.timer_delete = realtimer_delete, |
.timer_settime = realtimer_settime, |
.timer_gettime = realtimer_gettime, |
.event_hook = realtimer_event_hook |
}; |
itimer_zone = uma_zcreate("itimer", sizeof(struct itimer), |
NULL, NULL, itimer_init, itimer_fini, UMA_ALIGN_PTR, 0); |
register_posix_clock(CLOCK_REALTIME, &rt_clock); |
register_posix_clock(CLOCK_MONOTONIC, &rt_clock); |
} |
int |
register_posix_clock(int clockid, struct kclock *clk) |
{ |
if ((unsigned)clockid >= MAX_CLOCKS) { |
printf("%s: invalid clockid\n", __func__); |
return (0); |
} |
posix_clocks[clockid] = *clk; |
return (1); |
} |
static int |
itimer_init(void *mem, int size, int flags) |
{ |
struct itimer *it; |
it = (struct itimer *)mem; |
mtx_init(&it->it_mtx, "itimer lock", NULL, MTX_DEF); |
return (0); |
} |
static void |
itimer_fini(void *mem, int size) |
{ |
struct itimer *it; |
it = (struct itimer *)mem; |
mtx_destroy(&it->it_mtx); |
} |
static void |
itimer_enter(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
it->it_usecount++; |
} |
static void |
itimer_leave(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
KASSERT(it->it_usecount > 0, ("invalid it_usecount")); |
if (--it->it_usecount == 0 && (it->it_flags & ITF_WANTED) != 0) |
wakeup(it); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_create_args { |
clockid_t clock_id; |
struct sigevent * evp; |
timer_t * timerid; |
}; |
#endif |
int |
timer_create(struct thread *td, struct timer_create_args *uap) |
{ |
struct sigevent *evp1, ev; |
timer_t id; |
int error; |
if (uap->evp != NULL) { |
error = copyin(uap->evp, &ev, sizeof(ev)); |
if (error != 0) |
return (error); |
evp1 = &ev; |
} else |
evp1 = NULL; |
error = kern_timer_create(td, uap->clock_id, evp1, &id, -1); |
if (error == 0) { |
error = copyout(&id, uap->timerid, sizeof(timer_t)); |
if (error != 0) |
kern_timer_delete(td, id); |
} |
return (error); |
} |
static int |
kern_timer_create(struct thread *td, clockid_t clock_id, |
struct sigevent *evp, timer_t *timerid, timer_t preset_id) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
int id; |
int error; |
if (clock_id < 0 || clock_id >= MAX_CLOCKS) |
return (EINVAL); |
if (posix_clocks[clock_id].timer_create == NULL) |
return (EINVAL); |
if (evp != NULL) { |
if (evp->sigev_notify != SIGEV_NONE && |
evp->sigev_notify != SIGEV_SIGNAL && |
evp->sigev_notify != SIGEV_THREAD_ID) |
return (EINVAL); |
if ((evp->sigev_notify == SIGEV_SIGNAL || |
evp->sigev_notify == SIGEV_THREAD_ID) && |
!_SIG_VALID(evp->sigev_signo)) |
return (EINVAL); |
} |
if (p->p_itimers == NULL) |
itimers_alloc(p); |
it = uma_zalloc(itimer_zone, M_WAITOK); |
it->it_flags = 0; |
it->it_usecount = 0; |
it->it_active = 0; |
timespecclear(&it->it_time.it_value); |
timespecclear(&it->it_time.it_interval); |
it->it_overrun = 0; |
it->it_overrun_last = 0; |
it->it_clockid = clock_id; |
it->it_timerid = -1; |
it->it_proc = p; |
ksiginfo_init(&it->it_ksi); |
it->it_ksi.ksi_flags |= KSI_INS | KSI_EXT; |
error = CLOCK_CALL(clock_id, timer_create, (it)); |
if (error != 0) |
goto out; |
PROC_LOCK(p); |
if (preset_id != -1) { |
KASSERT(preset_id >= 0 && preset_id < 3, ("invalid preset_id")); |
id = preset_id; |
if (p->p_itimers->its_timers[id] != NULL) { |
PROC_UNLOCK(p); |
error = 0; |
goto out; |
} |
} else { |
/* |
* Find a free timer slot, skipping those reserved |
* for setitimer(). |
*/ |
for (id = 3; id < TIMER_MAX; id++) |
if (p->p_itimers->its_timers[id] == NULL) |
break; |
if (id == TIMER_MAX) { |
PROC_UNLOCK(p); |
error = EAGAIN; |
goto out; |
} |
} |
it->it_timerid = id; |
p->p_itimers->its_timers[id] = it; |
if (evp != NULL) |
it->it_sigev = *evp; |
else { |
it->it_sigev.sigev_notify = SIGEV_SIGNAL; |
switch (clock_id) { |
default: |
case CLOCK_REALTIME: |
it->it_sigev.sigev_signo = SIGALRM; |
break; |
case CLOCK_VIRTUAL: |
it->it_sigev.sigev_signo = SIGVTALRM; |
break; |
case CLOCK_PROF: |
it->it_sigev.sigev_signo = SIGPROF; |
break; |
} |
it->it_sigev.sigev_value.sival_int = id; |
} |
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || |
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { |
it->it_ksi.ksi_signo = it->it_sigev.sigev_signo; |
it->it_ksi.ksi_code = SI_TIMER; |
it->it_ksi.ksi_value = it->it_sigev.sigev_value; |
it->it_ksi.ksi_timerid = id; |
} |
PROC_UNLOCK(p); |
*timerid = id; |
return (0); |
out: |
ITIMER_LOCK(it); |
CLOCK_CALL(it->it_clockid, timer_delete, (it)); |
ITIMER_UNLOCK(it); |
uma_zfree(itimer_zone, it); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_delete_args { |
timer_t timerid; |
}; |
#endif |
int |
timer_delete(struct thread *td, struct timer_delete_args *uap) |
{ |
return (kern_timer_delete(td, uap->timerid)); |
} |
static struct itimer * |
itimer_find(struct proc *p, timer_t timerid, int include_deleting) |
{ |
struct itimer *it; |
PROC_LOCK_ASSERT(p, MA_OWNED); |
if ((p->p_itimers == NULL) || (timerid >= TIMER_MAX) || |
(it = p->p_itimers->its_timers[timerid]) == NULL) { |
return (NULL); |
} |
ITIMER_LOCK(it); |
if (!include_deleting && (it->it_flags & ITF_DELETING) != 0) { |
ITIMER_UNLOCK(it); |
it = NULL; |
} |
return (it); |
} |
static int |
kern_timer_delete(struct thread *td, timer_t timerid) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
PROC_LOCK(p); |
it = itimer_find(p, timerid, 0); |
if (it == NULL) { |
PROC_UNLOCK(p); |
return (EINVAL); |
} |
PROC_UNLOCK(p); |
it->it_flags |= ITF_DELETING; |
while (it->it_usecount > 0) { |
it->it_flags |= ITF_WANTED; |
msleep(it, &it->it_mtx, PPAUSE, "itimer", 0); |
} |
it->it_flags &= ~ITF_WANTED; |
CLOCK_CALL(it->it_clockid, timer_delete, (it)); |
ITIMER_UNLOCK(it); |
PROC_LOCK(p); |
if (KSI_ONQ(&it->it_ksi)) |
sigqueue_take(&it->it_ksi); |
p->p_itimers->its_timers[timerid] = NULL; |
PROC_UNLOCK(p); |
uma_zfree(itimer_zone, it); |
return (0); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_settime_args { |
timer_t timerid; |
int flags; |
const struct itimerspec * value; |
struct itimerspec * ovalue; |
}; |
#endif |
int |
timer_settime(struct thread *td, struct timer_settime_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
struct itimerspec val, oval, *ovalp; |
int error; |
error = copyin(uap->value, &val, sizeof(val)); |
if (error != 0) |
return (error); |
if (uap->ovalue != NULL) |
ovalp = &oval; |
else |
ovalp = NULL; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
PROC_UNLOCK(p); |
itimer_enter(it); |
error = CLOCK_CALL(it->it_clockid, timer_settime, |
(it, uap->flags, &val, ovalp)); |
itimer_leave(it); |
ITIMER_UNLOCK(it); |
} |
if (error == 0 && uap->ovalue != NULL) |
error = copyout(ovalp, uap->ovalue, sizeof(*ovalp)); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_gettime_args { |
timer_t timerid; |
struct itimerspec * value; |
}; |
#endif |
int |
timer_gettime(struct thread *td, struct timer_gettime_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
struct itimerspec val; |
int error; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
PROC_UNLOCK(p); |
itimer_enter(it); |
error = CLOCK_CALL(it->it_clockid, timer_gettime, |
(it, &val)); |
itimer_leave(it); |
ITIMER_UNLOCK(it); |
} |
if (error == 0) |
error = copyout(&val, uap->value, sizeof(val)); |
return (error); |
} |
#ifndef _SYS_SYSPROTO_H_ |
struct timer_getoverrun_args { |
timer_t timerid; |
}; |
#endif |
int |
timer_getoverrun(struct thread *td, struct timer_getoverrun_args *uap) |
{ |
struct proc *p = td->td_proc; |
struct itimer *it; |
int error ; |
PROC_LOCK(p); |
if (uap->timerid < 3 || |
(it = itimer_find(p, uap->timerid, 0)) == NULL) { |
PROC_UNLOCK(p); |
error = EINVAL; |
} else { |
td->td_retval[0] = it->it_overrun_last; |
ITIMER_UNLOCK(it); |
PROC_UNLOCK(p); |
error = 0; |
} |
return (error); |
} |
static int |
realtimer_create(struct itimer *it) |
{ |
callout_init_mtx(&it->it_callout, &it->it_mtx, 0); |
return (0); |
} |
static int |
realtimer_delete(struct itimer *it) |
{ |
mtx_assert(&it->it_mtx, MA_OWNED); |
callout_stop(&it->it_callout); |
return (0); |
} |
static int |
realtimer_gettime(struct itimer *it, struct itimerspec *ovalue) |
{ |
struct timespec cts; |
mtx_assert(&it->it_mtx, MA_OWNED); |
realtimer_clocktime(it->it_clockid, &cts); |
*ovalue = it->it_time; |
if (ovalue->it_value.tv_sec != 0 || ovalue->it_value.tv_nsec != 0) { |
timespecsub(&ovalue->it_value, &cts); |
if (ovalue->it_value.tv_sec < 0 || |
(ovalue->it_value.tv_sec == 0 && |
ovalue->it_value.tv_nsec == 0)) { |
ovalue->it_value.tv_sec = 0; |
ovalue->it_value.tv_nsec = 1; |
} |
} |
return (0); |
} |
static int |
realtimer_settime(struct itimer *it, int flags, |
struct itimerspec *value, struct itimerspec *ovalue) |
{ |
struct timespec cts, ts; |
struct timeval tv; |
struct itimerspec val; |
mtx_assert(&it->it_mtx, MA_OWNED); |
val = *value; |
if (itimespecfix(&val.it_value)) |
return (EINVAL); |
if (timespecisset(&val.it_value)) { |
if (itimespecfix(&val.it_interval)) |
return (EINVAL); |
} else { |
timespecclear(&val.it_interval); |
} |
if (ovalue != NULL) |
realtimer_gettime(it, ovalue); |
it->it_time = val; |
if (timespecisset(&val.it_value)) { |
realtimer_clocktime(it->it_clockid, &cts); |
ts = val.it_value; |
if ((flags & TIMER_ABSTIME) == 0) { |
/* Convert to absolute time. */ |
timespecadd(&it->it_time.it_value, &cts); |
} else { |
timespecsub(&ts, &cts); |
/* |
* We don't care if ts is negative, tztohz will |
* fix it. |
*/ |
} |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), |
realtimer_expire, it); |
} else { |
callout_stop(&it->it_callout); |
} |
return (0); |
} |
static void |
realtimer_clocktime(clockid_t id, struct timespec *ts) |
{ |
if (id == CLOCK_REALTIME) |
getnanotime(ts); |
else /* CLOCK_MONOTONIC */ |
getnanouptime(ts); |
} |
int |
itimer_accept(struct proc *p, timer_t timerid, ksiginfo_t *ksi) |
{ |
struct itimer *it; |
PROC_LOCK_ASSERT(p, MA_OWNED); |
it = itimer_find(p, timerid, 0); |
if (it != NULL) { |
ksi->ksi_overrun = it->it_overrun; |
it->it_overrun_last = it->it_overrun; |
it->it_overrun = 0; |
ITIMER_UNLOCK(it); |
return (0); |
} |
return (EINVAL); |
} |
int |
itimespecfix(struct timespec *ts) |
{ |
if (ts->tv_sec < 0 || ts->tv_nsec < 0 || ts->tv_nsec >= 1000000000) |
return (EINVAL); |
if (ts->tv_sec == 0 && ts->tv_nsec != 0 && ts->tv_nsec < tick * 1000) |
ts->tv_nsec = tick * 1000; |
return (0); |
} |
static void |
realtimer_event_hook(struct proc *p, clockid_t clock_id, int event) |
{ |
struct itimers *its; |
struct itimer *it; |
int i; |
/* |
* Timer 0 (ITIMER_REAL) is XSI interval timer, according to POSIX |
* specification, it should be inherited by new process image. |
*/ |
if (event == ITIMER_EV_EXEC) |
i = 1; |
else |
i = 0; |
its = p->p_itimers; |
for (; i < TIMER_MAX; i++) { |
if ((it = its->its_timers[i]) != NULL && |
it->it_clockid == clock_id) { |
ITIMER_LOCK(it); |
callout_stop(&it->it_callout); |
ITIMER_UNLOCK(it); |
} |
} |
} |
/* Timeout callback for realtime timer */ |
static void |
realtimer_expire(void *arg) |
{ |
struct timespec cts, ts; |
struct timeval tv; |
struct itimer *it; |
struct proc *p; |
it = (struct itimer *)arg; |
p = it->it_proc; |
realtimer_clocktime(it->it_clockid, &cts); |
/* Only fire if time is reached. */ |
if (timespeccmp(&cts, &it->it_time.it_value, >=)) { |
if (timespecisset(&it->it_time.it_interval)) { |
timespecadd(&it->it_time.it_value, |
&it->it_time.it_interval); |
while (timespeccmp(&cts, &it->it_time.it_value, >=)) { |
it->it_overrun++; |
timespecadd(&it->it_time.it_value, |
&it->it_time.it_interval); |
} |
} else { |
/* single shot timer ? */ |
timespecclear(&it->it_time.it_value); |
} |
if (timespecisset(&it->it_time.it_value)) { |
ts = it->it_time.it_value; |
timespecsub(&ts, &cts); |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), |
realtimer_expire, it); |
} |
ITIMER_UNLOCK(it); |
itimer_fire(it); |
ITIMER_LOCK(it); |
} else if (timespecisset(&it->it_time.it_value)) { |
ts = it->it_time.it_value; |
timespecsub(&ts, &cts); |
TIMESPEC_TO_TIMEVAL(&tv, &ts); |
callout_reset(&it->it_callout, tvtohz(&tv), realtimer_expire, |
it); |
} |
} |
void |
itimer_fire(struct itimer *it) |
{ |
struct proc *p = it->it_proc; |
int ret; |
if (it->it_sigev.sigev_notify == SIGEV_SIGNAL || |
it->it_sigev.sigev_notify == SIGEV_THREAD_ID) { |
PROC_LOCK(p); |
if (!KSI_ONQ(&it->it_ksi)) { |
ret = psignal_event(p, &it->it_sigev, &it->it_ksi); |
if (__predict_false(ret != 0)) { |
it->it_overrun++; |
/* |
* Broken userland code, thread went |
* away, disarm the timer. |
*/ |
if (ret == ESRCH) { |
ITIMER_LOCK(it); |
timespecclear(&it->it_time.it_value); |
timespecclear(&it->it_time.it_interval); |
callout_stop(&it->it_callout); |
ITIMER_UNLOCK(it); |
} |
} |
} else { |
it->it_overrun++; |
} |
PROC_UNLOCK(p); |
} |
} |
static void |
itimers_alloc(struct proc *p) |
{ |
struct itimers *its; |
int i; |
its = malloc(sizeof (struct itimers), M_SUBPROC, M_WAITOK | M_ZERO); |
LIST_INIT(&its->its_virtual); |
LIST_INIT(&its->its_prof); |
TAILQ_INIT(&its->its_worklist); |
for (i = 0; i < TIMER_MAX; i++) |
its->its_timers[i] = NULL; |
PROC_LOCK(p); |
if (p->p_itimers == NULL) { |
p->p_itimers = its; |
PROC_UNLOCK(p); |
} |
else { |
PROC_UNLOCK(p); |
free(its, M_SUBPROC); |
} |
} |
/* Clean up timers when some process events are being triggered. */ |
void |
itimers_event_hook(struct proc *p, int event) |
{ |
struct itimers *its; |
struct itimer *it; |
int i; |
if (p->p_itimers != NULL) { |
its = p->p_itimers; |
for (i = 0; i < MAX_CLOCKS; ++i) { |
if (posix_clocks[i].event_hook != NULL) |
CLOCK_CALL(i, event_hook, (p, i, event)); |
} |
/* |
* According to susv3, XSI interval timers should be inherited |
* by new image. |
*/ |
if (event == ITIMER_EV_EXEC) |
i = 3; |
else if (event == ITIMER_EV_EXIT) |
i = 0; |
else |
panic("unhandled event"); |
for (; i < TIMER_MAX; ++i) { |
if ((it = its->its_timers[i]) != NULL) { |
PROC_LOCK(p); |
if (KSI_ONQ(&it->it_ksi)) |
sigqueue_take(&it->it_ksi); |
PROC_UNLOCK(p); |
uma_zfree(itimer_zone, its->its_timers[i]); |
its->its_timers[i] = NULL; |
} |
} |
if (its->its_timers[0] == NULL && |
its->its_timers[1] == NULL && |
its->its_timers[2] == NULL) { |
free(its, M_SUBPROC); |
p->p_itimers = NULL; |
} |
} |
} |