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个函数:

1tasklet_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的初始化,主要是对funcdatacount进行赋值

2DECLARE_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操作。在非SMPtasklet_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位分别是bit0bit1位,其中bit 1TASKLET_STATE_RUN即用于SMP,主要就是用来防止多个CPU同时执行同一个tasklet的。

Logo

更多推荐