linux 4.6.3版本代码
假如写驱动的时候我们需要用到tasklet,那么首先需要分配一个新的tasklet_struct数据结构并初始化它,然后我们要把tasklet加入到总的tasklet链表中,最后要使得softirq可以调用它来执行驱动要做的工作,老规矩我们按照这个逻辑来组织本文。
分配一个新的tasklet并初始化
内核中用struct tasklet_struct来表示一个tasklet
struct tasklet_struct
{
struct tasklet_struct *next;-----指向链表中下一个tasklet
unsigned long state;-------------tasklet状态
atomic_t count;------------------锁计数器,tasklet_enable/tasklet_disable函数会修改此字段
void (*func)(unsigned long);-----tasklet函数指针
unsigned long data;--------------函数参数
};
我们可以使用下面的宏来定义自己的tasklet,也可以使用函数tasklet_init()来初始化
#define DECLARE_TASKLET(name, func, data) \--------------初始化时enable自己的tasklet
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \-----初始化时disable自己的tasklet
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
把tasklet加入到总链表中
总tasklet
内核会为每个cpu都分配一个tasklet链表,定义如下
struct tasklet_head {
struct tasklet_struct *head;
struct tasklet_struct **tail;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
把自己的tasklet加入到总tasklet中
我们需要把自己的tasklet加入到总链表中,这里分两种tasklet_schedule和tasklet_hi_schedule,这两种分别对应tasklet和hi_tasklet,其实执行步骤差不多,我们来看看tasklet_schedule
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;-----------加入总链表tasklet_vec
__this_cpu_write(tasklet_vec.tail, &(t->next));
raise_softirq_irqoff(TASKLET_SOFTIRQ);------------激活softirq/tasklet
local_irq_restore(flags);
}
tasklet是如何执行并调用到自定义的函数
初始化softirq的tasklet
tasklet的初始化在函数softirq_init中,这个函数是在start_kernel即系统初始化时调用的
void __init softirq_init(void)
{
int cpu;
for_each_possible_cpu(cpu) {
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;
}
open_softirq(TASKLET_SOFTIRQ, tasklet_action);----初始化tasklet的执行函数为tasklet_action
open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}
tasklet执行步骤
从上面可以看到tasklet的执行函数为tasklet_action,softirq最终会调用此函数
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, this_cpu_ptr(&tasklet_vec.head));
local_irq_enable();
while (list) {---进行链表循环调用每一个tasklet_struct
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的函数func
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();
}
}