一、问题背景
工厂BLMMI工站出现了一台死机进dump的机器
二、问题分析
2.1 初步定位
dmesg_TZ.txt
[ 67.746231][ C0] msm_watchdog f410000.qcom,wdt: QCOM Apps Watchdog bark! Now = 67.746225
[ 67.746239][ C0] msm_watchdog f410000.qcom,wdt: QCOM Apps Watchdog last pet at 47.745802
Non-secure Watchdog data
Watchdog enabled
Pet time: 15.0s
Bark time: 20.0s
Watchdog last pet: 4.7e-08
Watchdog next pet: 15.000000047
Watchdog next bark: 20.000000047
Watchdog pet timer not expired
Current jiffies crossed pet_timer expires jiffies
Core 0 PC: __delay+9c <ffffffc00900c050>
Core 0 LR: __delay+bc <ffffffc00900c070>
[<ffffffc00900c050>] __delay+0x9c
[<ffffffc00900c134>] __const_udelay+0x28
[<ffffffc001558890>] qcom_wdt_trigger_bite[qcom_wdt_core]+0x424
[<ffffffc001559118>] qcom_wdt_bark_handler[qcom_wdt_core]+0xac
[<ffffffc008152b04>] __handle_irq_event_percpu+0xc4
[<ffffffc008152e00>] handle_irq_event+0x48
[<ffffffc0081590d4>] handle_fasteoi_irq+0x124
[<ffffffc00815114c>] generic_handle_domain_irq+0x48
[<ffffffc008010154>] gic_handle_irq+0x4c
[<ffffffc008014968>] call_on_irq_stack+0x3c
[<ffffffc00903a110>] el1_interrupt[jt]+0x98
[<ffffffc00903a090>] el1h_64_irq_handler+0x18
[<ffffffc008011304>] ret_to_kernel[jt]+0x0
[<ffffffc008b91c40>] cpuidle_enter_state+0x1d0
[<ffffffc008b92140>] cpuidle_enter+0x38
[<ffffffc008116dac>] do_idle+0x1f8
[<ffffffc008116ec4>] cpu_startup_entry[jt]+0x30
[<ffffffc00903c0b0>] rest_init+0xe0
[<ffffffc009c2a0a4>] arch_call_rest_init+0x10
[<ffffffc009c2a4b4>] start_kernel+0x40c
[<ffffffc009c22194>] __primary_switched+0xc8
从上面的信息我们可以得到以下的关键信息:
- 死机的原因为发生了watchdog
- last pet的时间是47.745802s,bark的时间是67.746225s
- CPU0发生的watchdog bite
2.2 定位卡死线程
- 47.745802 秒:最后一次喂狗(last pet)
- 67.746225 秒:Watchdog bark,然后紧接着 bite
👉 这说明在 20 秒内系统没有“喂狗”,导致 Watchdog 判定系统挂死,进而触发重启。
{% tip success %}
📌 正常情况下系统怎么喂狗?
在高通平台,比如你这个使用的是 msm_watchdog(QCOM Apps Watchdog),喂狗是周期性由某个高优先级内核线程或 定时器中断完成的。
常见机制如下:
- CPU 保持调度活动
- 定时器事件及时触发
- 关键任务没被阻塞
{% endtip %}
2.2.1 分析这 20 秒为何没喂狗
从日志分析,这 20 秒时间系统**处于挂起(s2idle)**状态且 resume 出现异常:
🚩 证据1:系统进入 suspend(挂起)
[ 47.654971] PM: suspend entry (s2idle)
...
[ 67.746225] msm_watchdog ... bark!
说明系统本意是轻量挂起(s2idle),但挂起过程中出了问题,并没有成功 resume。
🚩 证据2:suspend/resume 中 driver 没正常返回
从日志看,多个设备在执行 suspend:
fg_suspend in sleep
sc853x-charger: Suspend successfully!
NXP PN560 nfc start suspend
__wlan_hdd_bus_suspend
...
🚩 证据3:PM: suspend exit 没有出现
如果系统成功 resume,会出现:
PM: suspend exit
Restarting tasks ...
{% p red, 但这段期间根本没有出现。 %}
这意味着:
系统从 s2idle 醒来失败,resume 回调没被执行或没被唤醒,而且主 CPU 可能处于不可调度状态。
2.2.2 查看当前的所有timer
Timer List Dump
-----------------------------------------------------------------------------------------------------------------------
CPU 0(tvec_base: ffffff80fb713240 timer_jiffies: 4294909085(67.16s) next_timer: 4294909632(69.348s) active_timers: NA)
-----------------------------------------------------------------------------------------------------------------------
+ vectors Timers (3)
INDEX TIMER_LIST_ADDR EXPIRES EXPIRES(s) FUNCTION WORK REMARKS
187 ffffff80040460b8 4294909614 69.276s delayed_work_timer_fn status_change_work[qpnp_smb5_main]
187 ffffff805c8a5828 4294909602 69.228s delayed_work_timer_fn xm_charge_work[xm_smart_chg]
215 ffffff808a3b4ce8 4294913181 83.544s addrconf_rs_timer
-----------------------------------------------------------------------------------------------------------------------------
CPU 1(tvec_base: ffffff80fb731240 timer_jiffies: 4294908993(66.792s) next_timer: 5368650816(4295034.084s) active_timers: NA)
-----------------------------------------------------------------------------------------------------------------------------
+ No vectors Timers found
------------------------------------------------------------------------------------------------------------------------
CPU 2(tvec_base: ffffff80fb74f240 timer_jiffies: 4294909072(67.108s) next_timer: 4294909312(68.068s) active_timers: NA)
------------------------------------------------------------------------------------------------------------------------
+ vectors Timers (3)
INDEX TIMER_LIST_ADDR EXPIRES EXPIRES(s) FUNCTION WORK REMARKS
114 ffffffc00a0f85b8 4294909326 68.124s delayed_work_timer_fn psi_avgs_work
182 ffffff8091dce218 4294909282 67.948s delayed_work_timer_fn wb_workfn
229 ffffffc002f28ca8 4294920216 111.684s delayed_work_timer_fn reg_check_chans_work[cfg80211]
-----------------------------------------------------------------------------------------------------------------------------
CPU 3(tvec_base: ffffff80fb76d240 timer_jiffies: 4294908992(66.788s) next_timer: 5368649856(4295030.244s) active_timers: NA)
-----------------------------------------------------------------------------------------------------------------------------
+ No vectors Timers found
-----------------------------------------------------------------------------------------------------------------------------
CPU 4(tvec_base: ffffff80fb78b240 timer_jiffies: 4294908992(66.788s) next_timer: 5368646592(4295017.188s) active_timers: NA)
-----------------------------------------------------------------------------------------------------------------------------
+ No vectors Timers found
-----------------------------------------------------------------------------------------------------------------------------
CPU 5(tvec_base: ffffff80fb7a9240 timer_jiffies: 4294908992(66.788s) next_timer: 5368646029(4295014.936s) active_timers: NA)
-----------------------------------------------------------------------------------------------------------------------------
+ No vectors Timers found
------------------------------------------------------------------------------------------------------------------------
CPU 6(tvec_base: ffffff80fb7c7240 timer_jiffies: 4294909073(67.112s) next_timer: 4294909328(68.132s) active_timers: NA)
------------------------------------------------------------------------------------------------------------------------
+ vectors Timers (3)
INDEX TIMER_LIST_ADDR EXPIRES EXPIRES(s) FUNCTION WORK REMARKS
114 ffffff8047192c10 4294909322 68.108s prb_retire_rx_blk_timer_expired
219 ffffffc00a33fca8 4294915404 92.436s delayed_work_timer_fn work_fn
234 ffffffc00a34bf30 4294922903 122.432s delayed_work_timer_fn gc_worker
------------------------------------------------------------------------------------------------------------------------
CPU 7(tvec_base: ffffff80fb7e5240 timer_jiffies: 4294908992(66.788s) next_timer: 4294909888(70.372s) active_timers: NA)
------------------------------------------------------------------------------------------------------------------------
+ vectors Timers (4)
INDEX TIMER_LIST_ADDR EXPIRES EXPIRES(s) FUNCTION WORK REMARKS
191 ffffffc00f4bbcf8 4294909829 70.136s process_timeout
208 ffffff80041e44e8 4294909807 70.048s addrconf_rs_timer
240 ffffffc00a256360 4294926229 135.736s delayed_work_timer_fn check_lifetime
425 ffffffc00a1a01a8 4305692338 43200.172s delayed_work_timer_fn wakeup_dirtytime_writeback
以CPU0的timer list为例子
CPU 0
tvec_base: ffffff80fb713240
timer_jiffies: 4294909085 --> 对应时间:67.16s
next_timer: 4294909632 --> 对应时间:69.348s
基本信息解析:
- tvec_base: ffffff80fb713240,这个为struct timer_base
- timer_jiffies: 4294909085,是当前的 tick 值,映射为 67.16 秒
next_timer是最近将要触发的 timer,发生在 69.348 秒
表明当前(67.16s)并没有到触发任何定时器的时候
| INDEX | 触发时间(秒) | FUNCTION | 说明 |
|---|---|---|---|
| 187 | 69.276s | delayed_work_timer_fn→status_change_work[qpnp_smb5_main] | SMB5充电器状态工作线程 |
| 187 | 69.228s | delayed_work_timer_fn→xm_charge_work[xm_smart_chg] | 小米智能充电策略 |
| 215 | 83.544s | addrconf_rs_timer | 网络自动配置定时器 |
从 47s 到 69s 之间,没有任何 active timer 被调度执行,自然也没有机会触发喂狗逻辑。
2.2.3 为什么 watchdog 没被喂?
❗️系统进入了 suspend 状态,但没有正确 resume,也没有任何中断或 wake source 触发,导致 CPU 停止调度定时器,喂狗任务未执行。
2.3 查找阻塞的进程
目前我们可以初步定位是某个进程在suspend后,没有resume起来。
查看tasks.txt,并查找pm_suspend函数。
Process: irq/141-pmic_pw, [affinity: 0xff] cpu: 5 pid: 693 start: 0xffffff8047c3cb00
=====================================================
Task name: irq/141-pmic_pw [affinity: 0xff] pid: 693 tgid: 693 cpu: 5 prio: 49 start: 0xffffff8047c3cb00
state: 0x2[D] exit_state: 0x0 stack base: 0xffffffc00e648000
Last_enqueued_ts: 47.729509516 Last_sleep_ts: 47.729527797
Stack:
[<ffffffc009041410>] __switch_to+0x164
[<ffffffc009041c14>] __schedule+0x624
[<ffffffc009042084>] schedule+0x84
[<ffffffc00868d7c0>] __bio_queue_enter[jt]+0x7c
[<ffffffc00869d174>] blk_mq_submit_bio+0x134
[<ffffffc00868f850>] __submit_bio+0xbc
[<ffffffc00868de78>] submit_bio_noacct_nocheck+0x1fc
[<ffffffc00868e1cc>] submit_bio_noacct[jt]+0x350
[<ffffffc00868e538>] submit_bio+0x124
[<ffffffc00841a294>] submit_bh_wbc.llvm.9013634744024665527+0x134
[<ffffffc00841b67c>] block_read_full_folio+0x434
[<ffffffc008684fd8>] blkdev_read_folio.llvm.18044127471816345542+0x1c
[<ffffffc0082da234>] filemap_read_folio+0x6c
[<ffffffc0082dafac>] do_read_cache_folio.llvm.15198525227592925442+0x144
[<ffffffc0082db1c4>] read_cache_page+0x1c
[<ffffffc00222f9f4>] block2mtd_write[block2mtd]+0xb8
[<ffffffc0021e0258>] mtd_write_oob[mtd]+0x1a4
[<ffffffc0021e007c>] mtd_write[mtd]+0x40
[<ffffffc0021bd3ec>] mtdoops_write[mtdoops]+0xe4
[<ffffffc0021bde40>] mtdoops_do_dump[mtdoops]+0x278
[<ffffffc0021bd910>] pwrkey_long_press_irq_event[mtdoops]+0x14
[<ffffffc0080f0508>] raw_notifier_call_chain+0x58
[<ffffffc001ef9838>] pm8941_pwrkey_irq[pm8941_pwrkey][jt]+0x1b0
[<ffffffc008156e44>] irq_thread_fn+0x44
[<ffffffc008156bf0>] irq_thread+0x164
[<ffffffc0080ee6c4>] kthread+0x10c
[<ffffffc00801491c>] ret_from_fork+0x10
Task name: irq/141-pmic_pw (PID 693)
CPU: 5, 状态: D(不可中断睡眠)
这说明它是 电源键(PMIC_PWRKEY)中断线程,也就是 pm8941_pwrkey 驱动的中断处理线程,它现在 卡在了某个阻塞点,未完成执行。
如此我们可以确定卡住的线程为irq/141-pmic_pw
三、根本原因分析
🔍栈回溯分析重点
从你贴出的内核栈,我们可以还原出这个线程的逻辑路径:
pm8941_pwrkey_irq() <-- 电源键中断处理
↳ mtdoops_do_dump() <--- 中断处理函数的notifier回调函数
↳ block2mtd_write()
↳ submit_bio() <-- 向 block 层提交 IO 写操作
↳ schedule() <-- 卡在 bio 提交等待(D 状态)
它正在尝试在 PM suspend 阶段执行mtdoops dump(异常写入),但是提交的 bio 操作阻塞了,线程状态为 D(不可中断睡眠),无法被唤醒。
而在 suspend/resume 流程中:
🌧️ 任意一个线程处于不可中断睡眠状态(D)且未完成 resume,都会阻塞整个系统 resume 流程,导致无法喂狗。
💣 进一步的根因推测:
这个线程中断发生时系统正在 suspend,中断触发写入(mtdoops_do_dump)流程,而底层 block device(比如 eMMC 或 UFS)已经挂起或者 IO 通道被冻结,因此:
- 提交 bio 时卡在 block queue,无法继续执行
- thread 被锁死在不可中断状态
- 所以 resume 无法完成,watchdog 20 秒后触发 bite!
四、解决方案
当前调用链结构
pm8941_pwrkey_irq() // IRQ handler
└─ raw_notifier_call_chain() // 通知链调用
└─ pwrkey_long_press_irq_event() // mtdoops 中注册的回调
└─ mtdoops_do_dump() // 💥直接在中断上下文中执行 dump(有 IO)
问题在于:你在 IRQ thread 里调用了 mtdoops_do_dump(),而 do_dump() 内部会提交阻塞的 bio 写操作,一旦 block 层 suspend,就必然死锁。
那我们就可以想办法避免 suspend 时执行 mtdoops_do_dump
延迟执行 mtdoops_do_dump() 到 resume 之后
我们将:
- 在
pwrkey_long_press_irq_event()中仅设置一个标志 - 在系统 resume 后 通过
workqueue异步触发 dump
////mtdoops.c
1️⃣ 在 mtdoops.c 中定义全局状态:
#include <linux/pm.h>
#include <linux/workqueue.h>
static atomic_t dump_pending = ATOMIC_INIT(0);
static struct work_struct deferred_dump_work;
2️⃣ 替换 pwrkey_long_press_irq_event() 函数
static int pwrkey_long_press_irq_event(struct notifier_block *this, unsigned long event,
void *ptr)
{
pr_info("pwrkey irq: deferring mtdoops dump until resume\n");
atomic_set(&dump_pending, 1);
return NOTIFY_DONE;
}
3️⃣ 添加 resume 通知钩子
static void do_deferred_dump(struct work_struct *work)
{
struct mtdoops_context *cxt = &oops_cxt;
pr_info("Executing deferred mtdoops dump after resume\n");
mtdoops_do_dump(&cxt->dump, MTD_DUMP_LONG_PRESS);
}
static int mtdoops_pm_notifier(struct notifier_block *nb, unsigned long action, void *data)
{
if (action == PM_POST_SUSPEND) {
if (atomic_read(&dump_pending)) {
atomic_set(&dump_pending, 0);
schedule_work(&deferred_dump_work);
}
}
return NOTIFY_OK;
}
static struct notifier_block mtdoops_resume_nb = {
.notifier_call = mtdoops_pm_notifier,
};
4️⃣ 在 mtdoops_init() 初始化中添加
INIT_WORK(&deferred_dump_work, do_deferred_dump);
register_pm_notifier(&mtdoops_resume_nb);
五、效果
- 在电源键中断触发时,不再执行阻塞的 dump
- 系统 resume 完成后,再通过内核线程安全地 dump
- 完全避免 suspend 卡死
- 非侵入性修改,兼容性强
六、测试建议
- 在按电源键时打印
defer dump日志是否出现 - 挂起后唤醒,看是否出现
Executing deferred mtdoops dump after resume - 观察是否还会触发 watchdog bite