深入理解Linux网络技术内幕-中断和网络驱动程序(三)

6330阅读 0评论2013-06-05 visualfan
分类:LINUX

          从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);

上一篇:深入理解Linux网络技术内幕-中断和网络驱动程序(二)
下一篇:深入理解Linux网络技术内幕-中断和网络驱动程序(四)