上篇讲完了softirq,今天让我们来看一个softirq应用实例-------tasklet
二、tasklet
1、优点
1、简单易用,在设备驱动中比较常见
2、同softirq一样,tasklet执行期间,中断处于使能
2、特点
1、动态分配
2、同一种tasklet不能同时在多个CPU上运行,但是不同的tasklet,可以同时在多个CPU上运行
3、tasklet种类
1、HI_SOFTIRQ,对应0号softirq
2、TASKLET_SOFTIRQ,对应5号softirq
两者除了调用顺序有先后外(先调用HI_SOFTIRQ,再调用TASKLET_SOFTIRQ),其余均相同
4、数据结构
/* Tasklets --- multithreaded analogue of BHs.
Main feature differing them of generic softirqs: tasklet
is running only on one CPU simultaneously.
Main feature differing them of BHs: different tasklets
may be run simultaneously on different CPUs.
Properties:
* If tasklet_schedule() is called, then tasklet is guaranteed
to be executed on some cpu at least once after this.
* If the tasklet is already scheduled, but its excecution is still not
started, it will be executed only once.
* If this tasklet is already running on another CPU (or schedule is called
from tasklet itself), it is rescheduled for later.
* Tasklet is strictly serialized wrt itself, but not
wrt another tasklets. If client needs some intertask synchronization,
he makes it with spinlocks.
*/
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
/* Tasklets */
struct tasklet_head
{
struct tasklet_struct *list;
};
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec) = { NULL };
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec) = { NULL }
5、使用方法
1、动态分配tasklet_struct
2、tasklet_init()
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data)
{
t->next = NULL;
t->state = 0;
atomic_set(&t->count, 0);
t->func = func;
t->data = data;
}
填充tasklet_struct结构体中各个字段
3、tasklet_schedule()
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
EXPORT_SYMBOL(__tasklet_schedule)
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
1、测试tasklet是否处于TASKLET_STATE_SCHED状态,如果没有处于,则置位state中TASKLET_STATE_SCHED bit,否则直接退出
2、将tasklet_struct结构体指针t赋值给当前CPU对应tasklet_vec中list字段
3、raise_softirq_irqoff()具体实现请参考https://blog.csdn.net/yxw0609131056/article/details/110004548 《深入理解Linux内核》第四章中断和异常(三),将当前CPU对应的irq_cpustat_t中__softirq_pending字段对应的bit TASKLET_SOFTIRQ置位。
4、当系统运行到softirq检查点时,就会调用do_softirq()
do_softirq()具体实现请参考https://blog.csdn.net/yxw0609131056/article/details/110004548 《深入理解Linux内核》第四章中断和异常(三)
5、在__do_softirq()中,就会调用h->action(h),
原来系统在初始化时,已经将action()指定为 tasklet_action()
void __init softirq_init(void)
{
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
6、tasklet_action()
static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = NULL;
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 = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
1、首先获得当前CPU对应tasklet_vec中list字段,赋值给list变量,并将其清空
2、如果list不为空,则说明有tasklet请求产生,需要进一步处理,否则直接退出
3、判断tasklet是否处于TASKLET_STATE_RUN并测试tasklet_struct结构体count字段,如果成功,则清除state中TASKLET_STATE_SCHED bit,否则直接跳到6
4、调用用户注册的func()
5、如果前面tasklet处于TASKLET_STATE_RUN,则清除TASKLET_STATE_RUN bit
6、重新设置当前CPU对应tasklet_vec中list字段,重新产生TASKLET_SOFTIRQ