作为稳定性工程师,我们在分析死机问题时,尤其是watchdog触发的死机问题时,不可避免的会查看timer_list或者hrtimer,去查看喂狗线程的状态。

timer list dump

Timer List Dump

--------------------------------------------------------------------------------------------------------------------------------------
CPU 0
--------------------------------------------------------------------------------------------------------------------------------------
BASE_STD     (tvec_base: ffffff82f2713240 timer_jiffies: 4297628107(10943.248s) next_timer: 4297628108(10943.252s) active_timers: NA)
+ vectors Timers (14)

	INDEX  TIMER_LIST_ADDR    EXPIRES        EXPIRES(s)     FUNCTION                                 WORK                                                 REMARKS
	12     ffffffc01441bb78   4297628108     10943.252s     process_timeout                                                                               
	23     ffffffc014413b78   4297628119     10943.296s     process_timeout                                                                               
	66     ffffffc00a20cf20   4297628173     10943.512s     delayed_work_timer_fn                    toggle_allocation_gate                               
	117    ffffffc01f9aaf40   4297628580     10945.14s      delayed_work_timer_fn                    msm_vidc_stats_handler[msm_video]                    
	144    ffffff8003d52120   4297630694     10953.596s     qcom_wdt_pet_task_wakeup[qcom_wdt_core]                                                       
	170    ffffff805ff92238   4297628247     10943.808s     delayed_work_timer_fn                    fg_monitor_workfunc[fg_bq28z610]                     
	173    ffffff82f27130b8   4297628435     10944.56s      delayed_work_timer_fn                    kfree_rcu_monitor                                    
	177    ffffff800262d750   4297628722     10945.708s     blk_rq_timed_out_timer                                                                        
	182    ffffffc011b2bd38   4297629019     10946.896s     process_timeout                                                                               
	185    ffffff80039e2a18   4297629218     10947.692s     delayed_work_timer_fn                    wb_workfn                                            
	191    ffffff80cca00690   4297629610     10949.26s      idletimer_tg_expired                                                                          
	213    ffffffc00f273d48   4297631896     10958.404s     process_timeout                                                                               
	283    ffffffc011b0bd18   4297698154     11223.436s     process_timeout                                                                               
	283    ffffffc00b3f3d18   4297698153     11223.432s     process_timeout                                                                               


BASE_DEF     (tvec_base: ffffff82f27144c0 timer_jiffies: 4297628001(10942.824s) next_timer: 4297628256(10943.844s) active_timers: NA)
+ vectors Timers (6)

	INDEX  TIMER_LIST_ADDR    EXPIRES        EXPIRES(s)     FUNCTION                                 WORK                                                 REMARKS
	76     ffffff82f2720fd8   4297628250     10943.82s      delayed_work_timer_fn                    vmstat_update                                        
	76     ffffffc00a088258   4297628250     10943.82s      delayed_work_timer_fn                    vmstat_shepherd                                      
	172    ffffffc00a1f27b0   4297628391     10944.384s     writeout_period                                                                               
	218    ffffffc00a1c3388   4297634636     10969.364s     wq_watchdog_timer_fn                                                                          
	266    ffffff8002728050   4297626482     10936.748s     idle_worker_timeout                                                                           
	271    ffffff82f2722fd0   4297647479     11020.736s     idle_worker_timeout                                                                           

...

hrtimer info

hrtimer info: 
 
 CPU 0 hrtimer_bases  v.v (struct hrtimer_cpu_base)0xffffff82f2715740  
     hrtimer_cpu_base 0xffffff82f2715780 
		 hrtimer     								function    																		_softexpires					  _softexpires
         v.v (struct hrtimer *)0xffffff82f2715d28  0xffffffc00819b23c ('tick_sched_timer', 0)                                            10943240000000                    10943240000000                  
         v.v (struct hrtimer *)0xffffffc02f4a3d98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943250362854                    10943250412854                  
         v.v (struct hrtimer *)0xffffffc029a6bc98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943256967130                    10943257017130                  
         v.v (struct hrtimer *)0xffffffc04353bbf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943302770459                    10943305270457                  
         v.v (struct hrtimer *)0xffffff80470e8168  0xffffffc0088deb50 ('pm_suspend_timer_fn', 0)                                         10943287661708                    10943350161708                  
         v.v (struct hrtimer *)0xffffffc020bfbc98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943377049095                    10943377099095                  
         v.v (struct hrtimer *)0xffffffc01a07bbf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943320656661                    10943384106653                  
         v.v (struct hrtimer *)0xffffffc0202c3bf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943406283583                    10943406475581                  
         v.v (struct hrtimer *)0xffffffc02baabc98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943416557128                    10943416607128                  
         v.v (struct hrtimer *)0xffffffc0147b3d98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943436796292                    10943436846292                  
         v.v (struct hrtimer *)0xffffffc02be53c98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943497224311                    10943497274311                  
         v.v (struct hrtimer *)0xffffffc01b363bf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943514320666                    10943514688663                  
         v.v (struct hrtimer *)0xffffffc02bdc3c98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943523970146                    10943524020146                  
         v.v (struct hrtimer *)0xffffffc01a043bf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943692678375                    10943693608372                  
         v.v (struct hrtimer *)0xffffffc01a6e3bf8  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943752190302                    10943754190301                  
         v.v (struct hrtimer *)0xffffffc02c5ebc98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943756620199                    10943756670199                  
         v.v (struct hrtimer *)0xffffffc02b07bc98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943786910167                    10943786960167                  
         v.v (struct hrtimer *)0xffffffc01eab3c98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10943950481292                    10943950531292                  
         v.v (struct hrtimer *)0xffffffc00a1c5818  0xffffffc008108688 ('sched_rt_period_timer', 0)                                       10944008000000                    10944008000000                  
         v.v (struct hrtimer *)0xffffffc019013d98  0xffffffc0081876b8 ('hrtimer_wakeup', 0)                                              10944058200719                    10944058250719                  

所以我们需要了解这两个是什么?干什么用的?在分析喂狗线程中的作用?以及如何从这些定时器信息中获取有效信息?

定时器的种类

在 Linux 内核中,定时器是实现延时执行和周期性任务的基础设施。随着系统对实时性要求的不断提高,内核提供了两种定时器机制:低精度定时器(基于 timer_list)和高精度定时器(基于 hrtimer)。本文将从机制原理、使用方法、性能对比等方面对两者进行深入剖析。

低精度定时器timer_list

实现机制

  • 时钟节拍(tick):内核通过固定频率的时钟中断(由 CONFIG_HZ 决定,典型值有 100、250、300、1000)维护全局 jiffies 计数。

  • 数据结构struct timer_list定时轮(timer wheel)结合,管理注册的定时器。

  • 触发方式:到期时刻到达下一个时钟节拍,执行钩子函数,存在最多一个节拍周期的抖动。

核心数据结构

struct timer_list {
	/*
	 * All fields that change during normal runtime grouped to the
	 * same cacheline
	 */
	struct hlist_node	entry;
	unsigned long		expires;
	void			(*function)(struct timer_list *);
	u32			flags;

#ifdef CONFIG_LOCKDEP
	struct lockdep_map	lockdep_map;
#endif

	ANDROID_KABI_RESERVE(1);
	ANDROID_KABI_RESERVE(2);
};

成员

类型

作用与说明

entry

struct hlist_node

用于将定时器插入到定时轮(timer wheel)的链表中进行管理;内核根据 expires 选择对应槽(bucket),将该节点挂入对应链表。

expires

unsigned long

存储定时器到期时刻,以全局 jiffies 为单位。当 jiffies >= expires 时,定时器触发;使用有符号比较处理溢出,粒度由 HZ 决定(如 HZ=1000 时为 1 ms)。

function

void (*)(struct timer_list *)

到期后执行的回调函数指针,接受自身指针作为参数,以便回调中访问定时器上下文。通常通过 timer_setup() 注册。

flags

u32

内部状态标志位,标记定时器状态,如 TIMER_STATE_UNUSED(未初始化/已删除)、TIMER_STATE_ENQUEUED(已入队)、TIMER_STATE_CALLBACK_RUNNING(回调中)等,通过原子操作设置与检查。

lockdep_map
(CONFIG_LOCKDEP)

struct lockdep_map (可选)

锁依赖检查(Lockdep)用的数据结构,仅在开启 CONFIG_LOCKDEP 时存在,用于跟踪定时器回调中可能获取的其他锁,帮助检测死锁风险。

ANDROID_KABI_RESERVE(1,2)

保留填充

Android 内核为保持 ABI 稳定预留的填充字段,便于未来扩展新成员而不破坏已有模块接口,对普通 Linux 内核模块开发可忽略。

核心api

下面是 timer_list 的核心 API 及其功能说明,汇总为表格形式:

API

头文件

原型

功能说明

timer_setup

<linux/timer.h>

void timer_setup(struct timer_list *timer,
void (*callback)(struct timer_list *),
unsigned int flags);

初始化一个 timer_list 对象,设置回调函数和标志;推荐用于新代码。

add_timer

<linux/timer.h>

int add_timer(struct timer_list *timer);

添加已初始化并设置好 expires 的定时器到内核定时轮;到期后回调将执行。

mod_timer

<linux/timer.h>

int mod_timer(struct timer_list *timer, unsigned long expires);

修改定时器的超时时刻:如果定时器已在队列中,则更新时间;否则相当于 add_timer

del_timer

<linux/timer.h>

int del_timer(struct timer_list *timer);

删除定时器,如果定时器回调正在执行则不等待(回调结束后不会再被触发)。

del_timer_sync

<linux/timer.h>

int del_timer_sync(struct timer_list *timer);

删除并同步:确保定时器回调正在执行时等待其完成,再返回;适用于需要与回调并发交互的场景。

del_timer_pending

<linux/timer.h>

bool del_timer_pending(struct timer_list *timer);

如果定时器未到期且已入队,则删除并返回 true;否则不做操作返回 false

timer_pending

<linux/timer.h>

bool timer_pending(const struct timer_list *timer);

检查定时器是否已添加到队列且尚未触发,返回 true 表示仍在等待。

setup_timer (旧)

<linux/timer.h>

void setup_timer(struct timer_list *timer,
void (*callback)(unsigned long), unsigned long data);

早期版本 API,设置回调和回调参数;已被 timer_setup 取代,不推荐新代码使用。

init_timer (旧)

<linux/timer.h>

void init_timer(struct timer_list *timer);

最原始的定时器初始化,仅清零结构体;后续需手动赋值回调、expires 等字段;已不推荐使用。

round_jiffies

<linux/timer.h>

unsigned long round_jiffies(unsigned long expires);

将给定的 expires 向上对齐到下一个节拍边界,降低多个定时器同时触发带来的抖动;常用于减少回调集中度。

简单的timer_list驱动

// timer_hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/timer.h>
#include <linux/jiffies.h>

#define INTERVAL_SEC   1  /* 间隔秒数 */

static struct timer_list hello_timer;

/* 定时器回调函数 */
static void hello_timer_callback(struct timer_list *t)
{
    pr_info("hello world\n");

    /* 重新设置定时器,1s 后再次触发 */
    mod_timer(&hello_timer, jiffies + msecs_to_jiffies(INTERVAL_SEC * 1000));
}

/* 模块加载时调用 */
static int __init hello_timer_init(void)
{
    pr_info("hello_timer: initializing\n");

    /* 初始化 timer_list 并指定回调 */
    timer_setup(&hello_timer, hello_timer_callback, 0);

    /* 启动定时器:jiffies + 1s */
    mod_timer(&hello_timer, jiffies + msecs_to_jiffies(INTERVAL_SEC * 1000));

    return 0;
}

/* 模块卸载时调用 */
static void __exit hello_timer_exit(void)
{
    int ret;

    /* 删除定时器并等待正在运行的回调结束 */
    ret = del_timer_sync(&hello_timer);
    if (ret)
        pr_warn("hello_timer: callback was still running\n");

    pr_info("hello_timer: exiting\n");
}

module_init(hello_timer_init);
module_exit(hello_timer_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple timer_list example that prints 'hello world' every second");

每隔 1 秒,内核日志中就会输出一次 “hello world”。

高精度定时器hrtimer

实现机制

  • 硬件时钟源
    hrtimer 可绑定到多种高分辨率硬件时钟源,如 HPET、APIC timer 或 CPU TSC。

  • 软件结构
    内核维护一棵红黑树(rbtree),按到期时间排序所有活跃的 hrtimer

  • 触发流程

    1. 用户调用 hrtimer_start() 安排一个到期时间点(ktime_t)。

    2. 内核将其插入红黑树,并编程硬件定时器产生一次中断。

    3. 当硬件中断到来,内核执行软中断(softirq),遍历红黑树中所有已到期的 hrtimer,依次调用它们的回调。

  • 精度与开销

    • 精度可达几十—几百纳秒,抖动远小于 1 tick(如配置 HZ=1000 的 1ms)。

    • 每个到期点会产生专属中断,频繁小定时器会带来较多中断和软中断开销。

核心数据结构

struct hrtimer {
	struct timerqueue_node		node;
	ktime_t				_softexpires;
	enum hrtimer_restart		(*function)(struct hrtimer *);
	struct hrtimer_clock_base	*base;
	u8				state;
	u8				is_rel;
	u8				is_soft;
	u8				is_hard;

	ANDROID_KABI_RESERVE(1);
};

成员

类型

作用与说明

node

struct timerqueue_node

红黑树或定时队列中的节点,用于在内核维护的定时器队列或层次化定时轮中插入和管理该定时器。

_softexpires

ktime_t

软件层面上的到期时间(绝对时间),在软中断上下文中用于触发回调;与硬件定时器的到期时间可能略有差别,用于统一调度处理。

function

enum hrtimer_restart (*)(struct hrtimer *)

定时器到期时调用的回调函数指针;返回值 HRTIMER_NORESTARTHRTIMER_RESTART 决定是否自动重启定时器。

base

struct hrtimer_clock_base *

指向所绑定的时钟基准(hrtimer_clock_base),包含硬件定时器编程、队列管理等函数和上下文;决定该定时器使用的时钟源(如 CLOCK_MONOTONICCLOCK_REALTIME)。

state

u8

状态标志,指示定时器当前在队列中的状态:未启用、已启用待触发、正在触发回调等;通过位域操作进行管理。

is_rel

u8

是否为相对定时器标志(REL):如果为 1,启动时会使用 current_time + _softexpires 作为实际到期时间;否则按绝对时间处理。

is_soft

u8

是否为软中断(softirq)触发的定时器标志:1 表示到期后通过软中断上下文执行回调;0 表示硬中断或专用中断上下文触发。

is_hard

u8

是否为硬中断(hardirq)触发的定时器标志:1 表示到期时在硬中断上下文立即执行回调,抖动最低;0 则依赖软中断或其他机制触发。

ANDROID_KABI_RESERVE(1)

保留填充

Android 内核为 ABI 稳定性预留的字段,便于未来扩展新成员而不破坏已有二进制兼容性;对一般 Linux 内核模块开发可忽略。

核心api

API

头文件

原型

功能说明

hrtimer_init

<linux/hrtimer.h>

void hrtimer_init(struct hrtimer *timer,
clockid_t which_clock,
enum hrtimer_mode mode);

初始化一个 hrtimer,指定时钟源(如 CLOCK_MONOTONIC)和定时模式(相对/绝对)。

hrtimer_start

<linux/hrtimer.h>

int hrtimer_start(struct hrtimer *timer, ktime_t tim, enum hrtimer_mode mode);

启动或重启定时器,到期时刻为 tim(相对或绝对视 mode 而定);返回非零表示已在运行并被重编程。

hrtimer_forward_now

<linux/hrtimer.h>

ktime_t hrtimer_forward_now(struct hrtimer *timer, ktime_t interval);

在当前时间基础上前移定时器到下一个周期(常用于周期性定时);返回旧的到期时间。

hrtimer_cancel

<linux/hrtimer.h>

int hrtimer_cancel(struct hrtimer *timer);

取消一个定时器;如果回调在运行返回非零,否则返回零;不会等待回调结束。

hrtimer_active

<linux/hrtimer.h>

bool hrtimer_active(const struct hrtimer *timer);

判断定时器是否已被启动且尚未到期。

hrtimer_try_to_cancel

<linux/hrtimer.h>

int hrtimer_try_to_cancel(struct hrtimer *timer);

试图取消定时器,并保证回调不在并发运行;类似 del_timer_sync 的效果。

简单的hrtimer定时器驱动

// hrtimer_hello.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

#define INTERVAL_NS    (1e9)  // 1秒 = 1e9 纳秒

static struct hrtimer hello_hrtimer;
static ktime_t      hello_period;

/* 定时器回调 */
static enum hrtimer_restart hello_fn(struct hrtimer *timer)
{
    pr_info("hello hrtimer\n");

    /* 周期性:基于上次到期时间前移一个周期 */
    hrtimer_forward_now(timer, hello_period);
    return HRTIMER_RESTART;
}

/* 模块加载 */
static int __init hrtimer_hello_init(void)
{
    pr_info("hrtimer_hello: init\n");

    /* 初始化:使用单调时钟,REL 模式 */
    hrtimer_init(&hello_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
    hello_hrtimer.function = hello_fn;

    /* 设置周期 */
    hello_period = ktime_set(0, INTERVAL_NS);

    /* 启动定时器:当前时刻 + 1s */
    hrtimer_start(&hello_hrtimer, hello_period, HRTIMER_MODE_REL);
    return 0;
}

/* 模块卸载 */
static void __exit hrtimer_hello_exit(void)
{
    int ret;

    /* 取消定时器,保证回调结束 */
    ret = hrtimer_cancel(&hello_hrtimer);
    if (ret)
        pr_warn("hrtimer_hello: callback was running\n");

    pr_info("hrtimer_hello: exit\n");
}

module_init(hrtimer_hello_init);
module_exit(hrtimer_hello_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("An hrtimer example that prints every second");

基于硬件的watchdog timer

我们的目的其实就是学习timer在watchdog的应用,所以在上面timeer_list hrtimer 介绍之后,本章就着重了解一watchdog timer

详细代码流程见:https://www.iliuqi.com/archives/android-stability-016

watchdog timer的使用

kernel_platform/msm-kernel/drivers/soc/qcom/qcom_wdt_core.c

static int qcom_wdt_init(struct msm_watchdog_data *wdog_dd,
			struct platform_device *pdev)
{
    //...
    timer_setup(&wdog_dd->pet_timer, qcom_wdt_pet_task_wakeup, 0);
	wdog_dd->pet_timer.expires = jiffies + delay_time;
	add_timer(&wdog_dd->pet_timer);
	timer_setup(&wdog_dd->user_pet_timer, qcom_wdt_user_pet_bite, 0);
    //...
}

这里注册了两pet_timer ,其user_pet_timer 是提供给用户空间的一个接口,可以通过这个接口手动触发watchdog bite

void qcom_wdt_trigger_bite(void)
{
	if (!wdog_data)
		return;
	compute_irq_count();
	dev_err(wdog_data->dev, "Causing a QCOM Apps Watchdog bite!\n");
	wdog_data->ops->show_wdt_status(wdog_data);
	wdog_data->ops->set_bite_time(1, wdog_data);
	wdog_data->ops->reset_wdt(wdog_data);
	/* Delay to make sure bite occurs */
	mdelay(10000);
	/*
	 * This function induces the non-secure bite and control
	 * should not return to the calling function. Non-secure
	 * bite interrupt is affined to all the cores and it may
	 * not be handled by the same cores which configured
	 * non-secure bite. So add forever loop here.
	 */
	while (1)
		udelay(1);

}

本章节着重理清另一个timer的流程,也就wdog_dd->pet_timer

wdog_dd->pet_timer 在函qcom_wdt_init 中被初始化并设置了定时周delay_time ,这个周期通CONFIGQCOMWATCHDOG_PET_TIME 配置!小米项目一般设置为15s。

喂狗线程函数为qcom_wdt_pet_task_wakeup

static void qcom_wdt_pet_task_wakeup(struct timer_list *t)
{
	struct msm_watchdog_data *wdog_dd =
		from_timer(wdog_dd, t, pet_timer);
	wdog_dd->timer_expired = true;
	wdog_dd->timer_fired = sched_clock();
	wake_up(&wdog_dd->pet_complete);
}

步骤

操作

作用

1

from_timer(wdog_dd, t, pet_timer);

根据传入的 timer_list *t 获取所属的 wdog_dd

2

wdog_dd->timer_expired = true;

标记看门狗任务已过期

3

wdog_dd->timer_fired = sched_clock();

记录高精度时间戳,用于后续调试或统计

4

wake_up(&wdog_dd->pet_complete);

唤醒等待在 pet_complete 等待队列的线程

这个函数唤醒wdog_dd->pet_complete ,而这个对应的就是watchdog线程


static __ref int qcom_wdt_kthread(void *arg)
{
	struct msm_watchdog_data *wdog_dd = arg;
	unsigned long delay_time = 0;
	struct sched_param param = {.sched_priority = MAX_RT_PRIO-1};
	int ret, cpu;

	sched_setscheduler(current, SCHED_FIFO, &param);
	while (!kthread_should_stop()) {
		do {
			ret = wait_event_interruptible(wdog_dd->pet_complete,
						wdog_dd->timer_expired);  
		} while (ret != 0);

		wdog_dd->thread_start = sched_clock();
		for_each_cpu(cpu, cpu_present_mask)
			wdog_dd->ping_start[cpu] = wdog_dd->ping_end[cpu] = 0;

		if (wdog_dd->do_ipi_ping)
			qcom_wdt_ping_other_cpus(wdog_dd);

		do {
			ret = wait_event_interruptible(wdog_dd->pet_complete,
						wdog_dd->user_pet_complete);
		} while (ret != 0);

		wdog_dd->timer_expired = false;
		wdog_dd->user_pet_complete = !wdog_dd->user_pet_enabled;

		if (wdog_dd->enabled) {
			delay_time = msecs_to_jiffies(wdog_dd->pet_time);
			wdog_dd->ops->reset_wdt(wdog_dd);
			wdog_dd->last_pet = sched_clock();
		}
		/* Check again before scheduling
		 * Could have been changed on other cpu
		 */
		if (!kthread_should_stop()) {
			spin_lock(&wdog_dd->freeze_lock);
			if (!wdog_dd->freeze_in_progress)
				mod_timer(&wdog_dd->pet_timer,
					jiffies + delay_time);
			spin_unlock(&wdog_dd->freeze_lock);
		}

		record_irq_count();
	}
	return 0;
}

操作 / 代码片段

功能说明

sched_setscheduler(current, SCHED_FIFO, &param);

将当前线程调度策略设为实时FIFO,优先级 MAX_RT_PRIO-1,保证高响应性

while (!kthread_should_stop()) { … }

主循环,在收到停止请求(kthread_stop())前持续执行

wait_event_interruptible(wdog_dd->pet_complete, wdog_dd->timer_expired);

阻塞等待“定时器到期”事件,由 qcom_wdt_pet_task_wakeup 设置 timer_expired=true 并唤醒

wdog_dd->thread_start = sched_clock();
for_each_cpu(...) ping_start/ ping_end = 0;

记录线程被唤醒的时间戳,并将所有在线 CPU 的 ping 时间数据清零

if (wdog_dd->do_ipi_ping)
qcom_wdt_ping_other_cpus(wdog_dd);

(可选)向其它 CPU 发送 IPI,以测量各核响应时延

wait_event_interruptible(wdog_dd->pet_complete, wdog_dd->user_pet_complete);

阻塞等待“用户喂狗”事件,直到 user_pet_completetrue

wdog_dd->timer_expired = false;
wdog_dd->user_pet_complete = !wdog_dd->user_pet_enabled;

清除“定时到期”标志;若用户喂狗功能被禁用,则直接跳过等待

if (wdog_dd->enabled) {
  delay_time = msecs_to_jiffies(pet_time);
  ops->reset_wdt(wdog_dd);
  last_pet = sched_clock();
}

若看门狗已启用,调用硬件接口重置定时器,并记录本次喂狗时间;计算下一次间隔 delay_time

if (!kthread_should_stop()) {
  spin_lock(&freeze_lock);
  mod_timer(&pet_timer, jiffies + delay_time);
  spin_unlock(&freeze_lock);
}

在未停止线程且未冻结时,用更新后的 delay_time 重编程 pet_timer,安排下一轮超时触发

record_irq_count();

统计并记录本次循环中断次数等指标,用于后续调试或性能监控

watchdog suspend/wakeup

作为一个timer,有一点无法避免开,也就是suspend/wakeup时如何处理!

static const struct dev_pm_ops qcom_soc_dev_pm_ops = {
#ifdef CONFIG_PM_SLEEP
	.suspend_late = qcom_wdt_pet_suspend,
	.resume_early = qcom_wdt_pet_resume,
#endif
	.freeze_late = qcom_wdt_pet_suspend,
	.restore_early = qcom_wdt_pet_resume,
};

在 Qualcomm 看门狗驱动中,上述 dev_pm_ops 定义了在系统进入 Suspend/Freeze 以及退出 Resume/Restore 时,各个阶段要调用的回调函数。具体对应关系和执行时序如下:

PM 阶段

回调名称

调用时机

执行函数

主要作用

系统挂起(Suspend)

.suspend_late

suspend 过程中 “晚” 阶段

qcom_wdt_pet_suspend

- 标记进入冻结状态(freeze_in_progress = true
- 同步删除定时器(del_timer_sync),防止回调在低功耗时触发
- 唤醒阻塞线程,避免挂起期间线程死锁

系统恢复(Resume)

.resume_early

resume 过程 “早” 阶段

qcom_wdt_pet_resume

- 清除冻结标志(freeze_in_progress = false
- 重新编程定时器(mod_timer),恢复下次超时触发
- 线程继续进入正常等待/喂狗循环

运行时冻结(Freeze)

.freeze_late

Runtime PM Freeze “晚” 阶段

qcom_wdt_pet_suspend

.suspend_late,在运行时省电(runtime suspend)场景下也要做同样的“冻结”处理

运行时恢复(Restore)

.restore_early

Runtime PM Resume “早” 阶段

qcom_wdt_pet_resume

.resume_early,在运行时恢复时重新启动定时器和线程循环

qcom_wdt_pet_suspend

int qcom_wdt_pet_suspend(struct device *dev)
{
	struct msm_watchdog_data *wdog_data =
			(struct msm_watchdog_data *)dev_get_drvdata(dev);

	if (!wdog_data)
		return 0;

	if (wdog_data->user_pet_enabled)
		del_timer_sync(&wdog_data->user_pet_timer);

	spin_lock(&wdog_data->freeze_lock);
	wdog_data->freeze_in_progress = true;
	spin_unlock(&wdog_data->freeze_lock);
	wdog_data->ops->reset_wdt(wdog_data);
	del_timer_sync(&wdog_data->pet_timer);
	if (wdog_data->wakeup_irq_enable) {
		if (wdog_data->hibernate || (pm_suspend_target_state == PM_SUSPEND_MEM)) {
			wdog_data->ops->disable_wdt(wdog_data);
			wdog_data->enabled = false;
		}
		wdog_data->last_pet = sched_clock();
		return 0;
	}

	wdog_data->ops->disable_wdt(wdog_data);
	wdog_data->enabled = false;
	wdog_data->last_pet = sched_clock();
	return 0;
}
  • 挂起前先同步删除所有定时器并“喂狗”一次,防止系统在进入低功耗后因定时器误触发或超时重启。

  • freeze_in_progress 标志阻止 kthread 在挂起期间重新编程主定时器。

  • 根据是否支持唤醒中断,以及挂起类型(休眠 vs 挂起),有条件地禁用硬件看门狗,并标记 enabled=false

qcom_wdt_pet_resume

int qcom_wdt_pet_resume(struct device *dev)
{
	uint32_t val;
	struct msm_watchdog_data *wdog_data =
			(struct msm_watchdog_data *)dev_get_drvdata(dev);
	unsigned long delay_time = 0;

	if (!wdog_data)
		return 0;

	val = BIT(EN);
	if (wdog_data->user_pet_enabled) {
		delay_time = msecs_to_jiffies(wdog_data->bark_time + 3 * 1000);
		wdog_data->user_pet_timer.expires = jiffies + delay_time;
		add_timer(&wdog_data->user_pet_timer);
	}

	delay_time = msecs_to_jiffies(wdog_data->pet_time);
	spin_lock(&wdog_data->freeze_lock);
	wdog_data->pet_timer.expires = jiffies + delay_time;
	add_timer(&wdog_data->pet_timer);
	wdog_data->freeze_in_progress = false;
	spin_unlock(&wdog_data->freeze_lock);
	if (wdog_data->wakeup_irq_enable) {
		if (wdog_data->hibernate || (pm_suspend_target_state == PM_SUSPEND_MEM)) {
			wdog_data->ops->set_bark_time(wdog_data->bark_time, wdog_data);
			wdog_data->ops->set_bite_time(wdog_data->bark_time + 10 * 1000, wdog_data);
			val |= BIT(UNMASKED_INT_EN);
			wdog_data->ops->enable_wdt(val, wdog_data);
			wdog_data->enabled = true;
		}
		wdog_data->ops->reset_wdt(wdog_data);
		wdog_data->last_pet = sched_clock();
		return 0;
	}

	wdog_data->ops->enable_wdt(val, wdog_data);
	wdog_data->ops->reset_wdt(wdog_data);
	wdog_data->enabled = true;
	wdog_data->last_pet = sched_clock();
	return 0;
}

总的来说,qcom_wdt_pet_resume() 在系统从 Suspend/Freeze 恢复后,会依次重启(或新建)所有相关定时器,解除冻结,并根据硬件和配置重新启用并复位看门狗,使“等待→喂狗→重编程”循环无缝继续。

Timer list dump

作为稳定性工程师,timer_list dump我们其实并不陌生,大概的显示如下:

Timer List Dump

--------------------------------------------------------------------------------------------------------------------------------------
CPU 0
--------------------------------------------------------------------------------------------------------------------------------------
BASE_STD     (tvec_base: ffffff887724c080 timer_jiffies: 4307926319(52136.096s) next_timer: 4307926528(52136.932s) active_timers: NA)
+ vectors Timers (5)

	INDEX  TIMER_LIST_ADDR    EXPIRES        EXPIRES(s)     FUNCTION                                 WORK                                                 REMARKS
	137    ffffff87a20ec7f8   4307927569     52141.096s     delayed_work_timer_fn                    battery_chg_dfx_monitor_work[qti_battery_charger]    
	137    ffffff805ba62460   4307927566     52141.084s     delayed_work_timer_fn                    wb_workfn                                            
	184    ffffff87cdde38d8   4307926504     52136.836s     neigh_timer_handler                                                                           
	237    ffffff8813f24708   4307933421     52164.504s     tw_timer_handler                                                                              
	248    ffffffc082631058   4307939003     52186.832s     delayed_work_timer_fn                    crng_reseed                                          


BASE_DEF     (tvec_base: ffffff887724d340 timer_jiffies: 4307926316(52136.084s) next_timer: 4307926592(52137.188s) active_timers: NA)
+ vectors Timers (10)

	INDEX  TIMER_LIST_ADDR    EXPIRES        EXPIRES(s)     FUNCTION                                 WORK                                                 REMARKS
	92     ffffffc0825b2850   4307926750     52137.82s      delayed_work_timer_fn                    vmstat_shepherd                                      
	131    ffffffc0826abbe8   4307927191     52139.584s     delayed_work_timer_fn                    neigh_periodic_work                                  
	131    ffffffc0826a5320   4307927191     52139.584s     delayed_work_timer_fn                    neigh_periodic_work                                  
	185    ffffffc0826a5408   4307926566     52137.084s     delayed_work_timer_fn                    neigh_managed_work                                   
	185    ffffffc0826abcd0   4307926566     52137.084s     delayed_work_timer_fn                    neigh_managed_work                                   
	230    ffffff8877a21a10   4307929726     52149.724s     idle_worker_timeout                                                                           
	238    ffffffc08275b380   4307933816     52166.084s     wq_watchdog_timer_fn                                                                          
	251    ffffff887725da10   4307940623     52193.312s     idle_worker_timeout                                                                           
	287    ffffff8877e03a10   4307936006     52174.844s     idle_worker_timeout                                                                           
	293    ffffff887725de88   4307960640     52273.38s      idle_worker_timeout                                                                           

--------------------------------------------------------------------------------------------------------------------------------------
CPU 1
--------------------------------------------------------------------------------------------------------------------------------------
....

下面对这段转储按字段含义和阅读流程做说明:

字段

含义

CPU 0

本页数据针对 CPU 0 的软定时器队列。

BASE_STD / BASE_DEF

定时器所挂载的两类 tvec_base(标准刻度与延迟刻度)——Linux 为了效率,会分两套不同粒度的定时器梯度管理。

tvec_base

内核中该定时器基座结构体的地址。

timer_jiffies

当前系统 jiffies 值(硬件节拍计数),以及换算后的秒数。

next_timer

这条基座上下一次即将触发的定时器的 jiffies 值及秒数。

active_timers

活跃定时器数(此处省略或标为 NA)。

基座名称

含义

触发条件

典型用途

BASE_STD

Standard Timer Base(标准定时器基座)

无论 CPU 是否空闲,只要到达 expires,就会唤醒并执行回调

绝大多数内核定时器(workqueue、网络维护、看门狗等关键定时器)

BASE_DEF

Deferrable Timer Base(可延迟定时器基座)

仅当 CPU 处于“可运行”状态时才触发;若 CPU 正在深度空闲,则推迟到下一次唤醒后才执行

对延迟不敏感、可随工作负载合并触发以节省功耗的定时器

核心区别

  1. 唤醒行为

    • BASE_STD 下的任何定时器到期都会唤醒 CPU,确保定时器回调按时执行。

    • BASE_DEF 下的定时器不强制唤醒,只有在 CPU 本身已被其他事件唤醒时才会运行;如果长期空闲,可无限期延后。

  2. 节能特性

    • 使用 BASE_DEF(通过 timer_setup_deferrable() 或在定时器初始化时设置 TIMER_DEFERRABLE 标志)可以合并同一时间窗口内的非关键任务,减少平台从深度空眠状态唤醒的次数,从而 降低功耗

  3. 配置方式

    • 默认 timer_setup() 创建的定时器属于 BASE_STD

    • 调用 timer_setup_deferrable() 或手动给 struct timer_list 加上 TIMER_DEFERRABLE 标志,则会被插入到 BASE_DEF 链表中。

关于vectors Timers的各列解释如下:

  • INDEX:在该 timer_base 链表或红黑树中的序号,用于调试定位。

  • TIMER_LIST_ADDR:这条定时器对象 struct timer_list 的地址。

  • EXPIRES / EXPIRES(s):下次触发的 jiffies 值及转换的秒数(相对于系统启动后)。

  • FUNCTION:回调函数名,触发时会执行此函数。

  • WORK:若该定时器用于驱动 Delayed Work,则显示对应的 work_struct 名称。

  • REMARKS:其他备注字段,若无特别信息则留空。


从上述我们已经了解到了watchdog timer的使用了,但是有一点当系统处于suspend时,由于qcom_wdt_pet_suspend 会去掉定时器,那在suspend中,难道系统就不会在喂狗了???

这个答案肯定是当然不会喂狗了!

为什么呢?

因为在suspend的函数中,会停止计数器计数,既然计数器都停止了,对于系统来说时间就停止了!

基于软件的软看门狗机制

Linux 的软看门狗(Software Watchdog)是一种基于内核定时器的软件实现,用来在用户态或内核态失去响应时触发系统重启或其他恢复动作。它尤其适用于没有硬件看门狗或需要额外保护层的场景。下面从关键组件和工作流程来介绍:

这个怎么理解呢?举个例子来说明

假设:某个 CPU 核在内核态连续运行超过阈值(默认 20秒),无法切换到其他进程/线程。

while (1) { /* 内核死循环 */ }          // Buggy 驱动代码
spin_lock(&lock); for(;;);                // 死锁且未释放锁
local_irq_disable(); while(1);            // 禁用中断后死循环

该 CPU 上的所有线程(包括关键守护进程)被"饿死",系统部分功能瘫痪。

而软看门狗就是来检测这样的场景的

软看门狗的基本原理

  • 观看者—被观看者模式

    • “观看者”定期检查系统或应用状态;若在预定时间内没有收到“心跳”(keepalive),则视为系统失去响应,触发重启或回调。

  • 基于高精度定时器

    • 使用 hrtimer(high-resolution timer)实现超时检测。

2025/06/halo_nzr4la2.png

1. 核心组件

组件

作用

hrtimer

高精度定时器(默认周期 4s),到期触发 watchdog_timer_fn

watchdog_timer_fn

检查对应 CPU 的看门狗线程是否被调度

watchdog/X 线程

内核线程(实时优先级),定期唤醒并更新 CPU 专属时间戳 watchdog_touch_ts

2. 检测流程

  1. 定时器到期
    hrtimer 到期 → 触发 watchdog_timer_fn(在 中断上下文 执行)

  2. 检查时间戳
    比较当前时间 now 和看门狗线程更新时间戳 watchdog_touch_ts

    if (now - watchdog_touch_ts > softlockup_threshold) { /* 触发警报! */ }
  3. 结果处理

    • 时间戳已更新:重置 hrtimer → 继续监控

    • 时间戳未更新

      • 打印内核警告:BUG: soft lockup - CPU#X stuck for 23s!

      • 触发堆栈回溯(帮助定位阻塞点)

      • 可配置为触发 panic() 使系统崩溃(需手动启用)

内核中的主要实现

模块名

对应文件/配置

定时器类型

核心逻辑

softdog

drivers/watchdog/softdog.c

hrtimer
(CLOCK_MONOTONIC)

- 初始化:hrtimer_init
- 启动:hrtimer_start 设超时
- 到期:回调 softdog_restart() 调用 __wd_do_restart()
- 喂狗:hrtimer_forward_now() 推迟超时

watchdog-core

drivers/watchdog/watchdog_core.c

- 注册 /dev/watchdog 字符设备
- 提供标准接口(WDIOC_KEEPALIVEWDIOC_SETTIMEOUT 等 ioctl)
- 与各硬件/软件 watchdog 驱动对接

watchdog

kernel/watchdog.c

hrtimer

  1. 初始化

    • 在系统启动时,通过 watchdog_register_device() 注册看门狗设备,初始化相关参数(超时、状态、操作函数等)。

  2. 定时器启动

    • 通过 watchdog_ops->start() 启动硬件或软件看门狗定时器,开始计时。

  3. 喂狗操作

    • 用户空间或内核通过 ioctl(WDIOC_KEEPALIVE) 或调用 watchdog_ops->ping() 喂狗,重置看门狗计时器。

  4. 超时处理

    • 如果在设定时间内没有调用 ping(),定时器超时触发,执行 watchdog_hang(),通过 kernel_restart() 或其他动作进行系统重启。

  5. 挂起与恢复

    • 在系统挂起(suspend)时,禁用看门狗定时器,防止误重启。

    • 在恢复时重新启动定时器,并确保看门狗继续正常工作。

软看门狗和soft lockup其实关系非常密切,它们在 Linux 内核中用于检测和处理系统中的软死锁问题(即某些内核线程或进程长时间没有响应,导致系统无法正常运行)。

下面介绍一些常见的debug的内核参数

常用的用于debug的内核参数

通过 /proc/sys/kernel 调整:

参数

默认值

作用

softlockup_panic

0

是否在检测到 lockup 时触发 panic

softlockup_threshold

20 (秒)

触发警报的阻塞时间阈值

watchdog_thresh

10 (秒)

硬锁+软锁公共阈值

nmi_watchdog

1

是否启用 NMI 硬锁检测

soft_watchdog

0

一个控制软看门狗机制的开关。它的作用是控制某些监测和调度机制是否启用,尤其是在检查系统软死锁(soft lockup)时

softlockup_all_cpu_backtrace

0

  • 软死锁检测:当一个 CPU 或多个 CPU 长时间处于没有进行任务调度的状态时(例如,长时间占用一个锁或者陷入繁重的循环中),内核会通过软死锁检测机制发现这一问题。

  • 该回溯信息会帮助我们定位导致软死锁的代码路径,通常在 soft_watchdog 检测到问题后触发。

  • backtrace:即栈回溯,它显示了该 CPU 在发生死锁时所执行的函数调用堆栈信息。通过栈回溯,可以准确知道哪些代码段导致了死锁,并帮助我们定位问题。

softlockup_panic

0

  • 软死锁处理:当系统检测到某些 CPU 处于软死锁状态时(长时间没有响应),它会尝试通过日志记录和栈回溯来找出根本原因。启用 softlockup_panic 后,系统会在检测到软死锁时直接进行 panic 处理,这通常会导致系统崩溃并重新启动。

  • 快速响应:该选项是为了减少因死锁导致的系统无响应时间,直接进行 panic 可以确保系统不会长时间停滞,尽快恢复。

  • 调试和诊断:触发 panic 后,内核会生成内存转储(core dump),我们可以从中提取出更多关于死锁发生时的上下文信息,进一步进行调试。

hrtimer info

在timer_list.txt中的hrtimer info解释

hrtimer info: 
 
 CPU 0 hrtimer_bases  v.v (struct hrtimer_cpu_base)0xffffff887724e600  
     hrtimer_cpu_base 0xffffff887724e6c0 
		 hrtimer     								function    																		_softexpires					  _softexpires
         v.v (struct hrtimer *)0xffffff887724f0b0  0xffffffc080206308 ('tick_sched_timer', 0)                                            52136120000000                    52136120000000                  
         v.v (struct hrtimer *)0xffffff887724f350  0xffffffc080246b80 ('watchdog_timer_fn', 0)                                           52140056000000                    52140056000000                  
         v.v (struct hrtimer *)0xffffffc0b0dfbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140466907210                    52140466957210                  
         v.v (struct hrtimer *)0xffffffc0b288bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140849712578                    52140849762578                  
         v.v (struct hrtimer *)0xffffffc0b5263c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52143244442057                    52143244492057                  
         v.v (struct hrtimer *)0xffffffc0b5743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52147396466222                    52147396516222                  
         v.v (struct hrtimer *)0xffffffc0b4e83c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52148067472002                    52148067522002                  
         v.v (struct hrtimer *)0xffffffc0b5903c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52152023883195                    52152023933195                  
         v.v (struct hrtimer *)0xffffffc0b6f1bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159000004843                    52159000054843                  
         v.v (struct hrtimer *)0xffffffc0b7c6bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159496489915                    52159496539915                  
         v.v (struct hrtimer *)0xffffffc09adf3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52164952366731                    52164952416731                  
         v.v (struct hrtimer *)0xffffffc0b9743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52166107545069                    52166107595069                  
         v.v (struct hrtimer *)0xffffffc0b56bbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52167617888766                    52167617938766                  
         v.v (struct hrtimer *)0xffffffc0b7c9bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52182007604461                    52182007654461                  
         v.v (struct hrtimer *)0xffffffc0b55bbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52182165841596                    52182165891596                  
         v.v (struct hrtimer *)0xffffffc0b28e3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52204781708479                    52204781758479                  
         v.v (struct hrtimer *)0xffffffc0b308bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52221587812571                    52221587862571                  
         v.v (struct hrtimer *)0xffffffc0b21f3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52221833683099                    52221833733099                  
         v.v (struct hrtimer *)0xffffffc0b5a53c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52242753492308                    52242753542308                  
         v.v (struct hrtimer *)0xffffffc099b03d38  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52279756334238                    52279756384238                  
         v.v (struct hrtimer *)0xffffffc0b4913c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52347051698425                    52347051748425                  
         v.v (struct hrtimer *)0xffffffc0b13f3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52692070982210                    52692071032210                  
         v.v (struct hrtimer *)0xffffffc09f94bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52698064978614                    52698065028614                  
         v.v (struct hrtimer *)0xffffffc09f1ebc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52701970378820                    52701970428820                  
         v.v (struct hrtimer *)0xffffffc09dac3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52710075325485                    52710075375485                  
         v.v (struct hrtimer *)0xffffff87ba6f3a00  0xffffffc0804b1a04 ('timerfd_tmrproc', 0)                                             54053001235604                    54053001235604                  
         v.v (struct hrtimer *)0xffffffc09c4d3d30  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              4300382257786162                  4300382257836162                
         v.v (struct hrtimer *)0xffffffc096f8bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              398883794239173412                398883794239223412              
         v.v (struct hrtimer *)0xffffffc09171bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
         v.v (struct hrtimer *)0xffffffc0918e3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
         v.v (struct hrtimer *)0xffffffc08d3b3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
     hrtimer_cpu_base 0xffffff887724e740 
		 hrtimer     								function    																		_softexpires					  _softexpires
         v.v (struct hrtimer *)0xffffff887724f0b0  0xffffffc080206308 ('tick_sched_timer', 0)                                            52136120000000                    52136120000000                  
         v.v (struct hrtimer *)0xffffff887724f350  0xffffffc080246b80 ('watchdog_timer_fn', 0)                                           52140056000000                    52140056000000                  
         v.v (struct hrtimer *)0xffffffc0b0dfbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140466907210                    52140466957210                  
         v.v (struct hrtimer *)0xffffffc0b288bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140849712578                    52140849762578                  
         v.v (struct hrtimer *)0xffffffc0b5263c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52143244442057                    52143244492057                  
         v.v (struct hrtimer *)0xffffffc0b5743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52147396466222                    52147396516222                  
         v.v (struct hrtimer *)0xffffffc0b4e83c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52148067472002                    52148067522002                  
         v.v (struct hrtimer *)0xffffffc0b5903c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52152023883195                    52152023933195                  
         v.v (struct hrtimer *)0xffffffc0b6f1bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159000004843                    52159000054843                  
         v.v (struct hrtimer *)0xffffffc0b7c6bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159496489915                    52159496539915                  
         v.v (struct hrtimer *)0xffffffc09adf3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52164952366731                    52164952416731                  
         v.v (struct hrtimer *)0xffffffc0b9743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52166107545069                    52166107595069                  
         v.v (struct hrtimer *)0xffffffc0b56bbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52167617888766                    52167617938766                  
         v.v (struct hrtimer *)0xffffffc0b7c9bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52182007604461                    52182007654461                  
         v.v (struct hrtimer *)0xffffffc0b55bbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52182165841596                    52182165891596                  
         v.v (struct hrtimer *)0xffffffc0b28e3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52204781708479                    52204781758479                  
         v.v (struct hrtimer *)0xffffffc0b308bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52221587812571                    52221587862571                  
         v.v (struct hrtimer *)0xffffffc0b21f3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52221833683099                    52221833733099                  
         v.v (struct hrtimer *)0xffffffc0b5a53c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52242753492308                    52242753542308                  
         v.v (struct hrtimer *)0xffffffc099b03d38  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52279756334238                    52279756384238                  
         v.v (struct hrtimer *)0xffffffc0b4913c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52347051698425                    52347051748425                  
         v.v (struct hrtimer *)0xffffffc0b13f3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52692070982210                    52692071032210                  
         v.v (struct hrtimer *)0xffffffc09f94bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52698064978614                    52698065028614                  
         v.v (struct hrtimer *)0xffffffc09f1ebc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52701970378820                    52701970428820                  
         v.v (struct hrtimer *)0xffffffc09dac3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52710075325485                    52710075375485                  
         v.v (struct hrtimer *)0xffffff87ba6f3a00  0xffffffc0804b1a04 ('timerfd_tmrproc', 0)                                             54053001235604                    54053001235604                  
         v.v (struct hrtimer *)0xffffffc09c4d3d30  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              4300382257786162                  4300382257836162                
         v.v (struct hrtimer *)0xffffffc096f8bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              398883794239173412                398883794239223412              
         v.v (struct hrtimer *)0xffffffc09171bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
         v.v (struct hrtimer *)0xffffffc0918e3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
         v.v (struct hrtimer *)0xffffffc08d3b3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              9223372036854775807               9223372036854775807             
     hrtimer_cpu_base 0xffffff887724e7c0 
		 hrtimer     								function    																		_softexpires					  _softexpires
         v.v (struct hrtimer *)0xffffff887724f0b0  0xffffffc080206308 ('tick_sched_timer', 0)                                            52136120000000                    52136120000000                  
         v.v (struct hrtimer *)0xffffff887724f350  0xffffffc080246b80 ('watchdog_timer_fn', 0)                                           52140056000000                    52140056000000                  
         v.v (struct hrtimer *)0xffffffc0b0dfbc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140466907210                    52140466957210                  
         v.v (struct hrtimer *)0xffffffc0b288bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52140849712578                    52140849762578                  
         v.v (struct hrtimer *)0xffffffc0b5263c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52143244442057                    52143244492057                  
         v.v (struct hrtimer *)0xffffffc0b5743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52147396466222                    52147396516222                  
         v.v (struct hrtimer *)0xffffffc0b4e83c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52148067472002                    52148067522002                  
         v.v (struct hrtimer *)0xffffffc0b5903c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52152023883195                    52152023933195                  
         v.v (struct hrtimer *)0xffffffc0b6f1bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159000004843                    52159000054843                  
         v.v (struct hrtimer *)0xffffffc0b7c6bc80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52159496489915                    52159496539915                  
         v.v (struct hrtimer *)0xffffffc09adf3c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52164952366731                    52164952416731                  
         v.v (struct hrtimer *)0xffffffc0b9743c80  0xffffffc0801f1adc ('hrtimer_wakeup', 0)                                              52166107545069                    52166107595069  

以下是各列和内容的解析

列名

含义

CPU

当前显示信息所涉及的 CPU。这里显示的是 CPU 0,表示这是关于 CPU 0 的高精度定时器(hrtimer)信息。

hrtimer_bases

hrtimer 基座结构体的地址。每个 CPU 都有一个 hrtimer_cpu_base 结构体,它包含该 CPU 的定时器队列和相关状态信息。

hrtimer_cpu_base

hrtimer_cpu_base 指向当前 CPU 的高精度定时器基座(基座是一个用于管理多个定时器的结构体)。

TIMER_LIST_ADDR

定时器对象 struct hrtimer 的内存地址。每个定时器都是一个 struct hrtimer 对象,包含定时器的各种信息。

FUNCTION

定时器到期时执行的回调函数。比如:tick_sched_timerwatchdog_timer_fn 等。

_softexpires

定时器的下次到期时间,表示定时器的触发时间(单位是 jiffies)。该时间表示从当前时刻起定时器多久后触发。

_softexpires (second column)

显示定时器的目标触发时间,通常与 _softexpires 一样,但用于对比,确保定时器触发的正确性和更新。

高通平台的watchdog debug

关于在debug watchdog触发的死机问题时,我们有通用的一般的方法,步骤见:

我这里介绍一下关于watchdog触发的判断依据以及小技巧。

  1. 查看dmesg_TZ.txt中解析出的watdog部分,检查CPU、ping_start/ping_end,与当前的系统时间做对比,判断ping cpu的动作的执行结果以及可能出现问题的cpu

  2. 查看timer_list.txt中的timer list dump,查qcom_wdt_pet_task_wakeup 在哪个CPU上?触发回调的时间点与当前时间点做比较,如果早于当前的系统时间说明定时器没有被触发?

  3. 查看各个CPU上的timer_jiffiesnext_timer来判断哪些CPU的时间戳没有被更新,则这些CPU是可怀疑对象!

  4. 如果watchdog定时器到了时间没有触发,则说明该CPU被阻塞了,去查当前CPU上的栈trace。

  5. 还可以检测hrtimer,看时间戳来判断是不是由于系统调度或者soft lockup等问题造成的。

再有必要的时候,也可以参考第3.3章节中的内核参数来抓取日志debug。

总结

  1. pet_timer是个低精度计时器,低精度定时器会在每个tick中断来时被检查,超时就触发软中断,而linux的软中断又会再中断返回时检查执行。所以只要有CPU还能响应中断pet_task_wakeup就能被执行,但是pet_task_wakeup也只是唤醒了喂狗线程而已,并没有直接去允许它,具体什么时候允许要看下次抢占点到了pet-thread能不能成功抢占cpu执行。

  2. watchdog_kthread作为一个实时进程,而且优先级最高加上唤醒抢占的机制,一旦喂狗线程被加到某个CPU的就绪列队中,一般很快就会被执行并且基本没有进程可以抢占它.

  3. 所以没法喂狗会发生在以下情况:

喂狗线程所在cpu长时间关抢占,导致即使pet_timer唤醒喂狗线程但得不到执行。

喂狗线程所在的cpu频繁被中断打断,导致没法按时喂狗。

其他cpu长时间处于关中断导致无法响应ipi中断。

所有cpu长时间处于关中断导致直接触发狗咬(pet_timer无法执行,狗叫中断无法响应)。