softirq函数是通过open_softirq函数注册的,其实就是在一个全局数据中设置对应的执行函数:
struct softirq_action
{
void (*action)(struct softirq_action *);
};
static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
void open_softirq(int nr, void (*action)(struct softirq_action *))
{
softirq_vec[nr].action = action;}
与softirq调度相关的函数包括:
__raise_softirq_irqoff:设置要执行的软softirq相关联的位标识,稍后检查这个标识,相关联的函数就会被调用。
static inline void __raise_softirq_irqoff(unsigned int nr)
{
trace_softirq_raise(nr);
or_softirq_pending(1UL << nr);}
#define or_softirq_pending(x) (local_softirq_pending() |= (x)) //位与上相关位,在当前CPU的变量上设置,谁触发谁执行机制
raise_softirq_irqoff:对__raise_softirq_irqoff函数进行调用封装,然后检查当前函数是不是从硬件或软件中断环境中调用,若不是则唤醒ksoftirqd线程(每个CPU都有自己的ksoftirqd内核线程),执行softirq。若是则不需要调用此线程,因为从中断中返回会调用do_softirq函数。
inline void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
/*
* If we're in an interrupt or softirq, we're done
* (this also catches softirq-disabled code). We will
* actually run the softirq once we return from
* the irq or softirq.
*
* Otherwise we wake up ksoftirqd to make sure we
* schedule the softirq soon.
*/
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);
}
raise_softirq:对raise_softirq_irqoff函数的封装调用,但执行时会关闭中断功能。
void raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
softirq调用会被执行的地点出了主动触发和ksoftirqd内核线程调度外,还包括:
irq_exit:从中断返回时。
/*
* 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);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
rcu_irq_exit();
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
tick_nohz_stop_sched_tick(0);
#endif
preempt_enable_no_resched();//打开可抢占}
static inline void invoke_softirq(void)
{
if (!force_irqthreads)
do_softirq();
else
wakeup_softirqd();
}
在netif_rx_ni函数中也会调用softirq:
int netif_rx_ni(struct sk_buff *skb)
{
int err;
preempt_disable(); //关闭可抢占
err = netif_rx(skb);
if (local_softirq_pending())
do_softirq(); preempt_enable(); //打卡可抢占
return err;
}
EXPORT_SYMBOL(netif_rx_ni);