分类: LINUX
2013-06-05 22:12:52
从Linux 2.4内核开始引入的softirq机制将bottom_half重新进行了实现,进而可以充分利用现在常见的SMP架构,因为不同的softirq可以在不同的CPU上同时进行,只是一个CPU上只能有同一种softirq的一个实例在运行。而同一种类型的softirq的不同实例可以同时在不同的CPU上运行,因此softirq执行的函数必须对共享的数据结构有相应的锁机制,以避免竞争的发生。当前实现了10种softirq,支持32种softirq:
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
NR_SOFTIRQS
};
其中原来的bottom_half机制通过HI_SOFTIRQ实现,tasklet机制由TASKLET_SOFTIRQ实现。NET_TX_SOFTIRQ和NET_RX_SOFTIRQ是专用于网络系统实现。
在softirq执行期间,中断功能是开启的,因而可以随机挂起处理新来的中断事件。
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);