タスクレット
Rev.1を表示中。最新版はこちら。
タスクレットは割り込み処理を遅延するものです。ソフト割り込みと何が違うかと言えば、タスクレットはソフト割り込みから呼ばれるもので、そもそもソフト割り込みの上位アプリケーションと言う感じでしょうか。ソフト割り込みは静的に割り込みIDが決められて、ユーザが新規に登録することはできません。しかしタスクレットはリスト構造をしていて、ユーザが新規に追加することが可能です。ユーザがタスクレットを使う場合DECLARE_TASKLETを使ってtasklet_struct構造体を初期化します。
include/linux/interrupt.h struct tasklet_struct { struct tasklet_struct *next; <-次にタスクレット構造体 unsigned long state; <-タスクレットの状態 atomic_t count; <-0なら許可、1なら禁止 void (*func)(unsigned long); <-処理関数 unsigned long data; <-引数 };そしてtasklet_hi_schedule/tasklet_schedule関数を呼ぶことで、タスクレットを起動することができます。ユーザはこれだけでタスクレット処理を記述することができるわけです。なおtasklet_hi_schedule関数はHI_SOFTIRQのソフト割り込みを、tasklet_schedule関数はTASKLET_SOFTIRQのソフト割り込みを呼ぶだけの違いです。
tasklet_hi_schedule/tasklet_schedule関数では、まずそのタスクレットすでにスケージューリングされているかチェックしています。二重登録防止です。タスクレットがスケジューリングされてということは、CPU変数のtasklet_vecにtasklet_struct構造体がリストされているかと言うことです。そうでないなら__tasklet_schedule/__tasklet_hi_schedule関数が呼ばれます。
include/linux/interrupt.h static inline void tasklet_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_schedule(t); } static inline void tasklet_hi_schedule(struct tasklet_struct *t) { if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) __tasklet_hi_schedule(t); }__tasklet_schedule/__tasklet_hi_schedule関数では、tasklet_struct 構造体をCPU変数のtasklet_vecにリスト追加して、TASKLET_SOFTIRQ/HI_SOFTIRQでraise_softirq_irqoff関数が呼んでいます。
kernel/softirq.c void __tasklet_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); t->next = NULL; *__get_cpu_var(tasklet_vec).tail = t; __get_cpu_var(tasklet_vec).tail = &(t->next); raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_restore(flags); } kernel/softirq.c void __tasklet_hi_schedule(struct tasklet_struct *t) { unsigned long flags; local_irq_save(flags); t->next = NULL; *__get_cpu_var(tasklet_hi_vec).tail = t; __get_cpu_var(tasklet_hi_vec).tail = &(t->next); raise_softirq_irqoff(HI_SOFTIRQ); local_irq_restore(flags); }TASKLET_SOFTIRQ/HI_SOFTIRQのソフト割り込みのコールバック処理はtasklet_action/tasklet_hi_action関数となっています。違いは__raise_softirq_irqoff関数でソフト割り込みに起動要求する時、TASKLET_SOFTIRQにするかHI_SOFTIRQにするかだけで、中身は全く同じです。
tasklet_action関数では、CPU変数のtasklet_vecからタスクレットリストを取得し、すべてのリストを走査して各タスクレットをt->func(t->data)として起動しています。なお、タスクレットを起動する前にtasklet_trylock関数でタスクレット状態がTASKLET_STATE_RUNであるかチェックしています。TASKLET_STATE_RUNでないならタスクレット状態をTASKLET_STATE_RUNにして0以外を返し、タスクレットが起動されます。TASKLET_STATE_RUNならそのタスクレットは起動中ということで、そのタスクレットをリストの末尾に繋ぎ変え再度__raise_softirq_irqoff関数を呼ぶことで、次にタスクレットの起動処理となるわけです。tasklet_trylock関数でTASKLET_STATE_RUNがどうかをチェックすることで、タスクレットを再入不可能な処理として作成することができるわけです。
kernel/softirq.c static void tasklet_action(struct softirq_action *a) { struct tasklet_struct *list; local_irq_disable(); list = __get_cpu_var(tasklet_vec).head; __get_cpu_var(tasklet_vec).head = NULL; __get_cpu_var(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; *__get_cpu_var(tasklet_vec).tail = t; __get_cpu_var(tasklet_vec).tail = &(t->next); __raise_softirq_irqoff(TASKLET_SOFTIRQ); local_irq_enable(); } }
include/linux/interrupt.h static inline int tasklet_trylock(struct tasklet_struct *t) { return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state); }