#include <sys/param.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
#include <sys/queue.h>
#include <machine/intr.h>
#include <uvm/uvm_extern.h>
#ifdef __USE_MI_SOFTINTR
struct softintr_hand {
TAILQ_ENTRY(softintr_hand) sih_q;
void (*sih_fn)(void *);
void *sih_arg;
struct cpu_info *sih_runner;
int sih_level;
unsigned short sih_flags;
unsigned short sih_state;
};
#define SIF_MPSAFE 0x0001
#define SIS_DYING 0x0001
#define SIS_PENDING 0x0002
#define SIS_RESTART 0x0004
TAILQ_HEAD(softintr_queue, softintr_hand);
struct softintr_queue softintr_queue[NSOFTINTR];
struct mutex softintr_lock = MUTEX_INITIALIZER(IPL_HIGH);
void
softintr_init(void)
{
int i;
for (i = 0; i < nitems(softintr_queue); i++)
TAILQ_INIT(&softintr_queue[i]);
}
void
softintr_dispatch(int level)
{
struct cpu_info *ci = curcpu();
struct softintr_queue *queue = &softintr_queue[level];
struct softintr_hand *sih;
mtx_enter(&softintr_lock);
while ((sih = TAILQ_FIRST(queue)) != NULL) {
KASSERT((sih->sih_state & (SIS_PENDING | SIS_RESTART)) ==
SIS_PENDING);
KASSERT(sih->sih_runner == NULL);
sih->sih_state &= ~SIS_PENDING;
TAILQ_REMOVE(queue, sih, sih_q);
sih->sih_runner = ci;
mtx_leave(&softintr_lock);
if (sih->sih_flags & SIF_MPSAFE) {
(*sih->sih_fn)(sih->sih_arg);
} else {
KERNEL_LOCK();
(*sih->sih_fn)(sih->sih_arg);
KERNEL_UNLOCK();
}
mtx_enter(&softintr_lock);
KASSERT((sih->sih_state & SIS_PENDING) == 0);
sih->sih_runner = NULL;
if (sih->sih_state & SIS_RESTART) {
TAILQ_INSERT_TAIL(queue, sih, sih_q);
sih->sih_state |= SIS_PENDING;
sih->sih_state &= ~SIS_RESTART;
}
uvmexp.softs++;
}
mtx_leave(&softintr_lock);
}
void *
softintr_establish(int ipl, void (*func)(void *), void *arg)
{
struct softintr_hand *sih;
int level;
unsigned short flags = 0;
if (ipl & IPL_MPSAFE)
flags |= SIF_MPSAFE;
ipl &= ~IPL_MPSAFE;
switch (ipl) {
#ifdef IPL_SOFT
case IPL_SOFT:
#endif
case IPL_SOFTCLOCK:
level = SOFTINTR_CLOCK;
break;
case IPL_SOFTNET:
level = SOFTINTR_NET;
break;
case IPL_TTY:
case IPL_SOFTTTY:
level = SOFTINTR_TTY;
break;
default:
panic("softintr_establish: unhandled ipl %d", ipl);
}
sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT);
if (sih != NULL) {
sih->sih_fn = func;
sih->sih_arg = arg;
sih->sih_runner = NULL;
sih->sih_level = level;
sih->sih_flags = flags;
sih->sih_state = 0;
}
return (sih);
}
void
softintr_disestablish(void *arg)
{
struct cpu_info *runner;
struct softintr_hand *sih = arg;
assertwaitok();
mtx_enter(&softintr_lock);
sih->sih_state |= SIS_DYING;
sih->sih_state &= ~SIS_RESTART;
if (sih->sih_state & SIS_PENDING) {
sih->sih_state &= ~SIS_PENDING;
TAILQ_REMOVE(&softintr_queue[sih->sih_level], sih, sih_q);
}
runner = sih->sih_runner;
mtx_leave(&softintr_lock);
if (runner != NULL)
sched_barrier(runner);
KASSERT((sih->sih_state & (SIS_PENDING | SIS_RESTART)) == 0);
KASSERT(sih->sih_runner == NULL);
free(sih, M_DEVBUF, sizeof(*sih));
}
void
softintr_schedule(void *arg)
{
struct softintr_hand *sih = arg;
struct softintr_queue *queue = &softintr_queue[sih->sih_level];
mtx_enter(&softintr_lock);
KASSERT((sih->sih_state & SIS_DYING) == 0);
if (sih->sih_runner == NULL) {
KASSERT((sih->sih_state & SIS_RESTART) == 0);
if ((sih->sih_state & SIS_PENDING) == 0) {
TAILQ_INSERT_TAIL(queue, sih, sih_q);
sih->sih_state |= SIS_PENDING;
softintr(sih->sih_level);
}
} else {
KASSERT((sih->sih_state & SIS_PENDING) == 0);
sih->sih_state |= SIS_RESTART;
}
mtx_leave(&softintr_lock);
}
#endif