创建 ksoftirqd
early_initcall(spawn_ksoftirqd);static __init int spawn_ksoftirqd(void)
{
void *cpu = (void *)(long)smp_processor_id();
int err = cpu_callback(&cpu_nfb, CPU_UP_PREPARE, cpu);
cpu_callback(&cpu_nfb, CPU_ONLINE, cpu);
register_cpu_notifier(&cpu_nfb);
return 0;
}
int __cpuinit cpu_callback(struct notifier_block *nfb,
unsigned long action,
void *hcpu)
{
int hotcpu = (unsigned long)hcpu;
struct task_struct *p;
switch (action) {
case CPU_UP_PREPARE:
case CPU_UP_PREPARE_FROZEN:
/*创建 Kthread 并绑定某个CPU,这里给出了 thread funcion 绑定到某个CPU的方法*/
p = kthread_create_on_node(run_ksoftirqd,
hcpu,
cpu_to_node(hotcpu),
"ksoftirqd/%d", hotcpu);
kthread_bind(p, hotcpu);
per_cpu(ksoftirqd, hotcpu) = p;
break;
/*唤醒并绑定某个CPU的Kthread */
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
wake_up_process(per_cpu(ksoftirqd, hotcpu));
break;
}
kthread function: run_ksoftirqd
/*ksoftirqd thread的当有pending的softirq时,调用处理函数*没有pending 事件,则睡眠
**/
int run_ksoftirqd(void * __bind_cpu)
{
__set_current_state(TASK_RUNNING);
while (local_softirq_pending()) {
__do_softirq();
}
set_current_state(TASK_INTERRUPTIBLE);
}
下面的问题是怎样pending位和怎样唤醒ksoftirqd, 当然还有怎样设置一个softirq
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
定义一个 named tasklet_strct:
#define DECLARE_TASKLET(name, func, data) \struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
tasklet_schedule
/*
*test_and_set_bit(int nr, long* addr)*将*addr 的第n位设置成1,并返回原来这一位的值
**/
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
/*将tasklet_struct提交到tasklet_vec*/
void __tasklet_schedule(struct tasklet_struct *t)
{
*__this_cpu_read(tasklet_vec.tail) = t;
__this_cpu_write(tasklet_vec.tail, &(t->next));
raise_softirq_irqoff(TASKLET_SOFTIRQ);
}
/*设置pending并唤醒ksoftirqd thread*/
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
if (!in_interrupt())
wakeup_softirqd();
}
static void wakeup_softirqd(void)
{
/* Interrupts are disabled: no need to stop preemption */
struct task_struct *tsk = __this_cpu_read(ksoftirqd);
if (tsk && tsk->state != TASK_RUNNING)
wake_up_process(tsk);
}
何时调用do_softirq
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/
void irq_exit(void)
{
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
}
static inline void invoke_softirq(void)
{
if (!force_irqthreads)
do_softirq();
}
asmlinkage void do_softirq(void)
{
__u32 pending;
pending = local_softirq_pending();
if (pending)
__do_softirq();
}
__do_softirq唤醒kthread: ksoftirqd
在函数__do_softirq调用所有 pending 的action后,再次判断是否又有新的pending,
如果有则再次处理,但是这个过程最多循环MAX_SOFTIRQ_RESTART :10次。
如果还有则唤醒 进程: ksoftirqd
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
pending = local_softirq_pending();
h = softirq_vec;
do {
if (pending & 1) {
unsigned int vec_nr = h - softirq_vec;
h->action(h);
}
h++;
pending >>= 1;
} while (pending);
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
if (pending)
wakeup_softirqd();
}
总结:
刚看代码有点疑惑,还以为所有的action 都是 ksoftirqd处理的,其实不然,是在中断退出的时候调用的 __do_softirq,在该函数中满足某些条件的时候才调用 kthread.
当然,可以强制使用 kthread的 macro:#define force_irqthreads (0).