执行摘要
本次分析针对 SPRD UMS9230 平台上 DDR Qualify.TT 测试过程中发生的冻屏(screen freeze)问题。
根因判定: Block 层 blk_mq_tags 结构体发生 use-after-free,Scsi_Host.tag_set.tags 指针指向已释放并被 cpumask/IRQ affinity 对象重用的 slab 内存(kmalloc-128)。当 EROFS readahead 路径调用 blk_mq_get_tag 时,解引用该失效指针读取到垃圾位图数据,导致所有 tag 分配失败,形成系统级 I/O 死锁。UFS 控制器本身工作正常(79905 个 SCSI 请求全部完成)。
置信度: high-probability
关键发现:
tag_set.tags = 0xffffff8082570a80指向 kmalloc-128 slab(blk_mq_tags 需要 192 字节)该地址偏移 0x80 处包含 ASCII 字符串 "effective_affinity"(cpumask/IRQ affinity 对象特征)
blk_mq_hw_ctx.tags = NULL,sched_tags = NULLUFS HBA 状态正常:
outstanding_tasks = 0,outstanding_reqs = 0,所有 SCSI 请求已完成40+ 个 D-state 任务阻塞在
blk_mq_get_tag(EROFS readahead 路径)
分析工件
会话模式与工具约束
会话 ID:
8eac7901-21e6-4dc6-ae4f-75bab9d785edcrash 参数:
-m vabits_actual=39 -m phys_offset=0x80000000 -m kimage_voffset=0xffffffbf88000000 --kaslr=0x80000SPRD 参数来源: 从
dump_report.txt手动提取(工具无法自动解析)结构体偏移限制: crash 工具对部分结构体(如
blk_mq_hw_ctx、request_queue)存在偏移解析错误(KABI 扩展导致),需通过rd命令读取原始内存验证
调查过程
阶段 1:初步症状识别
可用证据: dump_report.txt 显示 panic_reason: Oops - BUG: Fatal exception,异常任务为 kworker/0:2(PID 26422),PC 在 sprd_debug_check_crash_key+0xe4(手动触发 fulldump)。
假设: 冻屏的根因不是 fulldump 触发本身,需要找到导致冻屏的底层原因。
命令: bt → 显示手动触发 fulldump 的栈帧,非冻屏根因。
阶段 2:系统级阻塞发现
假设: 冻屏可能是系统级阻塞导致。
命令: ps(输出过大,保存到文件后用 Python 解析)→ 发现 40+ 个 D-state 任务。
结果: 大量任务处于 UN(D-state)状态,确认系统级阻塞。
阶段 3:阻塞路径收敛
假设: 多个 D-state 任务可能收敛到同一阻塞点。
命令: 逐一检查 D-state 任务的栈帧 → 所有任务阻塞在 blk_mq_get_tag,路径为 EROFS readahead → blk_mq_get_tag。
结果: Block 层 tag 分配成为所有 I/O 的瓶颈。这是冻屏的直接原因。
阶段 4:UFS 子系统状态检查
假设: UFS 控制器可能故障导致 I/O 无法完成。
命令: struct scsi_device 0xffffff808019f000(sda, LUN 0)
结果:
iorequest_cnt = 79905,iodone_cnt = 79905— SCSI 层所有请求已完成queue_depth = 31,device_blocked = 0sdev_state = SDEV_RUNNING
假设变更: UFS 控制器正常,问题在 block 层。
阶段 5:UFS HBA 深入检查
命令: struct ufs_hba 0xffffff808591e9e8
结果:
ufshcd_state = UFSHCD_STATE_OPERATIONALoutstanding_tasks = 0,outstanding_reqs = 0uic_link_state = UIC_LINK_HIBERN8_STATEclk_gating.state = CLKS_OFFis_irq_enabled = falsehost_self_blocked = 1,eh_noresume = 1
结论: UFS 控制器已完成所有请求并进入低功耗状态。I/O 死锁的根因不在 UFS 层。
阶段 6:Block 层 Tag 管理结构检查(关键发现)
假设: blk_mq_tags 结构体可能损坏。
命令: struct Scsi_Host 0xffffff808591e000 → 获取 tag_set.tags = 0xffffff8082570a80
命令: kmem 0xffffff8082570a80
结果:
CACHE OBJSIZE ALLOCATED TOTAL SLABS SSIZE NAME
ffffff8080002300 128 297284 298144 9317 4k kmalloc-128
SLAB MEMORY NODE TOTAL ALLOCATED FREE
fffffffe02095c00 ffffff8082570000 0 32 32 0
[ffffff8082570a80]
关键发现: tag_set.tags 指向 kmalloc-128 slab 中的一个 128 字节对象,但 struct blk_mq_tags 大小为 192 字节。该指针不可能指向有效的 blk_mq_tags 结构体。
阶段 7:内存内容检查(确认 use-after-free)
命令: rd -x 0xffffff8082570a80 30
结果:
ffffff8082570a80: ffffff8084d85600 0000000000000000 ← 偏移 0x00
...
ffffff8082570b00: 7669746365666665 696e696666615f65 ← 偏移 0x80: "effective_"
ffffff8082570b10: 0000000000007974 ← 偏移 0x90: "affinity\0"
关键发现: 偏移 0x80 处包含 ASCII 字符串 "effective_affinity"(0x7669746365666665 = "effectiv", 0x696e696666615f65 = "e_affini", 0x0000000000007974 = "ty\0")。这是 cpumask 或 IRQ affinity 描述符的特征数据,不是 blk_mq_tags 结构体内容。
结论: tag_set.tags 是一个失效指针(stale pointer)。原始 blk_mq_tags 对象已被释放,该 slab 槽位被 cpumask/IRQ affinity 对象重用。这是典型的 use-after-free 模式。
阶段 8:blk_mq_hw_ctx 验证
命令: struct blk_mq_hw_ctx 0xffffff80886a9080 + rd -x 原始内存验证
结果: 由于 KABI 扩展导致结构体偏移不匹配,crash 工具报告 tags = 0x0。通过原始内存分析确认:
偏移 0x130 处:
0xffffff8080ff5600(接近 request_queue 地址 0xffffff8080ff55f0)偏移 0x150 处:
0x0000000000000000(tags 指针为 NULL)
结论: blk_mq_hw_ctx 的 tags 指针为 NULL,进一步确认 tag 管理子系统已完全失效。
直接证据
Scsi_Host.tag_set.tags = 0xffffff8082570a80— 指向 kmalloc-128 slab(blk_mq_tags 需要 192 字节)地址 0xffffff8082570a80 偏移 0x80 处包含 ASCII "effective_affinity" — cpumask/IRQ affinity 对象特征
kmem确认该对象当前在 kmalloc-128 中已分配(非空闲)— 内存已被不同子系统重用blk_mq_hw_ctx.tags = NULL,sched_tags = NULL— 硬件上下文无 tag 指针UFS HBA
outstanding_tasks = 0,outstanding_reqs = 0— 无待处理 UFS 请求sda
iorequest_cnt = 79905,iodone_cnt = 79905— SCSI 层所有请求已完成40+ 个 D-state 任务阻塞在
blk_mq_get_tag(EROFS readahead 路径)— 系统级 I/O 死锁1% 空闲内存,2608% committed — 严重内存压力
Scsi_Host.host_self_blocked = 1,eh_noresume = 1— SCSI 主机自阻塞UFS 链路状态
UIC_LINK_HIBERN8_STATE,时钟门控CLKS_OFF— UFS 控制器已休眠
推理与根因评估
根因机制
tag_set.tags 指针是一个失效指针,指向已释放的 blk_mq_tags slab 对象。该 slab 槽位已被 cpumask/IRQ affinity 对象重用(由 "effective_affinity" ASCII 字符串证明)。当 EROFS readahead 路径调用 blk_mq_get_tag 时,解引用该失效指针读取到垃圾位图数据(大部分为零,末尾为 affinity 字符串),导致所有 tag 分配失败或返回无效索引。这形成了系统级 I/O 死锁。
依赖链
EROFS readahead → blk_mq_get_tag → 解引用 tag_set.tags (0xffffff8082570a80)
→ 读取到 cpumask/IRQ affinity 数据(非有效 tag 位图)
→ tag 分配失败 → I/O 请求无法提交 → 系统级 I/O 死锁 → 冻屏
UFS 控制器状态
UFS 硬件本身工作正常:
所有 79905 个 SCSI 请求已完成(iorequest_cnt == iodone_cnt)
HBA 状态为 OPERATIONAL,无待处理任务
链路已进入 HIBERN8 低功耗状态(无活跃 I/O)
时钟已门控(CLKS_OFF)
I/O 死锁完全发生在 block 层 tag 管理层面,与 UFS 硬件无关。
DDR 测试上下文
DDR Qualify.TT 压力测试导致严重内存压力(1% 空闲,2608% committed),可能加速了 slab compaction 和对象重用,使得失效指针更可能被无关数据覆盖。但 "effective_affinity" ASCII 模式过于结构化,不可能是随机位翻转的结果,指向软件层面的正常 slab 重用。
竞争假设
DDR 内存损坏假设: 弱于 use-after-free 假设。"effective_affinity" 是结构化的 ASCII 字符串,对应 cpumask/IRQ affinity 对象,这表明内存是被正常分配和写入的,而非随机损坏。
UFS 控制器故障假设: 已被排除。UFS HBA 状态正常,所有 SCSI 请求已完成。
置信度、限制与报告天花板
置信度标签
high-probability
置信度限制
无法证明精确的释放路径: 无法从 post-mortem 快照中恢复 blk_mq_tags 被释放时的调用栈
drgn 不可用: split dump 模式禁用了 drgn,无法通过 drgn 验证结构体字段偏移
crash 工具偏移错误: KABI 扩展导致部分结构体(blk_mq_hw_ctx、request_queue)字段偏移解析不正确,需通过原始内存读取验证
无法区分竞争类型: 无法确定是 blk_mq_tags teardown 竞争(队列移除 vs. 进行中 I/O)还是更复杂的 slab 管理损坏
最有用的下一步
启用 SLUB debugging(
CONFIG_SLUB_DEBUG)以在 slab 释放时记录调用栈在 DDR 压力测试环境中复现,使用
kmem -s监控 blk_mq_tags slab 分配/释放检查上游 5.15.x 系列是否有 blk_mq_tags 生命周期管理的已知修复补丁
推荐行动与缓解措施
关键命令附录
Report generated at: 2026-05-14
Session ID: 8eac7901-21e6-4dc6-ae4f-75bab9d785ed
Analyst: bsp-stability-ai by iliuqi
Author homepage: https://www.iliuqi.com