Linux中断下半部处理机制-tasklet
转载原文:https://www.cnblogs.com/Wangzx000/p/17488378.html
核心函数
linux\include\linux\interrupt.h
static inline void tasklet_schedule(struct tasklet_struct *t)
抓住核心函数就是抓住了使用的主要矛盾,tasklet机制的核心函数就是 tasklet_schedule(),这个函数用在tasklet需要被调度执行的地方,那到底执行什么内容呢?就需要传过来的参数 tasklet_struct
tasklet的数据结构
struct tasklet_struct
{
struct tasklet_struct *next; // 将多个tasklet链接成单向循环链表
unsigned long state; //TASKLET_STATE_SCHED(Tasklet is scheduled for execution) TASKLET_STATE_RUN(Tasklet is running (SMP only))
atomic_t count; // 0:激活tasklet 非0:禁用tasklet
void (*func)(unsigned long); // 用户自定义函数
unsigned long data; // 函数入参
};
这里面最重要的就是 func和data,这个就是tasklet执行时调用的函数和他的参数。我们要使用tasklet来执行任务,就需要提前设置好任务函数func和他的参数data,然后使用 tasklet_schedule() 把这个任务挂载到tasklet执行列表里面,就会被自动执行。
内核中有一些API可以辅助tasklet_struct来绑定func和data。
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_init使用案例
struct ieee80211_hw *ieee80211_alloc_hw(size_t priv_data_len,
const struct ieee80211_ops *ops)
{
tasklet_init(&local->tx_pending_tasklet, ieee80211_tx_pending,
(unsigned long)local);
}
tasklet特性
(1)一种特定类型的tasklet只能运行在一个CPU上,不能并行,只能串行执行
(2)多个不同的类型的tasklet可以并行在多个CPU上
(3)软中断是静态分配的,在内核编译好后,就不能再改变了。但tasklet灵活很多,可以在运行时改变
tasklet是在两种软中断类型的基础上实现的,因此如果不需要软中断的并行也行,tasklet就是最好的选择。所以也可以说tasklet是软中断的一种特殊用法,即延迟情况下的串行执行。
tasklet链表
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec); // 低优先级
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);// 高优先级
tasklet API接口
// 定义名字为name的非激活tasklet
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
// 定义名字为name的激活tasklet
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
//动态初始化tasklet
void tasklet_init(struct tasklet_struct *t,
void (*func)(unsigned long), unsigned long data);
// 函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出
static inline void tasklet_disable(struct tasklet_struct *t);
// 函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出
static inline void tasklet_enable(struct tasklet_struct *t);
// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
static inline void tasklet_schedule(struct tasklet_struct *t);
// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
static inline void tasklet_hi_schedule(struct tasklet_struct *t);
// 调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
void tasklet_kill(struct tasklet_struct *t);
tasklet原理
tasklet调度原理
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
__tasklet_schedule_common(t, &tasklet_vec,
TASKLET_SOFTIRQ);
}
static void __tasklet_schedule_common(struct tasklet_struct *t,
struct tasklet_head __percpu *headp,
unsigned int softirq_nr)
{
struct tasklet_head *head;
unsigned long flags;
local_irq_save(flags);
head = this_cpu_ptr(headp);
t->next = NULL;
*head->tail = t;
head->tail = &(t->next); // 加入tasklet列表
raise_softirq_irqoff(softirq_nr); // 触发软中断
local_irq_restore(flags);
}
tasklet执行过程
TASKLET_SOFTIRQ对应执行函数为tasklet_action,HI_SOFTIRQ为tasklet_hi_action,以tasklet_action为例:
static __latent_entropy void tasklet_action(struct softirq_action *a)
{
tasklet_action_common(a, this_cpu_ptr(&tasklet_vec), TASKLET_SOFTIRQ);
}
static void tasklet_action_common(struct softirq_action *a,
struct tasklet_head *tl_head,
unsigned int softirq_nr)
{
struct tasklet_struct *list;
local_irq_disable();
list = tl_head->head;
tl_head->head = NULL;
tl_head->tail = &tl_head->head; // 获取tasklet链表
local_irq_enable();
while (list) {
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t)) {
if (!atomic_read(&t->count)) {
// 执行tasklet
if (!test_and_clear_bit(TASKLET_STATE_SCHED,
&t->state))
BUG();
t->func(t->data);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
// 如果t->count的值不等于0,说明这个tasklet在调度之后,被disable掉了,所以会将tasklet结构体重新放回到tasklet_vec链表,并重新调度TASKLET_SOFTIRQ软中断,在之后enable这个tasklet之后重新再执行它
local_irq_disable();
t->next = NULL;
*tl_head->tail = t;
tl_head->tail = &t->next;
__raise_softirq_irqoff(softirq_nr);
local_irq_enable();
}
}