タスクレット
タスクレットのサンプルです。invoke_virtual_irq()は実際は割り込みハンドラとしてコールされます。割り込み処理で負荷のある実装はタスクレットとして実装することで、割り込みの取りこぼしが防げます。
#include <linux/kernel.h> #include <linux/module.h> #include <linux/interrupt.h> MODULE_LICENSE("GPL"); void hoge_fun(unsigned long data) { printk("%s\n", (char *)data); } DECLARE_TASKLET(hoge_tasklet, hoge_fun, 0); void invoke_virtual_irq(void) { hoge_tasklet.data = (unsigned long)"hoge by tasklet"; tasklet_schedule(&hoge_tasklet); } int init_module(void) { invoke_virtual_irq(); printk("after tasklet_schedule\n"); return 0; } void cleanup_module(void) { tasklet_kill(&hoge_tasklet); }動作
[root@localhost lkm]# dmesg -C [root@localhost lkm]# insmod tasklet.ko [root@localhost lkm]# dmesg [ 3626.655334] after tasklet_schedule [ 3626.655346] hoge by taskletDECLARE_TASKLETマクロでt->state=0です。TASKLET_STATE_SCHEDは多重コール防止フラグです。
#define DECLARE_TASKLET(name, func, data) \ struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data } struct tasklet_struct { struct tasklet_struct *next; unsigned long state; atomic_t count; void (*func)(unsigned long); unsigned long data; }; static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_schedule(t); }tasklet_vec.tailにtasklet_structをリストし、TASKLET_SOFTIRQでraise_softirq_irqoff()コールすることで、tasklet_action()がコールされます。
void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); t->next = NULL; *__this_cpu_read(tasklet_vec.tail) = t; __this_cpu_write(tasklet_vec.tail, &(t->next)); raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_restore(flags); }softirq_init()でopen_softirq(TASKLET_SOFTIRQ, tasklet_action)とし、tasklet_action()をタスクレットソフト割込みコールバックを設定します。
void __init softirq_init(void) { int cpu; for_each_possible_cpu(cpu) { int i; per_cpu(tasklet_vec, cpu).tail = &per_cpu(tasklet_vec, cpu).head; per_cpu(tasklet_hi_vec, cpu).tail = &per_cpu(tasklet_hi_vec, cpu).head; for (i = 0; i < NR_SOFTIRQS; i++) INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu)); } register_hotcpu_notifier(&remote_softirq_cpu_notifier); open_softirq(TASKLET_SOFTIRQ, tasklet_action); open_softirq(HI_SOFTIRQ, tasklet_hi_action); }tasklet_vec.headをヘッドとするリスト要素のtasklet_structのfuncを順次コールします。
static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; local_irq_disable(); list = __this_cpu_read(tasklet_vec.head); __this_cpu_write(tasklet_vec.head, NULL); __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head); local_irq_enable(); while (list) { struct tasklet_struct *t = list; list = list->next; if (tasklet_trylock(t)) { if (!atomic_read(&t->count)) { if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) BUG(); t->func(t->data); tasklet_unlock(t); continue; } tasklet_unlock(t); } local_irq_disable(); t->next = NULL; *__this_cpu_read(tasklet_vec.tail) = t; __this_cpu_write(tasklet_vec.tail, &(t->next)); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable(); } }