当前位置: 首页 > ai >正文

nohz_full 参数对内核软硬锁检测机制的影响分析

问题描述

某 4.x 内核使用 lkdtm 测试异常注入功能时,在触发 softlockup 后,内核一直检测不到不能触发 panic 自动重启。

排查过程

  1. 排查 softlockup 相关内核配置参数是否开启–已经开启
  2. 排查 sysctl kernel.softlockup_panic 配置是否开启–已经开启
  3. 排查 sysctl kernel.panic 配置是否设置 – 正常设置
  4. 排查每个核上的 watchdog_timer_fn hrtimer 定时器事件是否开启

在第四步中,查看 /proc/timer_list 发现只有 0 核上开启了 watchdog_timer_fn,其它核上未开启故而无法检测到 softlockup 与 hardlockup。

为什么其它核上未开启用于 softlockup 检测的 hrtimer 定时器事件?

在定位这个问题前,笔者对于 softlockup 的检测原理有个概要的认识,知道它会在每个 cpu 核上创建一个 hrtimer 定时器来进行检测,既然这个定时器都没有创建,那检测不到异常也是正常的。

另外一个问题就是为什么 0 核上开启了 watchdog_timer_fn 但是也检测不到呢?

在回答这个问题前笔者先分析下第一个问题,粗略扫描代码发现 softlockup 检测功能的开启与 watchdog_cpumask 掩码有关,此掩码配置又会受 housekeeping_mask 掩码内容影响,下面基于 4.x 内核对这一过程进行分析。

watchdog_cpumask 相关代码分析

内核会在 lockup 检测器初始化时,在 NO_HZ_FULL开启及 nohz_full功能运行时将未开启 nohz_full 的 cpu 核掩码写入 housekeeping_mask,此后 housekeeping_mask被拷贝到 watchdog_cpumask 中,否则拷贝 cpu_possible_mask 表示所有的 cpu 核掩码。

此后初始化 softlockup detector,在每个 cpu 核上创建 watchdog_thread 线程,仅在 watchdog_cpumask 使能的每个 cpu 核中唤醒 watchdog_thread 运行,未使能的 cpu 核上创建的 watchdog_thread 不会运行。watchdog_thread 线程模拟看门狗周期性更新 softlockup watchdog,实现类似于“喂狗”的操作。

系统中的 watchdog/x 线程:

[root@localhost]# ps aux |grep watchdog                                                  
root         12  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/0]  
root         13  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/1]  
root         20  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/2]  
root         27  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/3]  
root         34  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/4]  
root         41  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/5]  
root         48  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/6]  
root         55  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/7]  
root         62  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/8]  
root         69  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/9]  
root         76  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/10] 
root         83  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/11] 
root         90  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/12] 
root         97  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/13] 
root        104  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/14] 
root        111  0.0  0.0      0     0 ?        S    02:52   0:00 [watchdog/15] 

watchdog_threads 定义如下:

static struct smp_hotplug_thread watchdog_threads = {.store                  = &softlockup_watchdog,.thread_should_run      = watchdog_should_run,.thread_fn              = watchdog,.thread_comm            = "watchdog/%u",.setup                  = watchdog_enable,.cleanup                = watchdog_cleanup,.park                   = watchdog_disable,.unpark                 = watchdog_enable,
};

相关字段的含义如下:

回调触发时机作用
storethread_fn() 内部访问存储 watchdog 的全局变量
thread_should_run()线程调度时检查决定 watchdog 线程是否需要运行
thread_fn()thread_should_run() 返回 true 时执行执行 watchdog 逻辑,检测软锁
setup()线程创建时(CPU 上线)启用 watchdog,初始化数据
cleanup()CPU 下线,线程销毁释放资源,关闭 watchdog
park()CPU 下线时线程进入暂停状态,不再运行
unpark()CPU 重新上线重新启用 watchdog 线程
thread_comm线程创建时设定 watchdog 线程的名称

这里主要描述 setup 函数与 thread_should_run 及 thread_fn 函数。setup 函数在线程执行时做初始化的操作,这里对应的 watchdog_enable 函数,其代码如下:

static void watchdog_set_prio(unsigned int policy, unsigned int prio)
{struct sched_param param = { .sched_priority = prio };sched_setscheduler(current, policy, &param);
}static void watchdog_enable(unsigned int cpu)
{struct hrtimer *hrtimer = raw_cpu_ptr(&watchdog_hrtimer);/* kick off the timer for the hardlockup detector */hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);hrtimer->function = watchdog_timer_fn;/* Enable the perf event */watchdog_nmi_enable(cpu);/* done here because hrtimer_start can only pin to smp_processor_id() */hrtimer_start(hrtimer, ns_to_ktime(sample_period),HRTIMER_MODE_REL_PINNED);/* initialize timestamp */watchdog_set_prio(SCHED_FIFO, MAX_RT_PRIO - 1);__touch_watchdog();
}

它会初始化用于检测 softlockup 的 hrtimer,此 hrtimer 周期性运行检测是否发生了 softlockup,当检测到有 softlockup 发生时,根据配置触发 panic、打印信息。

同时这里还做了 hardlockup 初始化,老版本内核 hardlockup 使用 perf 注册一个周期性的 nmi 中断,在中断中执行 hardlockup 检测。注意它还将线程的调度策略设置为 SCHED_FIFO 并将优先级设置为最大以优先调度,最后立刻出发了一次喂狗操作,避免错误检测。

thread_should_run 函数的触发时机:

  • 调度器决定是否唤醒 watchdog 线程 时调用。
  • 每次 CPU 进入/退出空闲状态、上下文切换 时都会检查该函数

当返回 true 时调度器会执行 watchdog 线程,返回 false 则不需要执行。

经过上述分析,确定开启了 nohz_full 功能的核上不会使能 softlockup 检测,cat /proc/cmdline 发现我们并未设置 nohz_full 相关内核引导参数,继续阅读内核源码发现它由 NO_HZ_FULL_ALL 配置使能,继续对此配置进行分析。

NO_HZ_FULL_ALL 配置功能分析

内核原文:

config NO_HZ_FULL_ALLbool "Full dynticks system on all CPUs by default (except CPU 0)"depends on NO_HZ_FULLhelpIf the user doesn't pass the nohz_full boot option todefine the range of full dynticks CPUs, consider that allCPUs in the system are full dynticks by default.Note the boot CPU will still be kept outside the range tohandle the timekeeping duty.

如果用户没有传递 nohz_full 启动参数来定义完整的 dynticks cpu 的范围,则默认所有 cpu 都开启此模式。请注意,启动 cpu(cpu 0)仍将保持在范围之外以处理定时任务。

测试记录如下:

 [root@localhost]# dmesg | grep NO_HZ                                                     
[    0.000000] NO_HZ: Clearing 0 from nohz_full range for timekeeping           
[    0.000000] NO_HZ: Full dynticks CPUs: 1-127.   

除引导核外,所有使能了 nohz_full 功能的 cpu 核会从 housekeeping 掩码中去掉,这样在 softlockup 初始化的时候就不会在这些核上启动检测 softlockup 的 hrtimer 定时器事件,这就是内核没有检测到软锁并触发 panic 的根本原因。

同时 softlockup 检测的作用范围是单个核,一个核上的定时器事件只能检测该核上的 softlockup,这样即便 0 核上开启了 softlokcup 检测,但是触发 softlockup 的核非 0 核时,在问题场景也无法正常工作。

内核主线移除 NO_HZ_FULL_ALL 配置的修改:

commit a7c8655b073d89303911c89d0fd9fc4be7631fbe
Author: Paul E. McKenney <paulmck@kernel.org>
Date:   Thu Nov 30 15:36:35 2017 -0800sched/isolation: Eliminate NO_HZ_FULL_ALLCommit 6f1982fedd59 ("sched/isolation: Handle the nohz_full= parameter")broke CONFIG_NO_HZ_FULL_ALL=y kernels.  This breakage is due to the codeunder CONFIG_NO_HZ_FULL_ALL failing to invoke the shiny new housekeepingfunctions.  This means that rcutorture scenario TREE04 now emits RCU CPUstall warnings due to the RCU grace-period kthreads not being awakenedat a time of their choosing, or perhaps even not at all:[   27.731422] rcu_bh kthread starved for 21001 jiffies! g18446744073709551369 c18446744073709551368 f0x0 RCU_GP_WAIT_FQS(3) ->state=0x402 ->cpu=3[   27.731423] rcu_bh          I14936     9      2 0x80080000[   27.731435] Call Trace:[   27.731440]  __schedule+0x31a/0x6d0[   27.731442]  schedule+0x31/0x80[   27.731446]  schedule_timeout+0x15a/0x320[   27.731453]  ? call_timer_fn+0x130/0x130[   27.731457]  rcu_gp_kthread+0x66c/0xea0[   27.731458]  ? rcu_gp_kthread+0x66c/0xea0Because no one has complained about CONFIG_NO_HZ_FULL_ALL=y being broken,I hypothesize that no one is in fact using it, other than rcutorture.This commit therefore eliminates CONFIG_NO_HZ_FULL_ALL and updatesrcutorture's config files to instead use the nohz_full= kernel parameterto put the desired CPUs into nohz_full mode.Fixes: 6f1982fedd59 ("sched/isolation: Handle the nohz_full= parameter")Reported-by: kernel test robot <xiaolong.ye@intel.com>Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>Cc: Frederic Weisbecker <frederic@kernel.org>Cc: Thomas Gleixner <tglx@linutronix.de>Cc: Chris Metcalf <cmetcalf@mellanox.com>Cc: Christoph Lameter <cl@linux.com>Cc: Linus Torvalds <torvalds@linux-foundation.org>Cc: Luiz Capitulino <lcapitulino@redhat.com>Cc: Mike Galbraith <efault@gmx.de>Cc: Peter Zijlstra <peterz@infradead.org>Cc: Rik van Riel <riel@redhat.com>Cc: Wanpeng Li <kernellwp@gmail.com>Cc: Ingo Molnar <mingo@kernel.org>Cc: John Stultz <john.stultz@linaro.org>Cc: Jonathan Corbet <corbet@lwn.net>

linux 内核 v4.15 版本移除了 NO_HZ_FULL_ALL 选项,后续内核版本不存在此问题。

问题延伸:watchdog_cpumask 配置

通过写入 watchdog_cpumask文件能够动态配置 watchdog 线程进入暂停、运行状态以此来使能指定核上的 softlockup 检测。
它保存了掩码的值,不支持特殊的格式,当写入后,内核会通过 park、unpark 机制来暂停、运行指定核上的 watchdog_thread 线程来达到动态配置的效果。

如何解决此问题?

根据分析情况,高版本内核也已经不具备此配置,关闭 CONFIG_NOHZ_FULL_ALL 配置能够解决此问题,这里隐含着一个问题就是对于开启了 nohz_full 的 cpu 核,软硬锁检测功能将会失效。

总结

nohz_full 功能是针对 cpu 性能的一个优化,通过减少 cpu 核上运行的时钟中断来提高程序性能。需要注意的是开启了此功能对现有内核检测机制的影响,例如这里的 softlockup 检测失效的问题。

softlockup 检测作为一种可靠性的功能,此问题的存在表明产品在追求高性能的同时设计中也需要兼顾可靠性能力,有时候这两者可能还存在一些冲突,需要权衡。

http://www.xdnf.cn/news/14358.html

相关文章:

  • 嵌入式学习笔记 - SH79F6441 堆栈栈顶可以是片上内部RAM(00H-FFH)的任意地址怎么理解
  • (91)课113:存储函数与存储过程的区别总结。
  • DP刷题练习(三)
  • Golang 解大整数乘法
  • Python Pillow 库详解文档
  • pythton 语言的独特语法
  • Axure应用交互设计:多种类型元件实现新增中继器数据
  • 【springcloud】快速搭建一套分布式服务springcloudalibaba(五)
  • Python爬虫实战:研究Mr. Queue相关技术
  • 【Java SE】类和对象(3)
  • Kafka源码P2-生产者缓冲区
  • 基于大模型预测缺铁性贫血的综合技术方案大纲
  • 记录一次 Oracle 表空间不足问题的解决过程
  • Linux进程间通信(上)
  • Proteus8.17-LCD12864液晶屏幕仿真模型
  • 华为OD机试-考勤信息-双指针(JAVA 2025B卷)
  • AI是什么?大模型、语料、训练、推理、机器学习、神经网络等专业名词如何关联
  • 基于docker的nocobase本地部署流程
  • CPU的异常处理
  • PC16550 UART接收中断处理完整示例代码
  • 134-135Elements-UI组件库
  • 03- 六自由度串联机械臂(ABB)动力学分析
  • SoftMax 函数
  • Unity基础-范围检测
  • Redis全面深入学习目录
  • 求数组中最长单调不降连续子数组的长度
  • stm32 f103c8t6仿真 串口收发测试
  • 用AI配合MCP快速生成n8n工作流
  • 【Linux服务器】-安装zabbix-负载环境(故障自动切换场景)
  • HarmonyOS Grid 网格拖拽完全指南