linux kernel 工程师
全部博文(99)
分类: LINUX
2014-02-05 16:07:25
1. tasklet的特点
a. tasklet是softirq的一种, tasklet只可以在一个CPU上同步地执行, 不同的tasklet可以在不同地CPU上同步地执行。
在softirq中,
HI_SOFTIRQ和 TASKLET_SOFTIRQ是两种tasklet,但实现基本一样。只是HI_SOFTIRQ位于softirq_vec[]的0位置,在__do_softirq函数中,判断local_softirq_pending是从0bit开始的,所以优先级高。
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
看一下softirq_init,
TASKLET_SOFTIRQ, HI_SOFTIRQ两个向量分别安装了tasklet_action和 tasklet_hi_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; /*tasklet_vec的 tail指向&head */
per_cpu(tasklet_hi_vec, cpu).tail =
&per_cpu(tasklet_hi_vec, cpu).head; /* tasklet_hi_vec的 tail指向&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);
}
b. tasklet的串行化使tasklet函数不必是可重入的,因此简化了设备驱动程序开发者的工作
2. tasklet的数据结构
enum
{
TASKLET_STATE_SCHED, /* 1已经被调度,0表示还没调度*/
TASKLET_STATE_RUN /* 1tasklet正在执行,0表示尚未执行,只针对SMP有效,单处理器无意义 */
};
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state; /* 用来记录TASKLET_STATE_SCHED,TASKLET_STATE_RUN */
atomic_t count; /* 用来禁止tasklet */
void (*func)(unsigned long); /* tasklet的callback函数 */
unsigned long data;
};
/*
* Tasklets
*/
struct tasklet_head
{
struct tasklet_struct *head;
struct tasklet_struct **tail; /* 注意tail指向指针的地址 */
};
/* 每个cpu上都有一个tasklet的链表 */
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);
3. 如何调度一个tasklet
/* 调度一个tasklet,就是设置TASKLET_STATE_SCHED bit, 并把一个tasklet_struct放到tasklet_vec的尾部,并触发一次softirq */
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) /* 这个判断保证tasklet只会处于某一个cpu的tasklet_vec上,而不会在多个vec上 */
__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; /* *tail=t */
__this_cpu_write(tasklet_vec.tail, &(t->next)); /* tail=&t->next */
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
4. tasklet如何执行
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
/* 在中断关闭的情况下,摘下本cpu 对应 tasklet_vec链表*/
local_irq_disable();
list = __this_cpu_read(tasklet_vec.head);
__this_cpu_write(tasklet_vec.head, NULL); /* head=NULL */
__this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head); /* tail=&head */
local_irq_enable();
/* 遍历刚刚摘下的链表,如果可以执行,则执行,否则重新放回tasklet_vec */
while (list) {
struct tasklet_struct *t = list; /* 摘下一个tasklet_struct */
list = list->next;
if (tasklet_trylock(t)) { /* 实际上是判断state的TASKLET_STATE_RUN bit,如果该bit已经为1,表示别的cpu正在run这个tasklet。 */
if (!atomic_read(&t->count)) {/* 如果没有禁止 */
if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) /* clear sched bit,clear 之后就可以被其他cpu schedule了,也就是说可以加入别的cpu的tasklet_vec了,但由于此时TASKLET_STATE_RUN bit还没有clear,所以虽然此tasklet已经能够加入到别的cpu的tasklet_vec, 但还必须等到这里 TASKLET_STATE_RUN bit clear之后才能够run*/
BUG();
trace_irq_tasklet_low_entry(t);
t->func(t->data); /* tasklet的callback函数在这里执行 */
trace_irq_tasklet_low_exit(t);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable(); /* 把没有执行的t重新放回尾部,等下次softirq来执行 */
t->next = NULL;
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
__raise_softirq_irqoff(TASKLET_SOFTIRQ); /* 再次触发softirq*/
local_irq_enable();
}
}