#include <linux/interrupt.h>
#include <linux/slab.h>
#include <sys/kthread.h>
struct tasklet_entry {
struct tasklet_struct *ts;
STAILQ_ENTRY(tasklet_entry) tasklet_entries;
};
static struct lock tasklet_lock = LOCK_INITIALIZER("dltll", 0, LK_CANRECURSE);
static struct thread *tasklet_td = NULL;
STAILQ_HEAD(tasklet_list_head, tasklet_entry) tlist = STAILQ_HEAD_INITIALIZER(tlist);
STAILQ_HEAD(tasklet_hi_list_head, tasklet_entry) tlist_hi = STAILQ_HEAD_INITIALIZER(tlist_hi);
static int tasklet_pending = 0;
#define PROCESS_TASKLET_LIST(which_list) do { \
STAILQ_FOREACH_MUTABLE(te, &which_list, tasklet_entries, tmp_te) { \
struct tasklet_struct *t = te->ts; \
\
if (test_bit(TASKLET_IS_DYING, &t->state)) { \
STAILQ_REMOVE(&which_list, te, tasklet_entry, tasklet_entries); \
kfree(te); \
} \
\
\
if (atomic_read(&t->count) != 0) \
continue; \
\
\
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) \
continue; \
\
if (!tasklet_trylock(t)) \
continue; \
\
lockmgr(&tasklet_lock, LK_RELEASE); \
if (t->func) \
t->func(t->data); \
lockmgr(&tasklet_lock, LK_EXCLUSIVE); \
\
tasklet_unlock(t); \
} \
} while (0)
static void
tasklet_runner(void *arg)
{
struct tasklet_entry *te, *tmp_te;
lockmgr(&tasklet_lock, LK_EXCLUSIVE);
while (1) {
if (tasklet_pending == 0) {
lksleep(&tasklet_runner, &tasklet_lock, 0, "tkidle", 0);
}
tasklet_pending = 0;
PROCESS_TASKLET_LIST(tlist_hi);
PROCESS_TASKLET_LIST(tlist);
}
lockmgr(&tasklet_lock, LK_RELEASE);
}
void
tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->state = 0;
t->func = func;
t->data = data;
atomic_set(&t->count, 0);
}
#define TASKLET_SCHEDULE_COMMON(t, list) do { \
struct tasklet_entry *te; \
\
lockmgr(&tasklet_lock, LK_EXCLUSIVE); \
if (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) \
goto skip; \
\
STAILQ_FOREACH(te, &(list), tasklet_entries) { \
if (te->ts == t) \
goto found_and_done; \
} \
\
te = kmalloc(sizeof(struct tasklet_entry), M_DRM, M_CACHEALIGN | M_INTWAIT); \
te->ts = t; \
STAILQ_INSERT_TAIL(&(list), te, tasklet_entries); \
\
found_and_done: \
tasklet_pending = 1; \
wakeup(&tasklet_runner); \
skip: \
lockmgr(&tasklet_lock, LK_RELEASE); \
} while (0)
void
tasklet_schedule(struct tasklet_struct *t)
{
TASKLET_SCHEDULE_COMMON(t, tlist);
}
void
tasklet_hi_schedule(struct tasklet_struct *t)
{
TASKLET_SCHEDULE_COMMON(t, tlist_hi);
}
void
tasklet_kill(struct tasklet_struct *t)
{
set_bit(TASKLET_IS_DYING, &t->state);
wakeup(&tasklet_runner);
tasklet_unlock_wait(t);
}
int
tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &t->state);
}
void
tasklet_unlock(struct tasklet_struct *t)
{
clear_bit(TASKLET_STATE_RUN, &t->state);
}
void
tasklet_unlock_wait(struct tasklet_struct *t)
{
int ident = 0;
while (test_bit(TASKLET_STATE_RUN, &t->state)) {
tsleep(&ident, 0, "tletunlock", 1);
}
}
static int init_tasklets(void *arg)
{
kthread_create(tasklet_runner, NULL, &tasklet_td, "tasklet_runner");
return 0;
}
SYSINIT(linux_tasklet_init, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, init_tasklets, NULL);