linux 中断底半部之tasklet分析

Linuxtaskletstudy

一、tasklet作用

二、tasklet相关的数据结构

structtasklet_struct

{

structtasklet_struct*next;

unsignedlongstate;

atomic_tcount;

void(*func)(unsignedlong);

unsignedlongdata;

};

/*Tasklets*/

structtasklet_head

{

structtasklet_struct*head;

structtasklet_struct**tail;

};

该结构即为tasklet的主要数据结构,其中

state:保存tasklet的状态,判断当前tasklet是否已运行、对该tasklet进行加锁操作等,

count:tasklet的开启与关闭。0:enable1:disable

void(*func)(unsignedlong):该tasklet对应的回调处理函数,当我们使用tasklet时,该

函数即需要我们自己定义,用于处理底半部的相关事宜

data:回调处理函数的传参

三、tasklet初始化、

tasklet的初始化有2个函数:

1)tasklet_init

voidtasklet_init(structtasklet_struct*t,

void(*func)(unsignedlong),unsignedlongdata)

{

t->next=NULL;

t->state=0;

atomic_set(&t->count,0);

t->func=func;

t->data=data;

}

该函数主要实现tasklet的初始化,主要是对func、data、count进行赋值

2)DECLARE_TASKLET

#defineDECLARE_TASKLET(name,func,data)\

structtasklet_structname={NULL,0,ATOMIC_INIT(0),func,data}

该宏完成一个tasklet的定义与初始化,可谓一步到位。

四、tasklet对应的软中断注册

因为tasklet是基于软中断的,所以在使用tasklet之前,肯定需要先注册相应的软中断。

在softirq_init,注册了2个软中断TASKLET_SOFTIRQ、HI_SOFTIRQ,这2个软中断即是tasklet对应的(不同的优先级),

五、tasklet的加锁相关的函数

#ifdefCONFIG_SMP

staticinlineinttasklet_trylock(structtasklet_struct*t)

{

return!test_and_set_bit(TASKLET_STATE_RUN,&(t)->state);

}

staticinlinevoidtasklet_unlock(structtasklet_struct*t)

{

smp_mb__before_clear_bit();

clear_bit(TASKLET_STATE_RUN,&(t)->state);

}

staticinlinevoidtasklet_unlock_wait(structtasklet_struct*t)

{

while(test_bit(TASKLET_STATE_RUN,&(t)->state)){barrier();}

}

#else

#definetasklet_trylock(t)1

#definetasklet_unlock_wait(t)do{}while(0)

#definetasklet_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中

staticinlinevoidtasklet_schedule(structtasklet_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(structtasklet_struct*t)

{

unsignedlongflags;

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)将tasklett添加到tasklet_head中

b)调用raise_softirq_irqoff,开启tasklet对应的软中断

c)解除tasklet的锁

若不是:

a)调用tasklet_tryunlock,执行解锁操作。

staticvoidinline

__tasklet_common_schedule(structtasklet_struct*t,structtasklet_head*head,unsignedintnr)

{

if(tasklet_trylock(t)){

again:

/*Wemayhavebeenpreemptedbeforetasklet_trylock

*and__tasklet_actionmayhavealreadyrun.

*Sodoublechecktheschedbitwhilethetakslet

*islockedbeforeaddingittothelist.

*/

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{

/*Thisissubtle.Ifwehitthecornercaseabove

*Itispossiblethatwegetpreemptedrighthere,

*andanothertaskhassuccessfullycalled

*tasklet_schedule(),thenthisfunction,and

*failedonthetrylock.Thuswemustbesure

*beforereleasingthetaskletlock,thatthe

*SCHED_BITisclear.Otherwisethetasklet

*maygetitsSCHED_BITset,butnotaddedtothe

*list

*/

if(!tasklet_tryunlock(t))

gotoagain;

}

}

}

以上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.开启中断

staticvoidtasklet_action(structsoftirq_action*a)

{

structtasklet_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){

structtasklet_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位,其中bit1位TASKLET_STATE_RUN即用于SMP,主要就是用来防止多个CPU同时执行同一个tasklet的。

再怎么风光明媚的自家山川,

linux 中断底半部之tasklet分析

相关文章:

你感兴趣的文章:

标签云: