• softirq raise/处理


    softirq raise/处理

    调用raise_softirq_irqoff(HRTIMER_SOFTIRQ)触发一个softirq,这个函数会将local_softirq_pending_ref (int型)per cpu变量对应bit置1,如果当前不在interrupt context(softirq或者hardirq或者nmi)下,将会唤醒ksoftirqd/*线程。

    4.19/kernel/softirq.c

    void __raise_softirq_irqoff(unsigned int nr)
    {
        trace_softirq_raise(nr);
        or_softirq_pending(1UL << nr);
    }

    ksoftirqd/*线程唤醒后,将执行run_ksoftirqd()函数,主要看__do_softirq()

    static void run_ksoftirqd(unsigned int cpu)
    {
        local_irq_disable();
        if (local_softirq_pending()) {
            /*
             * We can safely run softirq on inline stack, as we are not deep
             * in the task stack here.
             */
            __do_softirq();
            local_irq_enable();
            cond_resched();
            return;
        }
        local_irq_enable();
    }
    asmlinkage __visible void __softirq_entry __do_softirq(void)
    {
        unsigned long end = jiffies + MAX_SOFTIRQ_TIME;
        unsigned long old_flags = current->flags;
        int max_restart = MAX_SOFTIRQ_RESTART;
        struct softirq_action *h;
        bool in_hardirq;
        __u32 pending;
        int softirq_bit;
    
        /*
         * Mask out PF_MEMALLOC s current task context is borrowed for the
         * softirq. A softirq handled such as network RX might set PF_MEMALLOC
         * again if the socket is related to swap
         */
        current->flags &= ~PF_MEMALLOC;
    
        pending = local_softirq_pending();
        account_irq_enter_time(current);
    
        __local_bh_disable_ip(_RET_IP_, SOFTIRQ_OFFSET);
        in_hardirq = lockdep_softirq_start();
    
    restart:
        /* Reset the pending bitmask before enabling irqs */
        set_softirq_pending(0);
    
        local_irq_enable();
    
        h = softirq_vec;
    
        while ((softirq_bit = ffs(pending))) {
            unsigned int vec_nr;
            int prev_count;
    
            h += softirq_bit - 1;
    
            vec_nr = h - softirq_vec;
            prev_count = preempt_count();
    
            kstat_incr_softirqs_this_cpu(vec_nr);
    
            trace_softirq_entry(vec_nr);
            h->action(h);
            trace_softirq_exit(vec_nr);
            if (unlikely(prev_count != preempt_count())) {
                pr_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_set(prev_count);
            }
            h++;
            pending >>= softirq_bit;
        }
    
        rcu_bh_qs();
        local_irq_disable();
    
        pending = local_softirq_pending();
        if (pending) {
            if (time_before(jiffies, end) && !need_resched() &&
                --max_restart)
                goto restart;
    
            wakeup_softirqd();
        }
    
        lockdep_softirq_end(in_hardirq);
        account_irq_exit_time(current);
        __local_bh_enable(SOFTIRQ_OFFSET);
        WARN_ON_ONCE(in_interrupt());
        current_restore_flags(old_flags, PF_MEMALLOC);
    }

    __do_softirq()首先会disable bh(bottom half),这个即是将thread_info里的preempt_count softirq field加1;

    然后会将当前cpu的per cpu变量local_softirq_pending_ref读出来,使用ffs获取其值中第一个不为0的bit,得到了对应是哪个softirq触发了,根据这个index,确定对应softirq_vec数组里的哪个元素,然后调用这个元素的action函数。

    softirq_vec数组是struct softirq_action结构体类型数组,在open softirq时,根据softirq的idx作为此数组元素的index,将这个softirq的action函数保存到这个数组

    在这个while循环里依次将local_softirq_pending_ref所有置起来的bit对应的softirq都处理完毕。

    处理完所有的softirq后,调用__local_bh_enable()将bh enable,即将thread_info里的preempt_count softirq field减一

    上述ksoftirqd/* kernel thread是每个cpu core均有这样一个thread,在raise softirq时,将per cpu变量local_softirq_pending_ref对应bit置1后,然后唤醒当前的cpu去处理这个softirq,也就是哪个cpu触发softirq,就由哪个cpu去处理这个softirq:

    root             9     2 0 18:59:59 ?     00:00:00 [ksoftirqd/0]
    root            17     2 0 18:59:59 ?     00:00:00 [ksoftirqd/1]
    root            22     2 0 18:59:59 ?     00:00:00 [ksoftirqd/2]
    root            27     2 0 18:59:59 ?     00:00:00 [ksoftirqd/3]

    local_bh_disable()/local_bh_enable()

    这两个函数分别是disable、enable softirq,disable softirq后,当硬中断发生了,处理完硬中断后如果raise了softirq,此时不会wake ksoftirq/* thread来处理softirq。即在raise_softirq_irqoff()里会判断当前是否在interrupt context,这个in_interrupt()包括hardirq、softirq、nmi,如果先前有disable softirq,此时将判断得出是在interrupt context,所以不会wake ksoftirq/* thread:

    kernel/softirq.c

    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();
    }

     good blog:

    https://zhuanlan.zhihu.com/p/80680484

  • 相关阅读:
    C# 关于爬取网站数据遇到csrf-token的分析与解决
    Nginx实现同一端口HTTP跳转HTTPS
    Console也要美颜了,来给Console添色彩
    程序员如何巧用Excel提高工作效率
    LeetCode每日一练(1-3)
    Json对象转Ts类
    JcApiHelper 简单好用的.Net ApiHelper
    .Net Core Mvc/WebApi 返回结果封装
    C#光盘刻录
    Orm框架开发之NewExpression合并问题
  • 原文地址:https://www.cnblogs.com/aspirs/p/15719621.html
Copyright © 2020-2023  润新知