#include <sys/param.h>
#include <sys/resourcevar.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/vnode.h>
#include <sys/mount.h>
#include <kern/clock.h>
#define HZ 100
volatile struct timeval time;
decl_simple_lock_data(, tz_slock);
struct gettimeofday_args{
struct timeval *tp;
struct timezone *tzp;
};
int
gettimeofday(p, uap, retval)
struct proc *p;
register struct gettimeofday_args *uap;
register_t *retval;
{
struct timeval atv;
int error = 0;
extern simple_lock_data_t tz_slock;
struct timezone ltz;
if (uap->tp) {
microtime(&atv);
if (error = copyout((caddr_t)&atv, (caddr_t)uap->tp,
sizeof (atv)))
return(error);
}
if (uap->tzp) {
usimple_lock(&tz_slock);
ltz = tz;
usimple_unlock(&tz_slock);
error = copyout((caddr_t)<z, (caddr_t)uap->tzp,
sizeof (tz));
}
return(error);
}
struct settimeofday_args {
struct timeval *tv;
struct timezone *tzp;
};
int
settimeofday(p, uap, retval)
struct proc *p;
struct settimeofday_args *uap;
register_t *retval;
{
struct timeval atv;
struct timezone atz;
int error, s;
extern simple_lock_data_t tz_slock;
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
if (uap->tv && (error = copyin((caddr_t)uap->tv,
(caddr_t)&atv, sizeof(atv))))
return (error);
if (uap->tzp && (error = copyin((caddr_t)uap->tzp,
(caddr_t)&atz, sizeof(atz))))
return (error);
if (uap->tv)
setthetime(&atv);
if (uap->tzp) {
usimple_lock(&tz_slock);
tz = atz;
usimple_unlock(&tz_slock);
}
return (0);
}
setthetime(tv)
struct timeval *tv;
{
long delta = tv->tv_sec - time.tv_sec;
mach_timespec_t now;
now.tv_sec = tv->tv_sec;
now.tv_nsec = tv->tv_usec * NSEC_PER_USEC;
clock_set_calendar_value(now);
boottime.tv_sec += delta;
#if NFSCLIENT || NFSSERVER
lease_updatetime(delta);
#endif
}
#define tickadj (40 * NSEC_PER_USEC)
#define bigadj (1 * NSEC_PER_SEC)
struct adjtime_args {
struct timeval *delta;
struct timeval *olddelta;
};
int
adjtime(p, uap, retval)
struct proc *p;
register struct adjtime_args *uap;
register_t *retval;
{
struct timeval atv;
int64_t total;
uint32_t delta;
int error;
if (error = suser(p->p_ucred, &p->p_acflag))
return (error);
if (error = copyin((caddr_t)uap->delta,
(caddr_t)&atv, sizeof (struct timeval)))
return (error);
total = (int64_t)atv.tv_sec * NSEC_PER_SEC + atv.tv_usec * NSEC_PER_USEC;
if (total > bigadj || total < -bigadj)
delta = 10 * tickadj;
else
delta = tickadj;
total = clock_set_calendar_adjtime(total, delta);
if (uap->olddelta) {
atv.tv_sec = total / NSEC_PER_SEC;
atv.tv_usec = (total / NSEC_PER_USEC) % USEC_PER_SEC;
(void) copyout((caddr_t)&atv,
(caddr_t)uap->olddelta, sizeof (struct timeval));
}
return (0);
}
void
inittodr(base)
time_t base;
{
microtime(&time);
boottime.tv_sec = time.tv_sec;
boottime.tv_usec = 0;
if (time.tv_sec < 0) {
printf ("WARNING: preposterous time in Real Time Clock");
time.tv_sec = 0;
time.tv_usec = 0;
setthetime(&time);
boottime = time;
printf(" -- CHECK AND RESET THE DATE!\n");
}
return;
}
void timevaladd(
struct timeval *t1,
struct timeval *t2);
void timevalsub(
struct timeval *t1,
struct timeval *t2);
void timevalfix(
struct timeval *t1);
uint64_t
tvtoabstime(
struct timeval *tvp);
struct getitimer_args {
u_int which;
struct itimerval *itv;
};
int
getitimer(p, uap, retval)
struct proc *p;
register struct getitimer_args *uap;
register_t *retval;
{
struct itimerval aitv;
if (uap->which > ITIMER_PROF)
return(EINVAL);
if (uap->which == ITIMER_REAL) {
aitv = p->p_realtimer;
if (timerisset(&p->p_rtime)) {
struct timeval now;
microuptime(&now);
if (timercmp(&p->p_rtime, &now, <))
timerclear(&aitv.it_value);
else {
aitv.it_value = p->p_rtime;
timevalsub(&aitv.it_value, &now);
}
}
else
timerclear(&aitv.it_value);
}
else
aitv = p->p_stats->p_timer[uap->which];
return (copyout((caddr_t)&aitv,
(caddr_t)uap->itv, sizeof (struct itimerval)));
}
struct setitimer_args {
u_int which;
struct itimerval *itv;
struct itimerval *oitv;
};
int
setitimer(p, uap, retval)
struct proc *p;
register struct setitimer_args *uap;
register_t *retval;
{
struct itimerval aitv;
register struct itimerval *itvp;
int error;
if (uap->which > ITIMER_PROF)
return (EINVAL);
if ((itvp = uap->itv) &&
(error = copyin((caddr_t)itvp,
(caddr_t)&aitv, sizeof (struct itimerval))))
return (error);
if ((uap->itv = uap->oitv) && (error = getitimer(p, uap, retval)))
return (error);
if (itvp == 0)
return (0);
if (itimerfix(&aitv.it_value) || itimerfix(&aitv.it_interval))
return (EINVAL);
if (uap->which == ITIMER_REAL) {
thread_call_func_cancel(realitexpire, (void *)p->p_pid, FALSE);
if (timerisset(&aitv.it_value)) {
microuptime(&p->p_rtime);
timevaladd(&p->p_rtime, &aitv.it_value);
thread_call_func_delayed(
realitexpire, (void *)p->p_pid,
tvtoabstime(&p->p_rtime));
}
else
timerclear(&p->p_rtime);
p->p_realtimer = aitv;
}
else
p->p_stats->p_timer[uap->which] = aitv;
return (0);
}
void
realitexpire(
void *pid)
{
register struct proc *p;
struct timeval now;
boolean_t funnel_state = thread_funnel_set(kernel_flock, TRUE);
p = pfind((pid_t)pid);
if (p == NULL) {
(void) thread_funnel_set(kernel_flock, FALSE);
return;
}
if (!timerisset(&p->p_realtimer.it_interval)) {
timerclear(&p->p_rtime);
psignal(p, SIGALRM);
(void) thread_funnel_set(kernel_flock, FALSE);
return;
}
microuptime(&now);
timevaladd(&p->p_rtime, &p->p_realtimer.it_interval);
if (timercmp(&p->p_rtime, &now, <=)) {
if ((p->p_rtime.tv_sec + 2) >= now.tv_sec) {
for (;;) {
timevaladd(&p->p_rtime, &p->p_realtimer.it_interval);
if (timercmp(&p->p_rtime, &now, >))
break;
}
}
else {
p->p_rtime = p->p_realtimer.it_interval;
timevaladd(&p->p_rtime, &now);
}
}
thread_call_func_delayed(realitexpire, pid, tvtoabstime(&p->p_rtime));
psignal(p, SIGALRM);
(void) thread_funnel_set(kernel_flock, FALSE);
}
int
itimerfix(tv)
struct timeval *tv;
{
if (tv->tv_sec < 0 || tv->tv_sec > 100000000 ||
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;
return (0);
}
int
itimerdecr(itp, usec)
register struct itimerval *itp;
int usec;
{
if (itp->it_value.tv_usec < usec) {
if (itp->it_value.tv_sec == 0) {
usec -= itp->it_value.tv_usec;
goto expire;
}
itp->it_value.tv_usec += 1000000;
itp->it_value.tv_sec--;
}
itp->it_value.tv_usec -= usec;
usec = 0;
if (timerisset(&itp->it_value))
return (1);
expire:
if (timerisset(&itp->it_interval)) {
itp->it_value = itp->it_interval;
itp->it_value.tv_usec -= usec;
if (itp->it_value.tv_usec < 0) {
itp->it_value.tv_usec += 1000000;
itp->it_value.tv_sec--;
}
} else
itp->it_value.tv_usec = 0;
return (0);
}
void
timevaladd(
struct timeval *t1,
struct timeval *t2)
{
t1->tv_sec += t2->tv_sec;
t1->tv_usec += t2->tv_usec;
timevalfix(t1);
}
void
timevalsub(
struct timeval *t1,
struct timeval *t2)
{
t1->tv_sec -= t2->tv_sec;
t1->tv_usec -= t2->tv_usec;
timevalfix(t1);
}
void
timevalfix(
struct timeval *t1)
{
if (t1->tv_usec < 0) {
t1->tv_sec--;
t1->tv_usec += 1000000;
}
if (t1->tv_usec >= 1000000) {
t1->tv_sec++;
t1->tv_usec -= 1000000;
}
}
void
microtime(
struct timeval *tvp)
{
mach_timespec_t now = clock_get_calendar_value();
tvp->tv_sec = now.tv_sec;
tvp->tv_usec = now.tv_nsec / NSEC_PER_USEC;
}
void
microuptime(
struct timeval *tvp)
{
mach_timespec_t now = clock_get_system_value();
tvp->tv_sec = now.tv_sec;
tvp->tv_usec = now.tv_nsec / NSEC_PER_USEC;
}
void
nanotime(
struct timespec *tsp)
{
mach_timespec_t now = clock_get_calendar_value();
tsp->tv_sec = now.tv_sec;
tsp->tv_nsec = now.tv_nsec;
}
void
nanouptime(
struct timespec *tsp)
{
mach_timespec_t now = clock_get_system_value();
tsp->tv_sec = now.tv_sec;
tsp->tv_nsec = now.tv_nsec;
}
uint64_t
tvtoabstime(
struct timeval *tvp)
{
uint64_t result, usresult;
clock_interval_to_absolutetime_interval(
tvp->tv_sec, NSEC_PER_SEC, &result);
clock_interval_to_absolutetime_interval(
tvp->tv_usec, NSEC_PER_USEC, &usresult);
return (result + usresult);
}
void
time_zone_slock_init(void)
{
extern simple_lock_data_t tz_slock;
simple_lock_init(&tz_slock);
}