古老的BH、tasklet、softirq
- 古老的BH:作为一种特殊的tasklet
kernel/softirq.c : static void (*bh_base[32])(void);
struct tasklet_struct bh_task_vec[32];
在softirq_init中,注册32个bh_task_vec的函数指针为 bh_action;
TASKLET_SOFTIRQ : tasklet_action;
HI_SOFTIRQ : tasklet_hi_action;
NET_RX_SOFTIRQ : net_rx_action; [net_dev_init]
NET_TX_SOFTIRQ: net_tx_action;
在sched_init中注册 TIMER_BH的处理函数指针为 timer_bh;
init_bh(TIMER_BH, timer_bh);
对于数据包的接收发送,标志对应的 NET_RX_SOFTIRQ / NET_TX_SOFTIRQ;
对于应用的 tasklet ,标志对应的 TASKLET_SOFTIRQ;
对于古老的BH,例如时钟中断,在do_IRQ -> timer_interrupt -> do_timer_interrupt -> do_timer :
mark_bh(TIMER_BH) -> tasklet_hi_schedule(bh_task_vec+TIMER_BH)
即将 TIMER_BH 所对应的 bh_task_vec 结构体挂接到对应的CPU的 tasklet_hi_vec 链表中,并标志HI_SOFTIRQ。
do_softirq时,tasklet_hi_action 遍历对应CPU上的 tasklet_hi_vec 链表中的 tasklet_struct,执行其相应的action,也就是 bh_action 函数。
bh_action 函数是个包装体,用于实际调用 bh_base 数组中的函数。对于时钟中断也就是 sched_init 中注册的 timer_bh 函数。
void __init softirq_init()
{
int i;
for (i=0; i<32; i++)
tasklet_init(bh_task_vec+i, bh_action, i);
open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);
open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
}
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;
}
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
void fastcall __tasklet_hi_schedule(struct tasklet_struct *t)
{
int cpu = smp_processor_id();
unsigned long flags;
local_irq_save(flags);
t->next = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_restore(flags);
}
static void tasklet_hi_action(struct softirq_action *a)
{
int cpu = smp_processor_id();
struct tasklet_struct *list;
local_irq_disable();
list = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].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 = tasklet_hi_vec[cpu].list;
tasklet_hi_vec[cpu].list = t;
__cpu_raise_softirq(cpu, HI_SOFTIRQ);
local_irq_enable();
}
}
static void bh_action(unsigned long nr)
{
int cpu = smp_processor_id();
if (!spin_trylock(&global_bh_lock))
goto resched;
if (!hardirq_trylock(cpu))
goto resched_unlock;
if (bh_base[nr])
bh_base[nr]();
hardirq_endlock(cpu);
spin_unlock(&global_bh_lock);
return;
resched_unlock:
spin_unlock(&global_bh_lock);
resched:
mark_bh(nr);
}