linux kernel 工程师
全部博文(99)
分类: LINUX
2014-02-05 17:20:41
1. softirq执行的上下文
softirq执行的上下文是中断上下文,也就是中断服务程序即将结束时。 irq_exit 会调用invoke_softirq();
/*
* Exit an interrupt context. Process softirqs if needed and possible:
*/
void irq_exit(void)
{
account_system_vtime(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
/* 这里判断in_interrupt,保证softirq不会嵌套。
假如在执行softirq时,硬中断发生,此softirq被中断执行,并且假如硬中断执行过程中raise了新的softirq,在硬中断执行完毕后,虽然发现有新的softirq pending,但在发现
in_interrupt() 中有softirq还有没有完成,本次就不会再次调用invoke_softirq。当硬件中断执行完毕后, softirq通过弾栈继续执行
*/
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
tick_nohz_irq_exit();
#endif
rcu_irq_exit();
sched_preempt_enable_no_resched();
}
static inline void invoke_softirq(void)
{
if (!force_irqthreads) {
#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
__do_softirq();
#else
do_softirq();
#endif
} else {
__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET);
wakeup_softirqd();
__local_bh_enable(SOFTIRQ_OFFSET);
}
}
2. __do_softirq的分析
调用__do_softirq之前需要保证中断是关闭的
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
pending = local_softirq_pending(); /* pending 的bit记入栈中,下面会清空全局变量里面的pending值 */
account_system_vtime(current);
__local_bh_disable((unsigned long)__builtin_return_address(0),
SOFTIRQ_OFFSET); /* 此处禁止softirq,防止softirq嵌套 */
lockdep_softirq_enter();
cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0); /* pending 的bit已经记入栈中,可以清空softirq全局变量里面的pending,
这里注意irq是关闭的, 清空pending值是安全的,因为不会有中断打断这过程从而也不会有新的pengding产生 */
local_irq_enable(); /* 开启本地中断,保证中断的最高优先级,即软中断可以被irq打断 */
h = softirq_vec;
do {
if (pending & 1) {
unsigned int vec_nr = h - softirq_vec;
int prev_count = preempt_count();
kstat_incr_softirqs_this_cpu(vec_nr);
trace_softirq_entry(vec_nr);
h->action(h);
trace_softirq_exit(vec_nr);
/* callback函数不应破坏抢占计数 */
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %u %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", vec_nr,
softirq_to_name[vec_nr], h->action,
prev_count, preempt_count());
preempt_count() = prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = local_softirq_pending(); /* 如果软中断被中断打断, 并且中断执行过程中raise了新的软中断,当中断退出后,此软中断重新执行,到这里需要重新读取softirqpending,到restart处重新开始处理softirq, 这样顺手就把新raise的softirq处理了, 但是restart的过程不能太多了,防止影响task的调度 */
if (pending && --max_restart)
goto restart;
if (pending)
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
__local_bh_enable(SOFTIRQ_OFFSET); /* 此处使能softirq */
}
3. do_softirq的分析
如果在irq没有关闭的环境下,比如在线程环境下执行softirq,需要要使用本函数。本函数只是__do_softirq的封装,只是会在调用__do_softirq()前关闭中断,结束后打开中断。
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
if (in_interrupt())
return;
local_irq_save(flags); /* 在调用__do_softirq()前关闭中断,结束后打开中断 */
/* 注意__do_softirq();中会有开中断,又关中断的操作 */
pending = local_softirq_pending();
if (pending)
__do_softirq();
local_irq_restore(flags);
}