Linux hung_task故障分析:从真实日志到Root Cause的Case Study

第8章:真实Case Study——从一条hung_task日志到Root Cause

0. Case边界、版本基线与证据声明

本章分析的真实日志头部是:

1
2
INFO: task as_mapreloc:3004 blocked for more than 184 seconds.
Tainted: P O 6.1.83-DR-PL5.1_V1.0.16 #1

目前没有完整Call Trace、Wait Object地址、Owner/Producer栈、IRQ/I/O trace、设备寄存器和firmware日志。因此本章不会编造最终Root Cause,而是展示如何从有限事实建立可继续执行的Case Analysis。

前文的机制说明以主线Linux v6.1标签提交830b3c68c1fb1e9176028d02ef86f3cf76aa2476为基线;本案例运行的是厂商派生内核6.1.83-DR-PL5.1_V1.0.16。最终符号、调用路径和行为必须以该构建对应的源码、.config、vmlinux及模块为准,不能把主线v6.1路径直接当作现场事实。

8.1 本章不再增加理论

本章只执行前七章的方法:

1
2
3
4
Raw Log -> Fact Table -> Missing Evidence
-> Call Trace分层 -> Wait Object -> Owner/Producer
-> Dependency Graph -> Timeline -> First Non-progress Point
-> Root Cause -> Fix -> Regression

重点是展示什么时候能下结论、什么时候必须停下来补证据。

8.2 Case基本信息

字段 已知值
Task/comm as_mapreloc
PID 3004
State 日志显示D状态背景
Reported duration 超过184秒
Kernel release 6.1.83-DR-PL5.1_V1.0.16
Taint P、O
完整Call Trace 缺失
Root Cause 未确认

任务名可能截断且不唯一,PID也可能在系统重启或进程退出后失效。

8.3 第一步:只写事实,不做推断

可以确认:内核hung_task Detector报告as_mapreloc:3004长时间没有观察到调度进展,运行内核带有专有和树外模块taint标志。

不能确认:它在等锁、Completion、I/O还是远端服务;不能确认厂商模块就是责任方;更不能确认死锁、IRQ丢失或硬件损坏。

8.4 从state:D能确认什么

D是用户可见的不可中断睡眠表示,说明任务当前主要处于阻塞而非持续占CPU的busy loop。分析方向应转为:它怎样睡下、等待哪个对象、谁负责推进该对象。

仅凭D状态不能知道Wait Object,也不能区分正常长延迟和异常永久停滞。

8.5 从blocked >184s能确认什么

v6.1检测器根据任务context switch计数和last_switch_time判断长期无进展,日志秒数受扫描周期和调度时机影响。因此“超过184秒”是检测尺度,不是某个业务API入口的精密秒表。

它足以证明异常值得调查,但不足以证明准确起点。

8.6 从Tainted: P O能确认什么

按相应内核文档,P通常表示加载过专有模块,O表示树外模块。它说明现场不是纯上游内核,并提示需要厂商源码、符号和模块信息。

taint是环境证据,不是责任判定。必须用Wait Object和依赖链把问题连接到具体模块,才能评价其相关性。

8.7 当前最缺的第一份证据:完整Call Trace

完整栈决定第一层分类:

1
2
3
4
5
6
wait_for_completion* -> Completion/Producer
mutex/rwsem slowpath -> Lock/Owner
wait event helper -> Condition/Writer/Waker
io_schedule/page wait -> I/O pipeline
nfs/rpc -> Network/Remote Server
reclaim/writeback -> Memory -> I/O dependency

没有栈,就无法确认Wait Primitive,更无法解析Wait Object。

8.8 Case分析总路线

1
2
3
4
5
6
7
8
9
10
as_mapreloc:3004
-> matching vendor Call Trace
-> Wait Primitive
-> Business Wait Point
-> concrete Wait Object
-> Owner / Producer
-> Owner / Producer current state
-> Next Dependency
-> First Non-progress Point
-> Evidence-backed Root Cause

每一箭头必须附证据和置信度。

8.9 如果Call Trace显示wait_for_completion

只能先写:

1
Immediate Cause:as_mapreloc等待某个Completion。

回到厂商对应源码,定位类似wait_for_completion(&req->done)的直接调用点,记录req实例、生命周期和请求ID。随后搜索本对象的complete()、complete_all()及reinit_completion()路径。

8.10 Completion路径下一步怎么查

建立候选Producer列表:IRQ handler、threaded IRQ、worker、timer、DMA callback或另一个task。对每个Producer回答:是否被调度/触发、是否处理同一对象、是否走到complete分支、是否存在错误早退。

静态源码证明“应该完成”,trace或现场状态才能证明“实际执行过”。

8.11 如果Producer是IRQ

依赖图初版:

1
as_mapreloc -> Completion C -> ISR X -> IRQ N -> Device/Firmware

补充/proc/interrupts前后计数、irq_handler_entry/exit、ISR状态位和driver complete事件。共享IRQ的entry不能证明目标设备确实完成。

8.12 如果Call Trace显示mutex_lock

第一层只确认:

1
Immediate Cause:as_mapreloc等待Mutex M。

需要从调用实参确认具体M,并利用vmcore、锁trace、lockdep或其他task stack找现场Owner。静态搜索只能列出可能持锁路径。

8.13 Mutex场景的完整链

可能形成:

1
2
as_mapreloc -> Mutex M -> Owner Task B
Task B -> Completion C -> IRQ -> Hardware

此时Lock只是阻塞传播节点,最终Root Cause可能位于Task B的下游。找到Owner后必须继续执行同一SOP。

8.14 如果Call Trace显示io_schedule

这说明睡眠具有I/O语义,但io_schedule()仍是框架。下一步从Business Wait Point定位page/folio、bio、request、journal、RPC或具体设备对象。

直接写“block layer bug”属于越过证据。

8.15 IO场景继续怎么查

本地存储链可能是:

1
as_mapreloc -> Filesystem -> bio/request -> Driver -> Storage

用block issue/complete、queue状态、timeout/reset、IRQ和设备日志识别请求在哪一层停滞。需要request ID或设备/扇区等字段防止错误配对。

8.16 如果Call Trace显示NFS或RPC

依赖跨越本机和远端:

1
as_mapreloc -> NFS -> RPC task -> Network -> Remote Server/Storage

收集RPC状态与重传、挂载参数、网络收发、服务端和远端存储证据。客户端D状态可能只是远端故障的表现。

8.17 如果Call Trace显示Memory或Reclaim

try_to_free_pages()、dirty throttling、writeback或folio wait不能直接证明内存管理Bug。继续追:

1
allocation -> reclaim -> writeback/folio -> filesystem -> block I/O -> device

记录GFP/order、zone水位、dirty/writeback、vmscan和块完成事件。

8.18 当前Case最重要的原则

无论完整栈落入哪类,都必须继续:

1
2
3
4
Wait Primitive只回答“怎么等”
Wait Object回答“等什么”
Owner/Producer回答“谁应推进”
Timeline回答“推进停在哪一刻”

第一个阻塞函数通常不是最终Root Cause。

8.19 Case Analysis Template

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[Environment]
Kernel release/commit/config/vmlinux/modules/taint:

[Waiter]
Name/PID/TID/State/Duration:

[Stack Layering]
Scheduler / Primitive / Business Point / Origin:

[Wait Object]
Type/address/container/lifetime/request ID:

[Dependency]
Owner or Producer / State / Next Object:

[Evidence]
Source / Runtime / Trace / Reproduction:

[Conclusion]
Symptom / Immediate Cause / Root Cause / Confidence:

8.20 怎样判断Confidence

本文采用四级:

1
2
3
4
Confirmed:直接证据闭合关键因果边,并经修复/复现验证
Strongly Supported:多项独立证据一致,但缺最后一层确认
Suspected:假设与有限证据相符,仍有多种替代解释
Unknown:现有证据无法有效区分候选原因

当前案例Root Cause等级是Unknown。

8.21 Confirmed需要什么

至少应明确Wait Object、Owner/Producer和First Non-progress Node;依赖边有对象级现场或trace证据;替代假设得到检查;修复改变对应机制;旧版本复现而修复版本通过回归。

“改完后偶尔没再出现”不等于Confirmed。

8.22 Strongly Supported

例如源码证明Completion C只在IRQ X完成,trace确认命令提交后无IRQ X且buffer窗口完整,但缺少设备寄存器。可写:完成链在IRQ交付前停滞得到强支持,但无法区分mask、firmware或hardware。

8.23 Suspected

若只有一条wait_for_completion()栈和驱动函数名,只能怀疑Producer未推进。没有具体Completion、Producer和timeline时,不能升级为IRQ、firmware或硬件结论。

8.24 Evidence Table模板

Hop Claim Evidence Confidence Missing Evidence
1 as_mapreloc等待对象X 完整栈+调用点 待补 对象实例
2 X由Producer P推进 对应版本源码 待补 本次请求关联
3 P未推进 trace/runtime 待补 触发与状态证据
4 底层原因是Y 寄存器/FW/复现 待补 替代假设排除

当前只有日志头,因此表中任何具体X、P、Y都不能预填为事实。

8.25 Dependency Graph模板

Completion候选图:

1
as_mapreloc -> Completion C -> Producer P -> External Event E

图中的C、P、E在证据出现前标为占位符,不能把教学示例当案例事实。

8.26 如果是Lock场景

1
as_mapreloc -> Mutex M -> Owner B -> Next Wait Object

需要证明M实例、B确实持有M、B的当前栈以及下一条依赖。若链最终回到as_mapreloc或已有节点,才调查Cycle。

8.27 如果是IO场景

1
as_mapreloc -> page/request/RPC -> queue -> Driver/Network -> Device/Server

每一层用对象ID、issue/complete、timeout、网络/设备状态证明。不能仅凭函数前缀合并不同请求。

8.28 真正要找的是First Non-progress Point

它是沿依赖方向第一个“应发生的进展没有发生”的边界。例如已确认submit和DMA start,而目标IRQ在完整窗口中缺失,则停止区间位于DMA完成、硬件/firmware和IRQ交付之间。

这仍是区间而非最终根因,需要更多底层状态继续缩小。

8.29 Flight Recorder应该怎么配置

在尚不知分类时,从低开销基础事件开始:

1
2
sched_switch / sched_waking / sched_wakeup
irq_handler_entry / irq_handler_exit

拿到完整栈后按分类添加block、workqueue、vmscan/writeback、nfs/rpc或driver事件。必须实测buffer历史能覆盖预期触发间隔。

8.30 Driver最好增加哪些Tracepoint

若Business Wait Point落在自有驱动,建议围绕状态机增加:

1
2
3
CMD_SUBMIT、CMD_START、DMA_START
FW_SEND、FW_ACK、IRQ_STATUS
CMD_COMPLETE、TIMEOUT、RESET

只添加能回答依赖链问题的结构化事件,避免用大量printk替代设计。

8.31 事件里必须带ID

并发请求必须通过command/request ID贯穿host、firmware和hardware tag:

1
2
SUBMIT sw=42 hw=7 -> DMA sw=42 hw=7
-> IRQ hw=7 -> COMPLETE sw=42

没有ID时,相邻事件不一定属于同一请求。

8.32 真实时间线应该长什么样

正常候选:

1
2
3
4
5
6
7
T0 SUBMIT id=42
T1 as_mapreloc以D状态切出
T2 DEVICE_START id=42
T3 IRQ_STATUS id=42 done=1
T4 COMPLETE id=42
T5 sched_waking as_mapreloc
T6 as_mapreloc重新运行

这只是期望模型,需要用对应驱动源码确认真实事件顺序。

8.33 异常时间线

1
2
3
4
5
6
T0 SUBMIT id=42
T1 Waiter切出
T2 DEVICE_START id=42
T3 之后没有目标完成事件
T4 Driver timeout
T5 traceoff

“没有事件”只有在event已启用、filter正确、window完整且无drop时才有证明力。

8.34 为什么不要只靠dmesg

dmesg善于保存告警和离散状态,但console输出延迟、多CPU交错、ring覆盖及printk扰动使其难以还原细粒度时序。它通常告诉你最终症状,不一定保存最后一次submit、IRQ和wake。

8.35 为什么不要只靠ftrace

trace说明事件发生顺序,源码解释这些事件的契约和对象关系,运行时状态补充未被trace覆盖的内部值,复现验证因果稳定性。

1
Source + Runtime State + Temporal Trace + Reproduction

四者相互印证才构成强证据。

8.36 真实Case分析阶段1:Static Analysis

拿到完整栈和对应厂商源码后:标记框架帧、Wait Primitive、Business Point;定位调用实参;记录Wait Object生命周期;搜索Owner/Producer、完成、唤醒、取消和错误路径;画Expected Dependency Chain。

主线v6.1只用于理解机制,不能替代厂商补丁源码。

8.37 真实Case分析阶段2:Runtime Analysis

现场仍在时采集:

1
2
3
4
5
/proc/<tid>/stack、status、wchan
all task stacks/SysRq-t(受控使用)
/proc/interrupts
锁、request、queue和device状态
寄存器与firmware日志

记录采集时间,避免把不同时间点状态错误拼成同时态。

8.38 真实Case分析阶段3:Temporal Analysis

Flight Recorder回答:Waiter何时切出、是否被wake、Producer是否运行、IRQ是否到达、request是否issue/complete、driver状态机最后停在哪个事件。

输出是带ID的Actual Timeline,而不是一堆未经关联的trace文本。

8.39 真实Case分析阶段4:Reproduction

记录触发条件、频率、负载、硬件和环境。多次复现是否停在同一对象和First Non-progress区间,决定假设可信度。

复现不解释机制,但能检验证据是否稳定以及修复是否真正相关。

8.40 真实Case分析阶段5:Fault Injection

在测试环境模拟怀疑的异常:延迟或跳过Completion、屏蔽测试IRQ、让firmware不回复、注入I/O错误或持锁延迟。比较生成的stack、timeline和恢复行为是否与现场同构。

异常注入必须具备超时、reset和安全退出,不能在生产机制造不可恢复D状态。

8.41 如何避免错误修复

把wait_for_completion()改成timeout后告警消失,只证明永久等待被限制,不证明Producer故障修复。还要检查迟到Completion、对象回收、重复完成、reset并发和错误返回传播。

修复验收应针对First Non-progress机制,而不是仅针对告警文本。

8.42 应该同时记录三类Fix

1
2
3
Root Cause Fix:修复导致进展停止的机制
Fault Containment:超时、取消、reset、降级,限制影响
Observability Improvement:tracepoint、计数器、状态快照和持久化

三者可以同时需要,但报告中不能互相替代。

8.43 Root Cause Fix例子

根据最终证据可能是:补正确的IRQ使能/路由、修正DMA descriptor或IOMMU映射、补齐错误路径complete()、统一锁顺序、修复firmware状态机。

在当前案例尚未获得完整栈前,以上都只是类别示例,不是修复建议。

8.44 Fault Containment例子

带超时等待、命令取消、设备reset、有限重试、fail-fast和功能降级可以防止单个外部故障无限传播。必须定义超时后的对象所有权和迟到回调处理。

8.45 Observability Improvement例子

增加command ID、最后提交/完成ID、IRQ计数、timeout状态快照、driver tracepoint、pstore或可导出的Flight Recorder。观测改进应有开销预算和敏感信息审查。

8.46 Regression Test怎么设计

至少包含原场景、长稳、高并发、资源压力、错误注入、reset/recovery、设备remove以及性能回归。每个测试验证:任务不会永久D、错误正确返回、对象生命周期安全、依赖链能恢复推进。

8.47 对于Driver类问题

覆盖正常完成、IRQ延迟/缺失、DMA timeout、firmware不回复、设备reset、迟到IRQ和并发命令。检查重复完成、UAF、资源泄漏和错误码。

8.48 对于Lock类问题

覆盖高竞争、不同锁顺序、异常早退、取消、嵌套锁和Owner下游阻塞。启用合适调试配置时结合lockdep,但不能只以“无lockdep告警”判定通过。

8.49 对于NFS或IO类问题

覆盖网络延迟/断链、服务端不可用、存储慢/timeout、队列freeze、重连和恢复。验证挂载/重试策略符合业务期望,并能保留诊断证据。

8.50 最终Bug Report推荐结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# Problem Summary
# Environment and Source Provenance
# Trigger/Reproduction
# Raw Evidence
# Waiter and Stack Layering
# Wait Object
# Owner/Producer
# Expected Dependency Graph
# Expected vs Actual Timeline
# First Non-progress Point
# Claim-Evidence-Confidence Table
# Root Cause and Alternatives
# Root Cause Fix
# Fault Containment
# Observability Improvement
# Regression/Fault Injection
# Remaining Risks

8.51 Case Summary模板

当前可以严谨写成:

1
2
3
4
5
6
7
8
9
10
as_mapreloc(pid=3004) was reported by the hung-task detector after
more than 184 seconds without observed scheduling progress, on vendor
kernel 6.1.83-DR-PL5.1_V1.0.16 with P/O taint flags.

The available excerpt confirms a long uninterruptible wait, but does
not contain the full Call Trace required to identify the Wait Primitive
or Wait Object. The Root Cause is therefore unknown.

The next step is to obtain the matching vendor symbols and full stack,
resolve the concrete Wait Object, then follow its Owner/Producer chain.

8.52 为什么这段结论合理

它区分了已确认事实与缺失证据,没有把D状态等同于deadlock,也没有把taint等同于厂商模块责任。它还给出了能改变调查状态的下一步,而不是泛泛地说“继续看日志”。

8.53 完整Call Trace拿到后怎么继续

1
2
3
4
5
6
7
8
9
1. 使用匹配vmlinux/module符号化完整栈
2. 标出Scheduler Frames
3. 标出Wait Primitive
4. 定位Business Wait Point
5. 从调用实参确认Wait Object
6. 找Owner/Producer与当前状态
7. 递归追Next Dependency
8. 按分类配置Flight Recorder
9. 形成Evidence Table与Dependency Graph

8.54 如果只有一次现场怎么办

立即保存完整dmesg/串口、内核构建信息、模块列表、taint、all task stacks、/proc/interrupts、目标PID stack/status、trace buffer、设备寄存器和firmware日志。原始文件只读归档,分析时制作副本。

采集动作本身可能改变现场,应按价值和风险排序。

8.55 如果系统很快恢复

恢复后的/proc只表示当前状态,不能代替故障时状态。下一版应部署常驻Flight Recorder,在driver timeout或更早错误点snapshot/traceoff,并持久化关键状态。

8.56 如果系统完全卡死

可评估hung_task_panic配合kdump、ramoops/pstore、硬件watchdog dump或平台崩溃采集。启用panic会把局部阻塞升级为系统重启,必须评估服务可用性、dump可靠性和存储容量。

8.57 为什么Case Study最终必须可复现

完整闭环是:旧版本在受控条件复现、证据稳定指向同一停止点、修复直接改变机制、新版本通过原场景和异常注入、性能与恢复能力无回归。

无法稳定复现时仍可修复高置信度问题,但报告必须保留不确定性与后续观测计划。

8.58 最终Case分析总图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Raw Log
-> Fact Table / Missing Evidence
-> Matching Vendor Source + Symbols
-> Full Call Trace
-> Wait Primitive / Business Point
-> Concrete Wait Object
-> Owner / Producer
-> Recursive Dependency Graph
-> Flight Recorder Actual Timeline
-> First Non-progress Point
-> Claim-Evidence-Confidence
-> Root Cause / Alternatives
-> Root Fix + Containment + Observability
-> Regression + Fault Injection

8.59 本章最重要的设计思想

  1. 真实案例先分Fact和Hypothesis。
  2. D状态与184秒只证明长期不可中断等待,不证明具体子系统责任。
  3. 厂商6.1.83派生内核必须使用匹配源码、配置和符号。
  4. 完整Call Trace是确定Wait Primitive的第一关键证据。
  5. Wait Primitive之后继续找对象、Owner/Producer和下一跳。
  6. First Non-progress Point比最外层Waiter更接近根因。
  7. 缺失事件只有在采集完整性得到证明后才有意义。
  8. Source、Runtime、Trace和Reproduction应相互验证。
  9. 修复分为Root Cause、Containment和Observability三类。
  10. 没有证据闭环时,明确写Unknown比虚构Root Cause更有价值。

8.60 博客总结

as_mapreloc:3004 blocked for more than 184 seconds目前只能证明该任务被hung_task Detector判定为长期无调度进展的不可中断等待者,并且运行在带专有和树外模块taint的厂商6.1.83派生内核上。缺少完整Call Trace意味着连Wait Primitive都尚未确认,因此Root Cause必须保持Unknown。

正确的下一步是取得匹配的vendor源码、.config、vmlinux和模块符号,完整符号化栈;从Business Wait Point的实参确定Wait Object;对锁找Owner,对Completion、condition和I/O找Producer;递归构建Dependency Graph,再用运行时状态和Flight Recorder Timeline确定First Non-progress Point。最终通过Claim-Evidence-Confidence表区分直接事实、强支持推断和未知项。

这正是成熟调试和“看见D状态就猜Driver/IO/死锁”的区别:结论的强度永远不能超过证据。

8.61 学完检查题

  1. 当前案例已经确认的事实有哪些?
  2. 为什么主线v6.1不能替代厂商6.1.83源码?
  3. state:D不能告诉你哪些信息?
  4. 184秒为何不是业务等待入口的精确时间?
  5. P O taint应怎样使用,不能怎样使用?
  6. 当前Root Cause的置信度为什么是Unknown?
  7. 完整Call Trace能把调查推进到哪一步?
  8. Completion、mutex和I/O栈分别选择什么调查方向?
  9. 如何确认具体Wait Object实例?
  10. 为什么静态源码不能证明Producer现场执行过?
  11. IRQ entry为何不证明目标设备完成?
  12. 找到mutex Owner后为什么不能停止?
  13. io_schedule()为什么不是根因终点?
  14. NFS等待的Producer可能位于哪里?
  15. reclaim栈如何继续依赖存储?
  16. Evidence Table为何必须包含Missing Evidence?
  17. Confirmed和Strongly Supported差在哪里?
  18. 只有单条栈时通常应使用什么置信度?
  19. Dependency Graph中的占位节点应怎样标记?
  20. First Non-progress Point与最终物理根因有何区别?
  21. 基础Flight Recorder应先启用哪些事件?
  22. 为什么拿到栈后再按分类增加事件?
  23. 自定义Driver事件为何需要request ID?
  24. “没有IRQ事件”成为证据前要检查什么?
  25. dmesg和ftrace各自缺少什么?
  26. Static Analysis的最终输出是什么?
  27. Runtime采集为什么必须记录时间?
  28. Temporal Analysis如何识别停止区间?
  29. Reproduction提高哪一类可信度?
  30. Fault Injection应具备哪些安全机制?
  31. timeout修复为何可能引入迟到回调问题?
  32. 三类Fix分别解决什么问题?
  33. Driver类回归应覆盖哪些异常?
  34. Lock类回归为何不能只看lockdep?
  35. NFS/IO类回归需要验证哪些恢复路径?
  36. 只有一次现场时应优先保存什么?
  37. 系统恢复后为何不能用当前stack替代故障stack?
  38. hung_task_panic的收益和风险分别是什么?
  39. 什么条件构成完整复现—修复—回归闭环?
  40. 你能否在不虚构任何证据的前提下,为本案例写出下一轮采集方案?

8.62 整个hung_task专题总结

1
2
3
4
5
6
7
8
9
10
11
12
13
TASK_UNINTERRUPTIBLE / D State
-> khungtaskd检测Long No Progress
-> Call Trace分层
-> Wait Primitive
-> Concrete Wait Object
-> Owner / Producer
-> Recursive Dependency Chain
-> Root Cause分类
-> Flight Recorder Timeline
-> First Non-progress Point
-> Evidence-backed Root Cause
-> Root Fix / Containment / Observability
-> Regression / Fault Injection

整个专题最终训练的不是识别某个函数名,而是持续回答三个问题:

这个Task具体在等谁?那一方为什么没有继续推进?支持这条因果边的证据是什么?


Linux hung_task故障分析:从真实日志到Root Cause的Case Study
https://cj0510.github.io/2026/10/11/Linux Kernel/Debug/Linux-hung-task故障分析-第8章-真实Case-Study/
作者
CJ1018
发布于
2026年10月11日
许可协议
CC BY-NC-SA 4.0