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

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

相关文章:

  • 大模型笔记6:微调
  • Redis中的zset的底层实现
  • 【Create my OS】5 内核线程
  • 【图片识别改名】如何批量识别大量图片的文字并重命名图片,基于WPF和京东OCR识别接口的实现方案
  • srm管理系统供应商管理在线询价比价管理电子采购(java)
  • redis序列化
  • 嵌入式学习笔记C语言阶段--16函数指针
  • YOLOv3 中的 NMS 详解(基于论文与 Darknet 实现)
  • C#设计模式之AbstractFactory_抽象工厂_对象创建新模式-学习
  • 使用 socat 和 xinetd 将程序绑定到端口运行
  • 安卓9.0系统修改定制化____默认开启 开发者选项中的OEM锁解锁选项 开搞篇 五
  • Milvus/ES 插入方案对比
  • OD 算法题 B卷【最多团队】
  • SeaTunnel与Hive集成
  • Mkdocs 阅读时间统计插件
  • 华为云Flexus+DeepSeek征文 | 基于华为云ModelArts Studio搭建PandaWiki知识库问答系统
  • 极客时间《后端存储实战课》阅读笔记
  • linux 阻塞和非阻塞
  • 【一天一个知识点】RAG 是“问答脑”,智能体是“有行动力的大脑”
  • XP POWER EJ ET EY FJ FR 系列软件和驱动程序和手侧
  • 哪个网站可以做h5页面/网站开发报价方案
  • 网站改版设计方案/关键词搜索引擎又称为
  • 网站建设需要什么研究条件/企业如何做网站
  • 一个网站的建立需要什么/在线数据分析工具
  • 一家专门做直销的网站/如何免费自己创建网站
  • 如何提升网站点击量/手机百度最新正版下载