• Linux - 驱动开发 - watchdog - SMP机制下多核确活


    说明

    • 理论上:不管IC是单核还是多核,只要watchdog有被循环feed,就不会触发超时重启,因此watchdog在SMP机制下的多核环境显得比较宽松,只要任意核存活(喂狗)就不会重启设备。

    实际情况

    • 有客户反馈,多核环境下(SMP机制管理)有核hang住了,但是设备没有重启。
    • 在SMP机制管理的8核arm平台上实测现象:
    1. 有核crash(特意使核上跑的程序crash),SMP能检测到,并且所有核都会被stop,watchdog能重启设备。
    2. 有核hang住(特意使核上跑的程序hang住),设备会非常卡,smp机制中的调度会每隔一段时间打印一次timeout,但是由于主核正常,喂狗正常,不会触发watchdog重启系统,如下:
    [  433.562934] rcu: INFO: rcu_sched detected stalls on CPUs/tasks:
    [  433.568883] rcu: 	1-...0: (16 ticks this GP) idle=e3a/0/0x3 softirq=98/98 fqs=10498 
    [  433.576660] 	(detected by 0, t=21007 jiffies, g=-935, q=16)
    [  433.582255] Task dump for CPU 1:
    [  433.585495] task:swapper/1       state:R  running task     stack:15152 pid:    0 ppid:     1 flags:0x0000000a
    [  433.595460] Call trace:
    [  433.597917]  __switch_to+0xb8/0xe8
    [  433.601332]  0xffffff8100130c00
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 根据一些测试现象推测:有核卡住,设备非常卡是因为smp调度时,调度过程是阻塞的(但是有超时),smp调度过程就会卡住很久,只有超时后,其它进程才能得到调度,将触发smp调度的操作放到wdt驱动的喂狗函数中,这样就会触发watchdog重启系统,smp调度阻塞住喂狗了。

    结论

    • IC生产,无法确保每个核都是一样稳定,如果多核IC中有少数核稳定性稍微差点,可能会出现部分核hang住,因此需要watchdog来检测这种情况并重启。

    实现

    • 同构多核使用SMP机制管理下,kenerl启动之前只有主核在运行,kernel启动过程中再由kernel bringup其它核,因此kernel运行前的固件(uboot等),不需要做检测。

    SMP机制下多核确活机制(严格模式)

    • 在多核SMP管理环境下,确认多核是否alive,只要任意核hang住,重启设备。

    思路

    • 通过SMP机制发送核间中断给每个核,每个核收到中断后,将一个全局CPU 位图变量打上标志,表示核正在运行。
    • 发送核间中断,让每个核执行同一个函数:
    // 每个核都会运行该函数
    static void cpu_alive(void *passed_regs)
    {
        ...
    }
    
    smp_call_function(cpu_alive, NULL, 0);
    smp_wmb();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 第一版做法:每次喂狗前,发送SMP调度请求,等待所有核运行完成。
    static cpumask_t cpus_alive = CPU_MASK_NONE;
    
    #ifdef CONFIG_SMP
    static void cpu_alive(void *passed_regs)
    {
        int cpu = smp_processor_id();
    
        pr_debug("cpu[%d] setmask \n", cpu);
        cpumask_set_cpu(cpu, &cpus_alive);
    }
    #endif
    
    // watchdog驱动喂狗函数
    static int dw_wdt_ping(struct watchdog_device *wdd)
    {       
        struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
        
    #ifdef CONFIG_SMP
        unsigned int msecs;
        unsigned int ncpus;
    
        cpus_alive = CPU_MASK_NONE;
    
        ncpus = num_online_cpus() - 1;
        
        pr_debug("Sending IPI to other cpus...\n");
        smp_call_function(cpu_alive, NULL, 0);
        smp_wmb();
    
        // 阻塞1s 等待所有核执行完成
        msecs = 1000; // 1s
        while ((cpumask_weight(&cpus_alive) < ncpus) && (--msecs > 0)) {
            cpu_relax();
            mdelay(1);
        }
    
        if (cpumask_weight(&cpus_alive) >= ncpus)
    #endif
            writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs
                   + WDOG_COUNTER_RESTART_REG_OFFSET);
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 问题
    1. 阻塞1s,等待所有核执行完成,如果存在核执行超时了,会导致误判。
    2. 如果将阻塞时间拉长,喂狗时间和wdt timeout时间需要空出该时间。
    • 新版本:每次喂狗前,检查上一次喂狗后发送SMP调度请求后的CPU 位图数据,喂狗,再发送一次SMP调度请求。
    static cpumask_t cpus_alive = CPU_MASK_NONE;
    
    #ifdef CONFIG_SMP
    static void cpu_alive(void *passed_regs)
    {       
        int cpu = smp_processor_id();
        
        pr_debug("cpu[%d] setmask \n", cpu);
        cpumask_set_cpu(cpu, &cpus_alive);
    }
    #endif
    
    static int dw_wdt_ping(struct watchdog_device *wdd)
    {
        struct dw_wdt *dw_wdt = to_dw_wdt(wdd);
        static int isFirst = 1;
    
    #ifdef CONFIG_SMP
        unsigned int ncpus;
    
        ncpus = num_online_cpus() - 1;
    
        if ((isFirst == 1) || cpumask_weight(&cpus_alive) >= ncpus) {
    #endif
            writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs +
                    WDOG_COUNTER_RESTART_REG_OFFSET);
    
    #ifdef CONFIG_SMP
            isFirst = 0;
            cpus_alive = CPU_MASK_NONE;
            smp_call_function(cpu_alive, NULL, 0);
            smp_wmb();
        }
    #endif
    
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 好处:等待所有核执行SMP请求和间隔喂狗并行起来了,不必像串行一样,多花一个等待时间。
  • 相关阅读:
    JS 中的闭包是什么?什么是闭包、闭包的作用、闭包的解决
    MAIR_ELX总结
    [React] Context上下文的使用
    【网络协议】RPC、REST API深入理解及简单demo实现
    [cpp primer随笔] 17. 类中名字的查找机制
    clickhouse的多路径存储策略
    2022年最新安徽食品安全管理员模拟试题及答案
    spicy(一)基本定义
    域名系统与IP地址分配
    HtmlCss光标(插入符caret)透明隐藏光标 221106笔记
  • 原文地址:https://blog.csdn.net/qazw9600/article/details/134384579