#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/thread.h>
static TAILQ_HEAD(, intr_config_hook) intr_config_hook_list =
TAILQ_HEAD_INITIALIZER(intr_config_hook_list);
static void run_interrupt_driven_config_hooks (void *dummy);
static int ran_config_hooks;
static struct lock intr_config_lk = LOCK_INITIALIZER("intrcfg", 0, 0);
static int intr_config_hook_delay_ms = 5000;
TUNABLE_INT("kern.intr_config_hook_delay_ms", &intr_config_hook_delay_ms);
static void
run_interrupt_driven_config_hooks(void *dummy)
{
struct intr_config_hook *hook_entry;
int save_ticks = ticks;
int save_count;
int waiting;
lockmgr(&intr_config_lk, LK_EXCLUSIVE);
save_count = ran_config_hooks++;
while (!TAILQ_EMPTY(&intr_config_hook_list)) {
TAILQ_FOREACH(hook_entry, &intr_config_hook_list, ich_links) {
if (hook_entry->ich_ran == 0) {
hook_entry->ich_ran = 1;
lockmgr(&intr_config_lk, LK_RELEASE);
(*hook_entry->ich_func)(hook_entry->ich_arg);
lockmgr(&intr_config_lk, LK_EXCLUSIVE);
break;
}
}
if (hook_entry)
continue;
if (TAILQ_EMPTY(&intr_config_hook_list))
break;
waiting = (ticks - save_ticks + 1) / hz;
if (waiting >= 10 && (waiting % 10) == 0) {
kprintf("**WARNING** waiting for the following device "
"to finish configuring:\n");
TAILQ_FOREACH(hook_entry, &intr_config_hook_list,
ich_links) {
kprintf(" %s:\tfunc=%p arg=%p\n",
(hook_entry->ich_desc ?
hook_entry->ich_desc : "?"),
hook_entry->ich_func,
hook_entry->ich_arg);
}
if (save_count || waiting >= 30) {
kprintf("Giving up, interrupt routing is "
"probably hosed\n");
break;
}
}
lksleep(&intr_config_hook_list, &intr_config_lk,
0, "conifhk", hz);
++waiting;
}
lockmgr(&intr_config_lk, LK_RELEASE);
#ifndef _KERNEL_VIRTUAL
if (save_count == 0 && intr_config_hook_delay_ms > 0) {
while (ticks - save_ticks < intr_config_hook_delay_ms * hz / 1000)
tsleep(&intr_config_hook_list, 0, "delay", hz / 10);
}
#endif
}
SYSINIT(intr_config_hooks, SI_SUB_INT_CONFIG_HOOKS, SI_ORDER_FIRST,
run_interrupt_driven_config_hooks, NULL);
int
config_intrhook_establish(struct intr_config_hook *hook)
{
struct intr_config_hook *hook_entry;
lockmgr(&intr_config_lk, LK_EXCLUSIVE);
for (hook_entry = TAILQ_FIRST(&intr_config_hook_list);
hook_entry != NULL;
hook_entry = TAILQ_NEXT(hook_entry, ich_links)) {
if (hook_entry == hook)
break;
if (hook_entry->ich_order > hook->ich_order)
break;
}
if (hook_entry == hook) {
kprintf("config_intrhook_establish: establishing an "
"already established hook.\n");
lockmgr(&intr_config_lk, LK_RELEASE);
return (1);
}
hook->ich_ran = 0;
if (hook_entry)
TAILQ_INSERT_BEFORE(hook_entry, hook, ich_links);
else
TAILQ_INSERT_TAIL(&intr_config_hook_list, hook, ich_links);
if (ran_config_hooks) {
lockmgr(&intr_config_lk, LK_RELEASE);
run_interrupt_driven_config_hooks(NULL);
lockmgr(&intr_config_lk, LK_EXCLUSIVE);
}
lockmgr(&intr_config_lk, LK_RELEASE);
return (0);
}
void
config_intrhook_disestablish(struct intr_config_hook *hook)
{
struct intr_config_hook *hook_entry;
lockmgr(&intr_config_lk, LK_EXCLUSIVE);
for (hook_entry = TAILQ_FIRST(&intr_config_hook_list);
hook_entry != NULL;
hook_entry = TAILQ_NEXT(hook_entry, ich_links)) {
if (hook_entry == hook)
break;
}
if (hook_entry == NULL) {
lockmgr(&intr_config_lk, LK_RELEASE);
panic("config_intrhook_disestablish: disestablishing an "
"unestablished hook (%p)", hook);
}
TAILQ_REMOVE(&intr_config_hook_list, hook, ich_links);
wakeup(&intr_config_hook_list);
lockmgr(&intr_config_lk, LK_RELEASE);
}