linux 中断底半部之tasklet分析
Linux tasklet study一、 tasklet作用二、 tasklet相关的数据结构struct tasklet_struct{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
Linux tasklet study
一、 tasklet作用
二、 tasklet相关的数据结构
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 *head;
struct tasklet_struct **tail;
};
该结构即为tasklet的主要数据结构,其中
state:保存tasklet的状态,判断当前tasklet是否已运行、对该tasklet进行加锁操作等,
count:tasklet的开启与关闭。 0:enable 1:disable
void (*func)(unsigned long):该tasklet对应的回调处理函数,当我们使用tasklet时,该
函数即需要我们自己定义,用于处理底半部的相关事宜
data:回调处理函数的传参
三、 tasklet初始化、
tasklet的初始化有2个函数:
1)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的初始化,主要是对func、data、count进行赋值
2)DECLARE_TASKLET
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
该宏完成一个tasklet的定义与初始化,可谓一步到位。
四、 tasklet对应的软中断注册
因为tasklet是基于软中断的,所以在使用tasklet之前,肯定需要先注册相应的软中断。
在softirq_init,注册了2个软中断TASKLET_SOFTIRQ、HI_SOFTIRQ,这2个软中断即是tasklet对应的(不同的优先级),
五、 tasklet的加锁相关的函数
#ifdef CONFIG_SMP
static inline int tasklet_trylock(struct tasklet_struct *t)
{
return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
}
static inline void tasklet_unlock(struct tasklet_struct *t)
{
smp_mb__before_clear_bit();
clear_bit(TASKLET_STATE_RUN, &(t)->state);
}
static inline void tasklet_unlock_wait(struct tasklet_struct *t)
{
while (test_bit(TASKLET_STATE_RUN, &(t)->state)) { barrier(); }
}
#else
#define tasklet_trylock(t) 1
#define tasklet_unlock_wait(t) do { } while (0)
#define tasklet_unlock(t) do { } while (0)
#endif
在SMP下,上锁函数tasklet_trylock主要是设置tasklet->state 的TASKLET_STATE_RUN位,将该位置为1,上锁的主要功能是保证SMP下在同一时刻只有一个CPU执行相同的tasklet操作。在非SMP下tasklet_trylock不起作用。
六、 tasklet的启用
tasklet_schedule
作用:
1)首先判断tasklet->state 的TASKLET_STATE_SCHED位是否置位,在未置位的情况下,设置该位,并调用__tasklet_schedule(t),将该tasklet加入到表头tasklet_vec中
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
__tasklet_schedule:
作用:
1、 关闭中断
2、 调用__tasklet_common_schedule,实现添加tasklet并开启软中断的功能
3、 开启中断
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags);
__tasklet_common_schedule(t, &__get_cpu_var(tasklet_vec), TASKLET_SOFTIRQ);
local_irq_restore(flags);
}
__tasklet_common_schedule:
作用:
1) 对该tasklet进行上锁操作,在上锁成功以后,执行下面操作
2) 判断当前tasklet的状态是否为TASKLET_STATE_SCHED,
若是则:
a) 将tasklet t添加到tasklet_head中
b) 调用raise_softirq_irqoff,开启tasklet对应的软中断
c) 解除tasklet的锁
若不是:
a) 调用tasklet_tryunlock,执行解锁操作。
static void inline
__tasklet_common_schedule(struct tasklet_struct *t, struct tasklet_head *head, unsigned int nr)
{
if (tasklet_trylock(t)) {
again:
/* We may have been preempted before tasklet_trylock
* and __tasklet_action may have already run.
* So double check the sched bit while the takslet
* is locked before adding it to the list.
*/
if (test_bit(TASKLET_STATE_SCHED, &t->state)) {
t->next = NULL;
*head->tail = t;
head->tail = &(t->next);
raise_softirq_irqoff(nr);
tasklet_unlock(t);
} else {
/* This is subtle. If we hit the corner case above
* It is possible that we get preempted right here,
* and another task has successfully called
* tasklet_schedule(), then this function, and
* failed on the trylock. Thus we must be sure
* before releasing the tasklet lock, that the
* SCHED_BIT is clear. Otherwise the tasklet
* may get its SCHED_BIT set, but not added to the
* list
*/
if (!tasklet_tryunlock(t))
goto again;
}
}
}
以上3个函数实现了将tasklet添加到tasklet_vec(每个CPU都有一个该变量)中,并开启软中断。
七 tasklet的执行
tasklet是依托软中断的,那么tasklet的回调处理函数也一定是在软中断TASKLET_SOFTIRQ、HI_SOFTIRQ的处理函数里调用的。对于软中断TASKLET_SOFTIRQ,其中断处理函数的定义如下:
tasklet_action:
作用:
1) 关闭软中断,获取运行CPU所对应的tasklet链表的表头,然后将表头置为NULL,再启中断
2) 遍历tasklet链表,每次遍历均执行如下操作:
a) 获取tasklet链表的一个tasklet变量
b) 对该tasklet执行加锁操作,即置位TASKLET_STATE_RUN
c) 判断当前tasklet是否使能,若已使能,则执行以下操作
i. tasklet的当前状态若为TASKLET_STATE_SCHED,则清空该位
ii. 调用该tasklet的回调处理函数
iii. 解锁该tasklet,重新while循环
若未使能,则执行以下操作:
i. 关闭中断
ii. 将该tasklet重新加入到链表tasklet_vec
iii. 开启软中断TASKLET_SOFTIRQ,在 下一次处理该软中断时,再处理该tasklet
iv. 开启中断
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();
}
}
问:在同一时刻,多个CPU不能同时执行同一个tasklet的功能是如何实现的
在tasklet的数据结构中,有一个成员选项state,该变量目前有意义的2位分别是bit0、bit1位,其中bit 1位TASKLET_STATE_RUN即用于SMP,主要就是用来防止多个CPU同时执行同一个tasklet的。
更多推荐
所有评论(0)