#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/thread.h>
#include <sys/globaldata.h>
#include <sys/systimer.h>
#include <sys/thread2.h>
void
systimer_intr(sysclock_t *timep, int in_ipi, struct intrframe *frame)
{
globaldata_t gd = mycpu;
sysclock_t time = *timep;
systimer_t info;
if (gd->gd_syst_nest)
return;
crit_enter();
++gd->gd_syst_nest;
while ((info = TAILQ_FIRST(&gd->gd_systimerq)) != NULL) {
if ((ssysclock_t)(info->time - time) > 0) {
cputimer_intr_reload(info->time - time);
break;
}
info->flags &= ~SYSTF_ONQUEUE;
TAILQ_REMOVE(info->queue, info, node);
gd->gd_systimer_inprog = info;
crit_exit();
info->func(info, in_ipi, frame);
crit_enter();
if (gd->gd_systimer_inprog == info && info->periodic) {
if (info->which != sys_cputimer) {
info->periodic = sys_cputimer->fromhz(info->freq);
info->which = sys_cputimer;
}
info->time += info->periodic;
if ((info->flags & SYSTF_NONQUEUED) &&
(ssysclock_t)(info->time - time) <= 0
) {
info->time += roundup(time - info->time, info->periodic);
}
systimer_add(info);
}
gd->gd_systimer_inprog = NULL;
}
--gd->gd_syst_nest;
crit_exit();
}
void
systimer_intr_enable(void)
{
cputimer_intr_enable();
}
void
systimer_add(systimer_t info)
{
struct globaldata *gd = mycpu;
KKASSERT((info->flags & SYSTF_ONQUEUE) == 0);
crit_enter();
if (info->gd == gd) {
systimer_t scan1;
systimer_t scan2;
scan1 = TAILQ_FIRST(&gd->gd_systimerq);
if (scan1 == NULL || (ssysclock_t)(scan1->time - info->time) > 0) {
cputimer_intr_reload(info->time - sys_cputimer->count());
TAILQ_INSERT_HEAD(&gd->gd_systimerq, info, node);
} else {
scan2 = TAILQ_LAST(&gd->gd_systimerq, systimerq);
for (;;) {
if (scan1 == NULL) {
TAILQ_INSERT_TAIL(&gd->gd_systimerq, info, node);
break;
}
if (info->flags & SYSTF_FIRST) {
if ((ssysclock_t)(scan1->time - info->time) >= 0) {
TAILQ_INSERT_BEFORE(scan1, info, node);
break;
}
if ((ssysclock_t)(scan2->time - info->time) < 0) {
TAILQ_INSERT_AFTER(&gd->gd_systimerq, scan2,
info, node);
break;
}
} else {
if ((ssysclock_t)(scan1->time - info->time) > 0) {
TAILQ_INSERT_BEFORE(scan1, info, node);
break;
}
if ((ssysclock_t)(scan2->time - info->time) <= 0) {
TAILQ_INSERT_AFTER(&gd->gd_systimerq, scan2,
info, node);
break;
}
}
scan1 = TAILQ_NEXT(scan1, node);
scan2 = TAILQ_PREV(scan2, systimerq, node);
}
}
info->flags = (info->flags | SYSTF_ONQUEUE) & ~SYSTF_IPIRUNNING;
info->queue = &gd->gd_systimerq;
} else {
KKASSERT((info->flags & SYSTF_IPIRUNNING) == 0);
info->flags |= SYSTF_IPIRUNNING;
lwkt_send_ipiq(info->gd, (ipifunc1_t)systimer_add, info);
}
crit_exit();
}
void
systimer_del(systimer_t info)
{
struct globaldata *gd = info->gd;
KKASSERT(gd == mycpu && (info->flags & SYSTF_IPIRUNNING) == 0);
crit_enter();
if (info->flags & SYSTF_ONQUEUE) {
TAILQ_REMOVE(info->queue, info, node);
info->flags &= ~SYSTF_ONQUEUE;
}
if (gd->gd_systimer_inprog == info)
gd->gd_systimer_inprog = NULL;
crit_exit();
}
static __inline
void
_systimer_init_periodic(systimer_t info, systimer_func_t func, void *data,
int64_t freq, int flags)
{
sysclock_t base_count;
if (sys_cputimer->sync_base == 0)
sys_cputimer->sync_base = sys_cputimer->count();
bzero(info, sizeof(struct systimer));
if ((flags & SYSTF_100KHZSYNC) && freq <= 100000)
info->periodic = sys_cputimer->fromhz(100000) * (100000 / freq);
if ((flags & SYSTF_MSSYNC) && freq <= 1000)
info->periodic = sys_cputimer->fromhz(1000) * (1000 / freq);
else
info->periodic = sys_cputimer->fromhz(freq);
base_count = sys_cputimer->count();
base_count = base_count -
(base_count - sys_cputimer->sync_base) % info->periodic;
info->time = base_count + info->periodic;
if (flags & SYSTF_OFFSETCPU)
info->time += mycpu->gd_cpuid * info->periodic / ncpus;
if (flags & SYSTF_OFFSET50)
info->time += info->periodic / 2;
info->func = func;
info->data = data;
info->freq = freq;
info->which = sys_cputimer;
info->gd = mycpu;
info->flags |= flags;
systimer_add(info);
}
void
systimer_init_periodic(systimer_t info, systimer_func_t func, void *data,
int64_t freq)
{
_systimer_init_periodic(info, func, data, freq, 0);
}
void
systimer_init_periodic_nq(systimer_t info, systimer_func_t func, void *data,
int64_t freq)
{
_systimer_init_periodic(info, func, data, freq, SYSTF_NONQUEUED);
}
void
systimer_init_periodic_nq1khz(systimer_t info, systimer_func_t func,
void *data, int64_t freq)
{
_systimer_init_periodic(info, func, data, freq,
SYSTF_NONQUEUED | SYSTF_MSSYNC);
}
void
systimer_init_periodic_nq100khz(systimer_t info, systimer_func_t func,
void *data, int64_t freq)
{
_systimer_init_periodic(info, func, data, freq,
SYSTF_NONQUEUED | SYSTF_100KHZSYNC);
}
void
systimer_init_periodic_flags(systimer_t info, systimer_func_t func,
void *data, int64_t freq, int flags)
{
_systimer_init_periodic(info, func, data, freq, flags);
}
void
systimer_adjust_periodic(systimer_t info, int64_t freq)
{
crit_enter();
info->periodic = sys_cputimer->fromhz(freq);
info->freq = freq;
info->which = sys_cputimer;
crit_exit();
}
void
systimer_init_oneshot(systimer_t info, systimer_func_t func,
void *data, int64_t us)
{
bzero(info, sizeof(struct systimer));
info->time = sys_cputimer->count() + sys_cputimer->fromus(us);
info->func = func;
info->data = data;
info->which = sys_cputimer;
info->gd = mycpu;
info->us = us;
systimer_add(info);
}
static void
systimer_changed_pcpu(void *arg __unused)
{
globaldata_t gd = mycpu;
systimer_t info;
crit_enter();
again:
TAILQ_FOREACH(info, &gd->gd_systimerq, node) {
if (info->which == sys_cputimer)
continue;
TAILQ_REMOVE(&gd->gd_systimerq, info, node);
info->flags &= ~SYSTF_ONQUEUE;
if (info->periodic) {
_systimer_init_periodic(info, info->func, info->data,
info->freq, info->flags);
} else {
info->time = sys_cputimer->count() +
sys_cputimer->fromus(info->us);
systimer_add(info);
}
goto again;
}
cputimer_intr_reload(1);
crit_exit();
}
void
systimer_changed(void)
{
globaldata_t gd = mycpu;
int i;
systimer_changed_pcpu(NULL);
for (i = 0; i < ncpus; ++i) {
if (i != gd->gd_cpuid) {
lwkt_send_ipiq(globaldata_find(i),
systimer_changed_pcpu, NULL);
}
}
}