• softlock_up以及时钟中断问题记录


    一. 背景介绍:

    softlock_up:
    内核开关, 打开之后可以检测软锁。软锁发生之后, 当cpu在设定的时间间隔中没有发生时间中断的话, 该机制就会发送一个nmi中断, 让系统重启。该机制用看门狗机制实现。
    nohz_full:
    内核开关, 用于消除某个cpu上的时钟中断, 可以在uboot的cmdline里指定想要关闭时钟中断的cpu核心序号。该功能主要应用于一些对时间性能很敏感的系统, 因为任务切换会伴随着时钟中断, 其中的上下文切换会对任务的性能和实时性产生影响, 若应用需要强性能和强实时性, 最好的情况是一个任务独占一个cpu, 不做任何时间片切换。
    一般该内核开关会伴随着核隔离使用, 下面是一个例子:

    setenv bootargs '... isolcpus=5-7 nohz_full=5-7 rw'
    
    • 1

    arch_timer:
    系统时钟

    二. 问题描述

    在一个需求中, dpdk需要在nohz_full和核隔离的情况使用。但是发现在4.19内核上打开了softlock_up和nohz_full隔离5~7号cpu。然而发现当两个内核开关打开的时候, 5 ~ 7号cpu依旧有来自于arch_timer的系统时钟。
    使用cat /proc/interrupts | grep arch_timer命令可以发现该中断在cpu 5 ~ 7 上增加。
    但是根据内核文档[3], 发现该现象是和该模块设计思路相悖的, 于是开始调查。

    默认情况下所有在线cpu上都会运行一个watchdog线程。不过在内核配置了”NO_HZ_FULL“的
    情况下watchdog线程默认只会运行在管家(housekeeping)cpu上,而”nohz_full“启动参数指
    定的cpu上则不会有watchdog线程运行。试想,如果我们允许watchdog线程在”nohz_full“指
    定的cpu上运行,这些cpu上必须得运行时钟定时器来激发watchdog线程调度;这样一来就会
    使”nohz_full“保护用户程序免受内核干扰的功能失效。当然,副作用就是”nohz_full“指定
    的cpu即使在内核产生了lockup问题我们也无法检测到。不过,至少我们可以允许watchdog
    线程在管家(non-tickless)核上继续运行以便我们能继续正常的监测这些cpus上的lockups
    事件。

    三. 调查

    首先调查softlock_up相关代码, 发现具体实现在 kernel/watchdog.c里, 其中开启softlock_up的watchdog入口函数为static void watchdog_enable(unsigned int cpu), 在同一个c文件里搜索其调用, 发现两处调用,
    以下是整理的两条调用链,
    ①:
    kernel_init

    kernel_init_freeable

    lockup_detector_init watchdog.c

    lockup_detector_setup

    lockup_detector_reconfigure

    softlockup_start_all

    softlockup_start_fn

    watchdog_enable

    ②:
    lockup_detector_online_cpu

    watchdog_enable

    查看watchdog_enable的①②上一级调用函数softlockup_start_alllockup_detector_online_cpu:

    static void softlockup_start_all(void)
    {
    	int cpu;
    
    	cpumask_copy(&watchdog_allowed_mask, &watchdog_cpumask);
    	for_each_cpu(cpu, &watchdog_allowed_mask)
    		smp_call_on_cpu(cpu, softlockup_start_fn, NULL, false);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    int lockup_detector_online_cpu(unsigned int cpu)
    {
    	watchdog_enable(cpu);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    发现①是用了cpu mask的, ②是没有使用cpu mask的。
    在两处添加打印, 发现softlockup_start_all在0~4号cpu上放置watchdog, 而lockup_detector_online_cpu在0 ~ 7上都放置了watchdog。所以第二个应该是和该模块设计思路不符合的, 应该是内的一个纰漏。
    查看5.15内核版本, 发现该问题已经被修复, 5.15版本lockup_detector_online_cpu函数为:

    int lockup_detector_online_cpu(unsigned int cpu)
    {
    	if (cpumask_test_cpu(cpu, &watchdog_allowed_mask))
    		watchdog_enable(cpu);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    已经使用了cpu mask。
    将该函数放在4.19内核上, 问题修复。

    再来分析一下为什么watchdog会影响NOHZ_FULL机制

    static void tick_sched_handle(struct tick_sched *ts, struct pt_regs *regs)
    {
    #ifdef CONFIG_NO_HZ_COMMON
    	/*
    	 * When we are idle and the tick is stopped, we have to touch
    	 * the watchdog as we might not schedule for a really long
    	 * time. This happens on complete idle SMP systems while
    	 * waiting on the login prompt. We also increment the "start of
    	 * idle" jiffy stamp so the idle accounting adjustment we do
    	 * when we go busy again does not account too much ticks.
    	 */
    	if (ts->tick_stopped) {
    		touch_softlockup_watchdog_sched();
    
    ...
    }
    			
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    从代码中可以看到, 当打开了NO_HZ_COMMON开关(NOHZ_FULL相关), 当发现该cpu是idle状态下, 且watchdog打开了的话, 必须调用touch_softlockup_watchdog_sched();函数告诉softlockup_watchdog我没有问题, 不需要对系统进行修正。
    下面是touch_softlockup_watchdog_sched()的代码, 从注释中也可以看出该代码的用处:

    /**
     * touch_softlockup_watchdog_sched - touch watchdog on scheduler stalls
     *
     * Call when the scheduler may have stalled for legitimate reasons
     * preventing the watchdog task from executing - e.g. the scheduler
     * entering idle state.  This should only be used for scheduler events.
     * Use touch_softlockup_watchdog() for everything else.
     */
    notrace void touch_softlockup_watchdog_sched(void)
    {
    	/*
    	 * Preemption can be enabled.  It doesn't matter which CPU's watchdog
    	 * report period gets restarted here, so use the raw_ operation.
    	 */
    	raw_cpu_write(watchdog_report_ts, SOFTLOCKUP_DELAY_REPORT);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    因此, 开了watchdog的cpu得接受arch_timer发过来的中断, 因为得定期通知softlock_up_watchdog.

    四. 参考:

    内核文档:
    [1] Linux NO_HZ_FULL NO_HZ 框架实现分析
    [2] (Nearly) full tickless operation in 3.10
    [3]内核代码文档:Documentation/translations/zh_CN/admin-guide/lockup-watchdogs.rst
    [4] linux 内核Lockup机制浅析

  • 相关阅读:
    Java岗吃透这份pdf,拿下阿里、腾讯等大厂offer
    06-JVM-监控及调优案例
    Java --- JVM之垃圾回收相关算法
    MacOS环境变量source生效但重启后又失效
    react源码中的生命周期和事件系统
    Kubernetes网络难懂?快来看这篇文章
    Kotlin源码编译报错,提示@UnsupportedAppUsage和@SystemApi声明的Java函数无法调用
    SQL并集、交集、差集使用
    一场网络攻击,可以“击垮”一个国家?【2022戴尔科技峰会预告】
    linux study01
  • 原文地址:https://blog.csdn.net/weixin_43651292/article/details/134259162