#include "opt_ntp.h"
#include "opt_pctrack.h"
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/callout.h>
#include <sys/kernel.h>
#include <sys/kinfo.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/resource.h>
#include <sys/resourcevar.h>
#include <sys/signalvar.h>
#include <sys/caps.h>
#include <sys/timex.h>
#include <sys/timepps.h>
#include <sys/upmap.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
#include <sys/kcollect.h>
#include <sys/exislock.h>
#include <sys/exislock2.h>
#include <vm/vm.h>
#include <vm/pmap.h>
#include <vm/vm_map.h>
#include <vm/vm_extern.h>
#include <sys/thread2.h>
#include <sys/spinlock2.h>
#include <machine/cpu.h>
#include <machine/limits.h>
#include <machine/smp.h>
#include <machine/cpufunc.h>
#include <machine/specialreg.h>
#include <machine/clock.h>
#ifdef DEBUG_PCTRACK
static void do_pctrack(struct intrframe *frame, int which);
#endif
static void initclocks (void *dummy);
SYSINIT(clocks, SI_BOOT2_CLOCKS, SI_ORDER_FIRST, initclocks, NULL);
struct kinfo_cputime cputime_percpu[MAXCPU];
#ifdef DEBUG_PCTRACK
struct kinfo_pcheader cputime_pcheader = { PCTRACK_SIZE, PCTRACK_ARYSIZE };
struct kinfo_pctrack cputime_pctrack[MAXCPU][PCTRACK_SIZE];
#endif
__read_mostly static int sniff_enable = 1;
__read_mostly static int sniff_target = -1;
__read_mostly static int clock_debug2 = 0;
SYSCTL_INT(_kern, OID_AUTO, sniff_enable, CTLFLAG_RW, &sniff_enable, 0 , "");
SYSCTL_INT(_kern, OID_AUTO, sniff_target, CTLFLAG_RW, &sniff_target, 0 , "");
SYSCTL_INT(_debug, OID_AUTO, clock_debug2, CTLFLAG_RW, &clock_debug2, 0 , "");
__read_mostly long pseudo_ticks = 1;
static int
sysctl_cputime(SYSCTL_HANDLER_ARGS)
{
int cpu, error = 0;
int root_error;
size_t size = sizeof(struct kinfo_cputime);
struct kinfo_cputime tmp;
root_error = caps_priv_check_self(SYSCAP_RESTRICTEDROOT);
for (cpu = 0; cpu < ncpus; ++cpu) {
tmp = cputime_percpu[cpu];
if (root_error == 0) {
tmp.cp_sample_pc =
(int64_t)globaldata_find(cpu)->gd_sample_pc;
tmp.cp_sample_sp =
(int64_t)globaldata_find(cpu)->gd_sample_sp;
}
if ((error = SYSCTL_OUT(req, &tmp, size)) != 0)
break;
}
if (root_error == 0) {
if (sniff_enable) {
int n = sniff_target;
if (n < 0)
smp_sniff();
else if (n < ncpus)
cpu_sniff(n);
}
}
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, cputime, (CTLTYPE_OPAQUE|CTLFLAG_RD), 0, 0,
sysctl_cputime, "S,kinfo_cputime", "CPU time statistics");
static int
sysctl_cp_time(SYSCTL_HANDLER_ARGS)
{
long cpu_states[CPUSTATES] = {0};
int cpu, error = 0;
size_t size = sizeof(cpu_states);
for (cpu = 0; cpu < ncpus; ++cpu) {
cpu_states[CP_USER] += cputime_percpu[cpu].cp_user;
cpu_states[CP_NICE] += cputime_percpu[cpu].cp_nice;
cpu_states[CP_SYS] += cputime_percpu[cpu].cp_sys;
cpu_states[CP_INTR] += cputime_percpu[cpu].cp_intr;
cpu_states[CP_IDLE] += cputime_percpu[cpu].cp_idle;
}
error = SYSCTL_OUT(req, cpu_states, size);
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, cp_time, (CTLTYPE_LONG|CTLFLAG_RD), 0, 0,
sysctl_cp_time, "LU", "CPU time statistics");
static int
sysctl_cp_times(SYSCTL_HANDLER_ARGS)
{
long cpu_states[CPUSTATES] = {0};
int cpu, error;
size_t size = sizeof(cpu_states);
for (error = 0, cpu = 0; error == 0 && cpu < ncpus; ++cpu) {
cpu_states[CP_USER] = cputime_percpu[cpu].cp_user;
cpu_states[CP_NICE] = cputime_percpu[cpu].cp_nice;
cpu_states[CP_SYS] = cputime_percpu[cpu].cp_sys;
cpu_states[CP_INTR] = cputime_percpu[cpu].cp_intr;
cpu_states[CP_IDLE] = cputime_percpu[cpu].cp_idle;
error = SYSCTL_OUT(req, cpu_states, size);
}
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, cp_times, (CTLTYPE_LONG|CTLFLAG_RD), 0, 0,
sysctl_cp_times, "LU", "per-CPU time statistics");
__read_mostly struct timespec boottime;
__read_mostly struct timespec ticktime0;
__read_mostly struct timespec ticktime2;
__read_mostly int ticktime_update;
__read_mostly time_t time_second;
__read_mostly time_t time_uptime;
struct hardtime {
__uint32_t time_second;
sysclock_t cpuclock_base;
};
#define BASETIME_ARYSIZE 16
#define BASETIME_ARYMASK (BASETIME_ARYSIZE - 1)
static struct timespec basetime[BASETIME_ARYSIZE];
static struct hardtime hardtime[BASETIME_ARYSIZE];
static volatile int basetime_index;
static int
sysctl_get_basetime(SYSCTL_HANDLER_ARGS)
{
struct timespec *bt;
int error;
int index;
index = basetime_index;
cpu_lfence();
bt = &basetime[index];
error = SYSCTL_OUT(req, bt, sizeof(*bt));
return (error);
}
SYSCTL_STRUCT(_kern, KERN_BOOTTIME, boottime, CTLFLAG_RD,
&boottime, timespec, "System boottime");
SYSCTL_PROC(_kern, OID_AUTO, basetime, CTLTYPE_STRUCT|CTLFLAG_RD, 0, 0,
sysctl_get_basetime, "S,timespec", "System basetime");
static void hardclock(systimer_t info, int, struct intrframe *frame);
static void statclock(systimer_t info, int, struct intrframe *frame);
static void schedclock(systimer_t info, int, struct intrframe *frame);
static void getnanotime_nbt(struct timespec *nbt, struct timespec *tsp);
__read_mostly sbintime_t sbticks;
__read_mostly int ticks;
__read_mostly int sched_ticks;
__read_mostly int clocks_running;
int64_t nsec_adj;
int64_t nsec_acc;
int64_t ntp_tick_permanent;
int64_t ntp_tick_acc;
int64_t ntp_delta;
int64_t ntp_big_delta = 1000000000;
int32_t ntp_tick_delta;
int32_t ntp_default_tick_delta;
time_t ntp_leap_second;
int ntp_leap_insert;
struct spinlock ntp_spin;
static void
initclocks(void *dummy)
{
spin_init(&ntp_spin, "ntp");
initclocks_pcpu();
clocks_running = 1;
if (kpmap) {
kpmap->tsc_freq = tsc_frequency;
kpmap->tick_freq = hz;
}
}
void
initclocks_pcpu(void)
{
struct globaldata *gd = mycpu;
crit_enter();
if (gd->gd_cpuid == 0) {
gd->gd_time_seconds = 1;
gd->gd_cpuclock_base = sys_cputimer->count();
hardtime[0].time_second = gd->gd_time_seconds;
hardtime[0].cpuclock_base = gd->gd_cpuclock_base;
} else {
gd->gd_time_seconds = globaldata_find(0)->gd_time_seconds;
gd->gd_cpuclock_base = globaldata_find(0)->gd_cpuclock_base;
}
systimer_intr_enable();
crit_exit();
}
static
uint64_t
collect_cputime_callback(int n)
{
static long cpu_base[CPUSTATES];
long cpu_states[CPUSTATES];
long total;
long acc;
long lsb;
bzero(cpu_states, sizeof(cpu_states));
for (n = 0; n < ncpus; ++n) {
cpu_states[CP_USER] += cputime_percpu[n].cp_user;
cpu_states[CP_NICE] += cputime_percpu[n].cp_nice;
cpu_states[CP_SYS] += cputime_percpu[n].cp_sys;
cpu_states[CP_INTR] += cputime_percpu[n].cp_intr;
cpu_states[CP_IDLE] += cputime_percpu[n].cp_idle;
}
acc = 0;
for (n = 0; n < CPUSTATES; ++n) {
total = cpu_states[n] - cpu_base[n];
cpu_base[n] = cpu_states[n];
cpu_states[n] = total;
acc += total;
}
if (acc == 0)
acc = 1;
lsb = acc / (10000 * 2);
kcollect_setvalue(KCOLLECT_SYSTPCT,
(cpu_states[CP_SYS] + lsb) * 10000 / acc);
kcollect_setvalue(KCOLLECT_IDLEPCT,
(cpu_states[CP_IDLE] + lsb) * 10000 / acc);
kcollect_setvalue(KCOLLECT_INTRPCT,
(cpu_states[CP_INTR] + lsb) * 10000 / acc);
return((cpu_states[CP_USER] + cpu_states[CP_NICE] + lsb) * 10000 / acc);
}
static
void
initclocks_other(void *dummy)
{
struct globaldata *ogd = mycpu;
struct globaldata *gd;
int n;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
gd = mycpu;
systimer_init_periodic_flags(&gd->gd_statclock, statclock,
NULL, stathz,
SYSTF_MSSYNC | SYSTF_FIRST |
SYSTF_OFFSET50 | SYSTF_OFFSETCPU);
systimer_init_periodic_flags(&gd->gd_hardclock, hardclock,
NULL, hz,
SYSTF_MSSYNC | SYSTF_OFFSETCPU);
}
lwkt_setcpu_self(ogd);
kcollect_register(KCOLLECT_USERPCT, "user", collect_cputime_callback,
KCOLLECT_SCALE(KCOLLECT_USERPCT_FORMAT, 0));
kcollect_register(KCOLLECT_SYSTPCT, "syst", NULL,
KCOLLECT_SCALE(KCOLLECT_SYSTPCT_FORMAT, 0));
kcollect_register(KCOLLECT_IDLEPCT, "idle", NULL,
KCOLLECT_SCALE(KCOLLECT_IDLEPCT_FORMAT, 0));
}
SYSINIT(clocks2, SI_BOOT2_POST_SMP, SI_ORDER_ANY, initclocks_other, NULL);
static
void
initclocks_usched(void *dummy)
{
struct globaldata *ogd = mycpu;
struct globaldata *gd;
int n;
for (n = 0; n < ncpus; ++n) {
lwkt_setcpu_self(globaldata_find(n));
gd = mycpu;
systimer_init_periodic_flags(&gd->gd_schedclock, schedclock,
NULL, ESTCPUFREQ,
SYSTF_MSSYNC | SYSTF_OFFSETCPU);
}
lwkt_setcpu_self(ogd);
}
SYSINIT(clocks3, SI_BOOT2_USCHED, SI_ORDER_ANY, initclocks_usched, NULL);
void
set_timeofday(struct timespec *ts)
{
struct timespec *nbt;
int ni;
crit_enter();
ni = (basetime_index + 1) & BASETIME_ARYMASK;
cpu_lfence();
nbt = &basetime[ni];
nanouptime(nbt);
nbt->tv_sec = ts->tv_sec - nbt->tv_sec;
nbt->tv_nsec = ts->tv_nsec - nbt->tv_nsec;
if (nbt->tv_nsec < 0) {
nbt->tv_nsec += 1000000000;
--nbt->tv_sec;
}
spin_lock(&ntp_spin);
boottime.tv_sec = nbt->tv_sec;
boottime.tv_nsec = nbt->tv_nsec;
ntp_delta = 0;
cpu_sfence();
basetime_index = ni;
spin_unlock(&ntp_spin);
crit_exit();
}
static void
hardclock(systimer_t info, int in_ipi, struct intrframe *frame)
{
sysclock_t cputicks;
struct proc *p;
struct globaldata *gd = mycpu;
if ((gd->gd_reqflags & RQF_IPIQ) == 0 && lwkt_need_ipiq_process(gd)) {
need_ipiq();
}
if (gd->gd_cpuid == 0) {
int ni;
cputicks = info->time - gd->gd_cpuclock_base;
if (cputicks >= sys_cputimer->freq) {
cputicks /= sys_cputimer->freq;
if (cputicks > 1) {
kprintf("Warning: hardclock missed "
"%ju seconds\n", (uintmax_t)cputicks);
}
gd->gd_time_seconds += cputicks;
gd->gd_cpuclock_base += sys_cputimer->freq * cputicks;
time_uptime += cputicks;
}
ni = (basetime_index + 1) & BASETIME_ARYMASK;
hardtime[ni].time_second = gd->gd_time_seconds;
hardtime[ni].cpuclock_base = gd->gd_cpuclock_base;
} else {
int ni;
ni = basetime_index;
cpu_lfence();
gd->gd_time_seconds = hardtime[ni].time_second;
gd->gd_cpuclock_base = hardtime[ni].cpuclock_base;
}
if (gd->gd_cpuid == 0) {
struct timespec *nbt;
struct timespec nts;
int leap;
int ni;
++ticks;
++sbticks;
nanotime(&nts);
atomic_add_int_nonlocked(&ticktime_update, 1);
cpu_sfence();
if (ticktime_update & 2)
ticktime2 = nts;
else
ticktime0 = nts;
cpu_sfence();
atomic_add_int_nonlocked(&ticktime_update, 1);
#if 0
if (tco->tc_poll_pps)
tco->tc_poll_pps(tco);
#endif
ni = (basetime_index + 1) & BASETIME_ARYMASK;
nbt = &basetime[ni];
*nbt = basetime[basetime_index];
spin_lock(&ntp_spin);
if (ntp_delta != 0) {
nbt->tv_nsec += ntp_tick_delta;
ntp_delta -= ntp_tick_delta;
if ((ntp_delta > 0 && ntp_delta < ntp_tick_delta) ||
(ntp_delta < 0 && ntp_delta > ntp_tick_delta)) {
ntp_tick_delta = ntp_delta;
}
}
if (ntp_tick_permanent != 0) {
ntp_tick_acc += ntp_tick_permanent;
if (ntp_tick_acc >= (1LL << 32)) {
nbt->tv_nsec += ntp_tick_acc >> 32;
ntp_tick_acc -= (ntp_tick_acc >> 32) << 32;
} else if (ntp_tick_acc <= -(1LL << 32)) {
nbt->tv_nsec -= (-ntp_tick_acc) >> 32;
ntp_tick_acc += ((-ntp_tick_acc) >> 32) << 32;
}
}
if (nbt->tv_nsec >= 1000000000) {
nbt->tv_sec++;
nbt->tv_nsec -= 1000000000;
} else if (nbt->tv_nsec < 0) {
nbt->tv_sec--;
nbt->tv_nsec += 1000000000;
}
if (nsec_adj != 0) {
nsec_acc += nsec_adj;
if (nsec_acc >= 0x100000000LL) {
nbt->tv_nsec += nsec_acc >> 32;
nsec_acc = (nsec_acc & 0xFFFFFFFFLL);
} else if (nsec_acc <= -0x100000000LL) {
nbt->tv_nsec -= -nsec_acc >> 32;
nsec_acc = -(-nsec_acc & 0xFFFFFFFFLL);
}
if (nbt->tv_nsec >= 1000000000) {
nbt->tv_nsec -= 1000000000;
++nbt->tv_sec;
} else if (nbt->tv_nsec < 0) {
nbt->tv_nsec += 1000000000;
--nbt->tv_sec;
}
}
spin_unlock(&ntp_spin);
getnanotime_nbt(nbt, &nts);
if (time_second != nts.tv_sec) {
if (ntp_leap_second) {
if (ntp_leap_second == nts.tv_sec) {
if (ntp_leap_insert) {
nbt->tv_sec++;
nts.tv_sec++;
} else {
nbt->tv_sec--;
nts.tv_sec--;
}
ntp_leap_second--;
}
}
leap = ntp_update_second(time_second, &nsec_adj);
nsec_adj /= hz;
nbt->tv_sec += leap;
nts.tv_sec += leap;
time_second = nts.tv_sec;
curthread->td_wakefromcpu = -1;
}
cpu_sfence();
basetime_index = ni;
if (kpmap) {
int w;
w = (kpmap->upticks + 1) & 1;
getnanouptime(&kpmap->ts_uptime[w]);
getnanotime(&kpmap->ts_realtime[w]);
cpu_sfence();
++kpmap->upticks;
cpu_sfence();
}
{
globaldata_t gd;
int n;
for (n = 0; n < ncpus; ++n) {
gd = globaldata_find(n);
if (gd->gd_exisarmed == 0)
break;
}
if (n == ncpus) {
for (n = 0; n < ncpus; ++n) {
gd = globaldata_find(n);
gd->gd_exisarmed = 0;
}
++pseudo_ticks;
}
}
}
lwkt_schedulerclock(curthread);
if (ticks & 1) {
exis_hold_gd(gd);
exis_drop_gd(gd);
}
hardclock_softtick(gd);
vmstats_rollup_cpu(gd);
vfscache_rollup_cpu(gd);
mycpu->gd_vmstats = vmstats;
if ((p = curproc) != NULL && lwkt_trytoken(&p->p_token)) {
crit_enter_hard();
if (p->p_upmap)
++p->p_upmap->runticks;
if (frame && CLKF_USERMODE(frame) &&
timevalisset(&p->p_timer[ITIMER_VIRTUAL].it_value) &&
itimerdecr(&p->p_timer[ITIMER_VIRTUAL], ustick) == 0) {
p->p_flags |= P_SIGVTALRM;
need_user_resched();
}
if (timevalisset(&p->p_timer[ITIMER_PROF].it_value) &&
itimerdecr(&p->p_timer[ITIMER_PROF], ustick) == 0) {
p->p_flags |= P_SIGPROF;
need_user_resched();
}
crit_exit_hard();
lwkt_reltoken(&p->p_token);
}
setdelayed();
}
static void
statclock(systimer_t info, int in_ipi, struct intrframe *frame)
{
globaldata_t gd = mycpu;
thread_t td;
struct proc *p;
int bump;
sysclock_t cv;
sysclock_t scv;
cv = sys_cputimer->count();
scv = gd->statint.gd_statcv;
if (scv == 0) {
bump = 1;
} else {
bump = muldivu64(sys_cputimer->freq64_usec,
(cv - scv), 1L << 32);
if (bump < 0)
bump = 0;
if (bump > 1000000)
bump = 1000000;
}
gd->statint.gd_statcv = cv;
#if 0
stv = &gd->gd_stattv;
if (stv->tv_sec == 0) {
bump = 1;
} else {
bump = tv.tv_usec - stv->tv_usec +
(tv.tv_sec - stv->tv_sec) * 1000000;
if (bump < 0)
bump = 0;
if (bump > 1000000)
bump = 1000000;
}
*stv = tv;
#endif
td = curthread;
p = td->td_proc;
if ((td->td_flags & TDF_CLKTHREAD) && td->td_preempted)
td = td->td_preempted;
if (frame && CLKF_USERMODE(frame)) {
if (p && (p->p_flags & P_PROFIL))
addupc_intr(p, CLKF_PC(frame), 1);
td->td_uticks += bump;
if (p && p->p_nice > NZERO)
cpu_time.cp_nice += bump;
else
cpu_time.cp_user += bump;
} else {
int intr_nest = gd->gd_intr_nesting_level;
if (in_ipi) {
--intr_nest;
}
if ((frame && CLKF_INTR(intr_nest)) ||
cpu_interrupt_running(td)) {
td->td_iticks += bump;
#ifdef DEBUG_PCTRACK
if (frame)
do_pctrack(frame, PCTRACK_INT);
#endif
cpu_time.cp_intr += bump;
} else if (gd->gd_flags & GDF_VIRTUSER) {
td->td_uticks += bump;
if (p && p->p_nice > NZERO)
cpu_time.cp_nice += bump;
else
cpu_time.cp_user += bump;
} else {
if (clock_debug2 > 0) {
--clock_debug2;
kprintf("statclock preempt %s (%p %p)\n", td->td_comm, td, &gd->gd_idlethread);
}
td->td_sticks += bump;
if (td == &gd->gd_idlethread) {
if (gd->gd_reqflags &
(RQF_IDLECHECK_WK_MASK & ~RQF_INTPEND)) {
cpu_time.cp_sys += bump;
} else {
cpu_time.cp_idle += bump;
}
} else {
#ifdef DEBUG_PCTRACK
if (frame)
do_pctrack(frame, PCTRACK_SYS);
#endif
cpu_time.cp_sys += bump;
}
}
}
}
#ifdef DEBUG_PCTRACK
static void
do_pctrack(struct intrframe *frame, int which)
{
struct kinfo_pctrack *pctrack;
pctrack = &cputime_pctrack[mycpu->gd_cpuid][which];
pctrack->pc_array[pctrack->pc_index & PCTRACK_ARYMASK] =
(void *)CLKF_PC(frame);
++pctrack->pc_index;
}
static int
sysctl_pctrack(SYSCTL_HANDLER_ARGS)
{
struct kinfo_pcheader head;
int error;
int cpu;
int ntrack;
head.pc_ntrack = PCTRACK_SIZE;
head.pc_arysize = PCTRACK_ARYSIZE;
if ((error = SYSCTL_OUT(req, &head, sizeof(head))) != 0)
return (error);
for (cpu = 0; cpu < ncpus; ++cpu) {
for (ntrack = 0; ntrack < PCTRACK_SIZE; ++ntrack) {
error = SYSCTL_OUT(req, &cputime_pctrack[cpu][ntrack],
sizeof(struct kinfo_pctrack));
if (error)
break;
}
if (error)
break;
}
return (error);
}
SYSCTL_PROC(_kern, OID_AUTO, pctrack, (CTLTYPE_OPAQUE|CTLFLAG_RD), 0, 0,
sysctl_pctrack, "S,kinfo_pcheader", "CPU PC tracking");
#endif
static void
schedclock(systimer_t info, int in_ipi __unused, struct intrframe *frame)
{
struct lwp *lp;
struct rusage *ru;
struct vmspace *vm;
long rss;
if ((lp = lwkt_preempted_proc()) != NULL) {
++lp->lwp_cpticks;
usched_schedulerclock(lp, info->periodic, info->time);
} else {
usched_schedulerclock(NULL, info->periodic, info->time);
}
if ((lp = curthread->td_lwp) != NULL) {
if ((ru = &lp->lwp_proc->p_ru) &&
(vm = lp->lwp_proc->p_vmspace) != NULL) {
ru->ru_ixrss += pgtok(btoc(vm->vm_tsize));
ru->ru_idrss += pgtok(btoc(vm->vm_dsize));
ru->ru_isrss += pgtok(btoc(vm->vm_ssize));
if (lwkt_trytoken(&vm->vm_map.token)) {
rss = pgtok(vmspace_resident_count(vm));
if (ru->ru_maxrss < rss)
ru->ru_maxrss = rss;
lwkt_reltoken(&vm->vm_map.token);
}
}
}
if (mycpu->gd_cpuid == 0)
++sched_ticks;
}
int
tvtohz_high(struct timeval *tv)
{
int ticks;
long sec, usec;
sec = tv->tv_sec;
usec = tv->tv_usec;
if (usec < 0) {
sec--;
usec += 1000000;
}
if (sec < 0) {
#ifdef DIAGNOSTIC
if (usec > 0) {
sec++;
usec -= 1000000;
}
kprintf("tvtohz_high: negative time difference "
"%ld sec %ld usec\n",
sec, usec);
#endif
ticks = 1;
} else if (sec <= INT_MAX / hz) {
ticks = (int)(sec * hz + howmany((u_long)usec, ustick)) + 1;
} else {
ticks = INT_MAX;
}
return (ticks);
}
int
tstohz_high(struct timespec *ts)
{
int ticks;
long sec, nsec;
sec = ts->tv_sec;
nsec = ts->tv_nsec;
if (nsec < 0) {
sec--;
nsec += 1000000000;
}
if (sec < 0) {
#ifdef DIAGNOSTIC
if (nsec > 0) {
sec++;
nsec -= 1000000000;
}
kprintf("tstohz_high: negative time difference "
"%ld sec %ld nsec\n",
sec, nsec);
#endif
ticks = 1;
} else if (sec <= INT_MAX / hz) {
ticks = (int)(sec * hz + howmany((u_long)nsec, nstick)) + 1;
} else {
ticks = INT_MAX;
}
return (ticks);
}
int
tvtohz_low(struct timeval *tv)
{
int ticks;
long sec;
sec = tv->tv_sec;
if (sec <= INT_MAX / hz)
ticks = (int)(sec * hz + (u_long)tv->tv_usec / ustick);
else
ticks = INT_MAX;
return (ticks);
}
int
tstohz_low(struct timespec *ts)
{
int ticks;
long sec;
sec = ts->tv_sec;
if (sec <= INT_MAX / hz)
ticks = (int)(sec * hz + (u_long)ts->tv_nsec / nstick);
else
ticks = INT_MAX;
return (ticks);
}
void
startprofclock(struct proc *p)
{
if ((p->p_flags & P_PROFIL) == 0) {
p->p_flags |= P_PROFIL;
#if 0
if (++profprocs == 1 && stathz != 0) {
crit_enter();
psdiv = psratio;
setstatclockrate(profhz);
crit_exit();
}
#endif
}
}
void
stopprofclock(struct proc *p)
{
if (p->p_flags & P_PROFIL) {
p->p_flags &= ~P_PROFIL;
#if 0
if (--profprocs == 0 && stathz != 0) {
crit_enter();
psdiv = 1;
setstatclockrate(stathz);
crit_exit();
}
#endif
}
}
static int
sysctl_kern_clockrate(SYSCTL_HANDLER_ARGS)
{
struct kinfo_clockinfo clkinfo;
clkinfo.ci_hz = hz;
clkinfo.ci_tick = ustick;
clkinfo.ci_tickadj = ntp_default_tick_delta / 1000;
clkinfo.ci_profhz = profhz;
clkinfo.ci_stathz = stathz ? stathz : hz;
return (sysctl_handle_opaque(oidp, &clkinfo, sizeof clkinfo, req));
}
SYSCTL_PROC(_kern, KERN_CLOCKRATE, clockrate, CTLTYPE_STRUCT|CTLFLAG_RD,
0, 0, sysctl_kern_clockrate, "S,clockinfo","");
void
getmicrouptime(struct timeval *tvp)
{
struct globaldata *gd = mycpu;
sysclock_t delta;
do {
tvp->tv_sec = gd->gd_time_seconds;
delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
} while (tvp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tvp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tvp->tv_usec = muldivu64(sys_cputimer->freq64_usec, delta, 1L << 32);
if (tvp->tv_usec >= 1000000) {
tvp->tv_usec -= 1000000;
++tvp->tv_sec;
}
}
void
getnanouptime(struct timespec *tsp)
{
struct globaldata *gd = mycpu;
sysclock_t delta;
do {
tsp->tv_sec = gd->gd_time_seconds;
delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
} while (tsp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tsp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tsp->tv_nsec = muldivu64(sys_cputimer->freq64_nsec, delta, 1L << 32);
}
void
microuptime(struct timeval *tvp)
{
struct globaldata *gd = mycpu;
sysclock_t delta;
do {
tvp->tv_sec = gd->gd_time_seconds;
delta = sys_cputimer->count() - gd->gd_cpuclock_base;
} while (tvp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tvp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tvp->tv_usec = muldivu64(sys_cputimer->freq64_usec, delta, 1L << 32);
}
void
nanouptime(struct timespec *tsp)
{
struct globaldata *gd = mycpu;
sysclock_t delta;
do {
tsp->tv_sec = gd->gd_time_seconds;
delta = sys_cputimer->count() - gd->gd_cpuclock_base;
} while (tsp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tsp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tsp->tv_nsec = muldivu64(sys_cputimer->freq64_nsec, delta, 1L << 32);
}
void
getmicrotime(struct timeval *tvp)
{
struct timespec ts;
int counter;
do {
counter = *(volatile int *)&ticktime_update;
cpu_lfence();
switch(counter & 3) {
case 0:
ts = ticktime2;
break;
case 1:
ts = ticktime2;
break;
case 2:
ts = ticktime0;
break;
case 3:
ts = ticktime0;
break;
}
cpu_lfence();
} while (counter != *(volatile int *)&ticktime_update);
tvp->tv_sec = ts.tv_sec;
tvp->tv_usec = ts.tv_nsec / 1000;
}
void
getnanotime(struct timespec *tsp)
{
struct timespec ts;
int counter;
do {
counter = *(volatile int *)&ticktime_update;
cpu_lfence();
switch(counter & 3) {
case 0:
ts = ticktime2;
break;
case 1:
ts = ticktime2;
break;
case 2:
ts = ticktime0;
break;
case 3:
ts = ticktime0;
break;
}
cpu_lfence();
} while (counter != *(volatile int *)&ticktime_update);
*tsp = ts;
}
static void
getnanotime_nbt(struct timespec *nbt, struct timespec *tsp)
{
struct globaldata *gd = mycpu;
sysclock_t delta;
do {
tsp->tv_sec = gd->gd_time_seconds;
delta = gd->gd_hardclock.time - gd->gd_cpuclock_base;
} while (tsp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tsp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tsp->tv_nsec = muldivu64(sys_cputimer->freq64_nsec, delta, 1L << 32);
tsp->tv_sec += nbt->tv_sec;
tsp->tv_nsec += nbt->tv_nsec;
while (tsp->tv_nsec >= 1000000000) {
tsp->tv_nsec -= 1000000000;
++tsp->tv_sec;
}
}
void
microtime(struct timeval *tvp)
{
struct globaldata *gd = mycpu;
struct timespec *bt;
sysclock_t delta;
do {
tvp->tv_sec = gd->gd_time_seconds;
delta = sys_cputimer->count() - gd->gd_cpuclock_base;
} while (tvp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tvp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tvp->tv_usec = muldivu64(sys_cputimer->freq64_usec, delta, 1L << 32);
bt = &basetime[basetime_index];
cpu_lfence();
tvp->tv_sec += bt->tv_sec;
tvp->tv_usec += bt->tv_nsec / 1000;
while (tvp->tv_usec >= 1000000) {
tvp->tv_usec -= 1000000;
++tvp->tv_sec;
}
}
void
nanotime(struct timespec *tsp)
{
struct globaldata *gd = mycpu;
struct timespec *bt;
sysclock_t delta;
do {
tsp->tv_sec = gd->gd_time_seconds;
delta = sys_cputimer->count() - gd->gd_cpuclock_base;
} while (tsp->tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
tsp->tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
tsp->tv_nsec = muldivu64(sys_cputimer->freq64_nsec, delta, 1L << 32);
bt = &basetime[basetime_index];
cpu_lfence();
tsp->tv_sec += bt->tv_sec;
tsp->tv_nsec += bt->tv_nsec;
while (tsp->tv_nsec >= 1000000000) {
tsp->tv_nsec -= 1000000000;
++tsp->tv_sec;
}
}
time_t
get_approximate_time_t(void)
{
struct globaldata *gd = mycpu;
struct timespec *bt;
bt = &basetime[basetime_index];
return(gd->gd_time_seconds + bt->tv_sec);
}
static int
pps_fetch_timeout(struct timespec *timeout, struct pps_state *pps)
{
int to, err;
pps_seq_t *ap, *cp;
pps_seq_t a, c;
to = INT_MAX;
if (timeout->tv_sec > -1)
to = tstohz_low(timeout);
ap = &pps->ppsinfo.assert_sequence;
cp = &pps->ppsinfo.clear_sequence;
a = atomic_load_acq_int(ap);
c = atomic_load_acq_int(cp);
while (a == atomic_load_acq_int(ap) && c == atomic_load_acq_int(cp)) {
err = tsleep(pps, PCATCH, "ppsfch", to);
if (err == EWOULDBLOCK) {
if (timeout->tv_sec < 0)
continue;
return (ETIMEDOUT);
}
if (err != 0)
return (err);
}
return (0);
}
int
pps_ioctl(u_long cmd, caddr_t data, struct pps_state *pps)
{
pps_params_t *app;
struct pps_fetch_args *fapi;
#ifdef PPS_SYNC
struct pps_kcbind_args *kapi;
#endif
int err;
switch (cmd) {
case PPS_IOC_CREATE:
return (0);
case PPS_IOC_DESTROY:
return (0);
case PPS_IOC_SETPARAMS:
app = (pps_params_t *)data;
if (app->mode & ~pps->ppscap)
return (EINVAL);
pps->ppsparam = *app;
return (0);
case PPS_IOC_GETPARAMS:
app = (pps_params_t *)data;
*app = pps->ppsparam;
app->api_version = PPS_API_VERS_1;
return (0);
case PPS_IOC_GETCAP:
*(int*)data = pps->ppscap;
return (0);
case PPS_IOC_FETCH:
fapi = (struct pps_fetch_args *)data;
if (fapi->tsformat && fapi->tsformat != PPS_TSFMT_TSPEC)
return (EINVAL);
if (fapi->timeout.tv_sec != 0 || fapi->timeout.tv_nsec != 0) {
err = pps_fetch_timeout(&fapi->timeout, pps);
if (err != 0)
return (err);
}
pps->ppsinfo.current_mode = pps->ppsparam.mode;
fapi->pps_info_buf = pps->ppsinfo;
return (0);
case PPS_IOC_KCBIND:
#ifdef PPS_SYNC
kapi = (struct pps_kcbind_args *)data;
if (kapi->tsformat && kapi->tsformat != PPS_TSFMT_TSPEC)
return (EINVAL);
if (kapi->kernel_consumer != PPS_KC_HARDPPS)
return (EINVAL);
if (kapi->edge & ~pps->ppscap)
return (EINVAL);
pps->kcmode = kapi->edge;
return (0);
#else
return (EOPNOTSUPP);
#endif
default:
return (ENOTTY);
}
}
void
pps_init(struct pps_state *pps)
{
pps->ppscap |= PPS_TSFMT_TSPEC | PPS_CANWAIT;
if (pps->ppscap & PPS_CAPTUREASSERT)
pps->ppscap |= PPS_OFFSETASSERT;
if (pps->ppscap & PPS_CAPTURECLEAR)
pps->ppscap |= PPS_OFFSETCLEAR;
}
void
pps_event(struct pps_state *pps, sysclock_t count, int event)
{
struct globaldata *gd;
struct timespec *tsp;
struct timespec *osp;
struct timespec *bt;
struct timespec ts;
sysclock_t *pcount;
#ifdef PPS_SYNC
sysclock_t tcount;
#endif
sysclock_t delta;
pps_seq_t *pseq;
int foff;
#ifdef PPS_SYNC
int fhard;
#endif
int ni;
gd = mycpu;
if (event == PPS_CAPTUREASSERT) {
tsp = &pps->ppsinfo.assert_timestamp;
osp = &pps->ppsparam.assert_offset;
foff = pps->ppsparam.mode & PPS_OFFSETASSERT;
#ifdef PPS_SYNC
fhard = pps->kcmode & PPS_CAPTUREASSERT;
#endif
pcount = &pps->ppscount[0];
pseq = &pps->ppsinfo.assert_sequence;
} else {
tsp = &pps->ppsinfo.clear_timestamp;
osp = &pps->ppsparam.clear_offset;
foff = pps->ppsparam.mode & PPS_OFFSETCLEAR;
#ifdef PPS_SYNC
fhard = pps->kcmode & PPS_CAPTURECLEAR;
#endif
pcount = &pps->ppscount[1];
pseq = &pps->ppsinfo.clear_sequence;
}
if (*pcount == count)
return;
*pcount = count;
do {
ts.tv_sec = gd->gd_time_seconds;
delta = count - gd->gd_cpuclock_base;
} while (ts.tv_sec != gd->gd_time_seconds);
if (delta >= sys_cputimer->freq) {
ts.tv_sec += delta / sys_cputimer->freq;
delta %= sys_cputimer->freq;
}
ts.tv_nsec = muldivu64(sys_cputimer->freq64_nsec, delta, 1L << 32);
ni = basetime_index;
cpu_lfence();
bt = &basetime[ni];
ts.tv_sec += bt->tv_sec;
ts.tv_nsec += bt->tv_nsec;
while (ts.tv_nsec >= 1000000000) {
ts.tv_nsec -= 1000000000;
++ts.tv_sec;
}
atomic_add_rel_int(pseq, 1);
*tsp = ts;
if (foff) {
timespecadd(tsp, osp, tsp);
if (tsp->tv_nsec < 0) {
tsp->tv_nsec += 1000000000;
tsp->tv_sec -= 1;
}
}
#ifdef PPS_SYNC
if (fhard) {
tcount = count - pps->ppscount[2];
pps->ppscount[2] = count;
if (tcount >= sys_cputimer->freq) {
delta = (1000000000 * (tcount / sys_cputimer->freq) +
sys_cputimer->freq64_nsec *
(tcount % sys_cputimer->freq)) >> 32;
} else {
delta = muldivu64(sys_cputimer->freq64_nsec,
tcount, 1L << 32);
}
hardpps(tsp, delta);
}
#endif
wakeup(pps);
}
tsc_uclock_t
tsc_get_target(int ns)
{
#if defined(_RDTSC_SUPPORTED_)
if (cpu_feature & CPUID_TSC) {
return (rdtsc() + tsc_frequency * ns / (int64_t)1000000000);
}
#endif
return(-1);
}
int
tsc_test_target(int64_t target)
{
#if defined(_RDTSC_SUPPORTED_)
if (cpu_feature & CPUID_TSC) {
if ((int64_t)(target - rdtsc()) <= 0)
return(1);
return(0);
}
#endif
return(-1);
}
void
tsc_delay(int ns)
{
int64_t clk;
clk = tsc_get_target(ns);
cpu_pause();
cpu_pause();
while (tsc_test_target(clk) == 0) {
cpu_pause();
cpu_pause();
cpu_pause();
cpu_pause();
}
}