源码中的文档:

不论是软狗还是硬狗,大体思路都差不多:
准备一个定时器,定时10s,超时之后启动地球毁灭程序~~~。为了防止地球毁灭,需要时不时的将计时器的计数值清零,称为喂狗。一般看门狗定时器是无论系统跑飞还是卡死都能正常运行的,一般超时后直接拉CPU的reset引脚,让系统复位。

先看probe函数:

static int msm_watchdog_probe(struct platform_device *pdev)
{int ret;struct msm_watchdog_data *wdog_dd;struct md_region md_entry;if (!pdev->dev.of_node || !enable)return -ENODEV;wdog_dd = kzalloc(sizeof(struct msm_watchdog_data), GFP_KERNEL);if (!wdog_dd)return -EIO;ret = msm_wdog_dt_to_pdata(pdev, wdog_dd);if (ret)goto err;wdog_data = wdog_dd;wdog_dd->dev = &pdev->dev;platform_set_drvdata(pdev, wdog_dd);cpumask_clear(&wdog_dd->alive_mask);wdog_dd->watchdog_task = kthread_create(watchdog_kthread, wdog_dd,"msm_watchdog");if (IS_ERR(wdog_dd->watchdog_task)) {ret = PTR_ERR(wdog_dd->watchdog_task);goto err;}init_watchdog_data(wdog_dd);return 0;
err:kzfree(wdog_dd);return ret;
}

probe主要就是申请了msm_watchdog_data的内存,将pdev和pdata做了绑定,再通过msm_wdog_dt_to_pdata将设备树的信息获取过来。值得关注的是还创建了一个名为msm_watchdog的内核线程:

线程执行watchdog_kthread,只是创建,还没投入执行。而后继续调用init_watchdog_data继续完善核心数据结构。
从设备树获取watchdog配置:

static int msm_wdog_dt_to_pdata(struct platform_device *pdev,struct msm_watchdog_data *pdata)
{struct device_node *node = pdev->dev.of_node;struct resource *res;int ret, cpu, num_scandump_sizes;//获取看门狗的硬件内存信息res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "wdt-base");if (!res)return -ENODEV;pdata->size = resource_size(res);pdata->phys_base = res->start;if (unlikely(!(devm_request_mem_region(&pdev->dev, pdata->phys_base,pdata->size, "msm-watchdog")))) {dev_err(&pdev->dev, "%s cannot reserve watchdog region\n",__func__);return -ENXIO;}pdata->base  = devm_ioremap(&pdev->dev, pdata->phys_base,pdata->size);if (!pdata->base) {dev_err(&pdev->dev, "%s cannot map wdog register space\n",__func__);return -ENXIO;}//获取狗叫和狗咬中断资源pdata->bark_irq = platform_get_irq(pdev, 0);pdata->bite_irq = platform_get_irq(pdev, 1);//获取狗叫狗咬的超时信息ret = of_property_read_u32(node, "qcom,bark-time", &pdata->bark_time);ret = of_property_read_u32(node, "qcom,pet-time", &pdata->pet_time);//获取是否喂狗时ping其他cpupdata->do_ipi_ping = of_property_read_bool(node, "qcom,ipi-ping");//获取看门狗是否再系统休眠唤醒时自动停止和恢复pdata->wakeup_irq_enable = of_property_read_bool(node,"qcom,wakeup-enable");//获取狗叫中断是否是CPU私有中断,PPI中断pdata->irq_ppi = irq_is_percpu(pdata->bark_irq);dump_pdata(pdata);return 0;
}

进一步填充watchdog data:

static void init_watchdog_data(struct msm_watchdog_data *wdog_dd)
{unsigned long delay_time;uint32_t val;u64 timeout;int ret;//申请狗叫中断if (wdog_dd->irq_ppi) {wdog_dd->wdog_cpu_dd = alloc_percpu(struct msm_watchdog_data *);if (!wdog_dd->wdog_cpu_dd) {dev_err(wdog_dd->dev, "fail to allocate cpu data\n");return;}*raw_cpu_ptr(wdog_dd->wdog_cpu_dd) = wdog_dd;ret = request_percpu_irq(wdog_dd->bark_irq, wdog_ppi_bark,"apps_wdog_bark",wdog_dd->wdog_cpu_dd);if (ret) {dev_err(wdog_dd->dev, "failed to request bark irq\n");free_percpu(wdog_dd->wdog_cpu_dd);return;}} else {ret = devm_request_irq(wdog_dd->dev, wdog_dd->bark_irq,wdog_bark_handler, IRQF_TRIGGER_RISING,"apps_wdog_bark", wdog_dd);if (ret) {dev_err(wdog_dd->dev, "failed to request bark irq\n");return;}}//将喂狗延时转换成jiffiesdelay_time = msecs_to_jiffies(wdog_dd->pet_time);//初始化min_slack_ticks[ns]wdog_dd->min_slack_ticks = UINT_MAX;wdog_dd->min_slack_ns = ULLONG_MAX;//为了写到狗叫和狗咬寄存器中,必须将设备树中ms单位的时间转成写寄存器时的单位timeout = (wdog_dd->bark_time * WDT_HZ)/1000;//狗叫时间来自于bark_time__raw_writel(timeout, wdog_dd->base + WDT0_BARK_TIME);//狗咬时间来自于bark_time + 3s__raw_writel(timeout + 3*WDT_HZ, wdog_dd->base + WDT0_BITE_TIME);//panic时,系统已经挂了,所以需要借助watchdog之手重启。wdog_dd->panic_blk.notifier_call = panic_wdog_handler;atomic_notifier_chain_register(&panic_notifier_list,&wdog_dd->panic_blk);//初始化后面会用到的变量mutex_init(&wdog_dd->disable_lock);init_waitqueue_head(&wdog_dd->pet_complete);/**这段代码主要做了以下几件事情:*1.让前面提到的watchdog_task跑起来*2.让喂狗定时器pet_timer跑起来*3.使能复位看门狗定时器,让其工作起来*/wdog_dd->timer_expired = false;wake_up_process(wdog_dd->watchdog_task);init_timer(&wdog_dd->pet_timer);wdog_dd->pet_timer.data = (unsigned long)wdog_dd;wdog_dd->pet_timer.function = pet_task_wakeup;wdog_dd->pet_timer.expires = jiffies + delay_time;add_timer(&wdog_dd->pet_timer);val = BIT(EN);if (wdog_dd->wakeup_irq_enable)val |= BIT(UNMASKED_INT_EN);__raw_writel(val, wdog_dd->base + WDT0_EN);__raw_writel(1, wdog_dd->base + WDT0_RST);wdog_dd->last_pet = sched_clock();wdog_dd->enabled = true;init_watchdog_sysfs(wdog_dd);if (wdog_dd->irq_ppi)enable_percpu_irq(wdog_dd->bark_irq, 0);if (!ipi_en)cpu_pm_register_notifier(&wdog_cpu_pm_nb);dev_info(wdog_dd->dev, "MSM Watchdog Initialized\n");
}

走到这里,整个watchdog初始化已经完成了,看看门狗定时器也愉快地跑起来了。留下了一个

pet_timer,一个内核线程watchdog_task。

先看pet_timer,pet_time到了之后,会执行pet_task_wakeup:

static void pet_task_wakeup(unsigned long data)
{struct msm_watchdog_data *wdog_dd =(struct msm_watchdog_data *)data;wdog_dd->timer_expired = true;wdog_dd->timer_fired = sched_clock();wake_up(&wdog_dd->pet_complete);
}

主要就是把在等pet_complete的进程全部唤醒起来检查条件了,搜索了源码,就是前面的watchdog_task在列队等待:

wdog_dd->watchdog_task = kthread_create(watchdog_kthread, wdog_dd, "msm_watchdog");static __ref int watchdog_kthread(void *arg)
{struct msm_watchdog_data *wdog_dd =(struct msm_watchdog_data *)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 {//开始在pet_complete等待,pet_timer会在超时时设置timer_expired位trueret = wait_event_interruptible(wdog_dd->pet_complete,wdog_dd->timer_expired);} while (ret != 0);wdog_dd->thread_start = sched_clock();//cpu_present_mask - has bit 'cpu' set iff cpu is populated, 就是所有已经用起来的CPU,不论是不是在线for_each_cpu(cpu, cpu_present_mask)wdog_dd->ping_start[cpu] = wdog_dd->ping_end[cpu] = 0;//ping一下其他CPU,确保其他CPU都能正常响应核间中断if (wdog_dd->do_ipi_ping)ping_other_cpus(wdog_dd);wdog_dd->timer_expired = false;if (enable) {delay_time = msecs_to_jiffies(wdog_dd->pet_time);pet_watchdog(wdog_dd);}/* Check again before scheduling* Could have been changed on other cpu*/mod_timer(&wdog_dd->pet_timer, jiffies + delay_time);}return 0;
}static void ping_other_cpus(struct msm_watchdog_data *wdog_dd)
{int cpu;cpumask_clear(&wdog_dd->alive_mask);/* Make sure alive mask is cleared and set in order */smp_mb();//遍历所有在线的cpu,在ping_start[cpu]填ping的时间,然后依次等这些CPU执行keep_alive_responsefor_each_cpu(cpu, cpu_online_mask) {if (!cpu_idle_pc_state[cpu] && !cpu_isolated(cpu)) {//把ping这个cpu的时间填进对应index,然后让cpu执行keep_alive_responsewdog_dd->ping_start[cpu] = sched_clock();smp_call_function_single(cpu, keep_alive_response,wdog_dd, 1);}}
}static void keep_alive_response(void *info)
{int cpu = smp_processor_id();struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)info;//将自己对应的位置1cpumask_set_cpu(cpu, &wdog_dd->alive_mask);//cpu收到IPI中断后,将收到的时间天道对应的indexwdog_dd->ping_end[cpu] = sched_clock();/* Make sure alive mask is cleared and set in order */smp_mb();
}

当所有在线的CPU都执行了keep_alive_response,接着通过pet_watchdog重置一下wathdong定时器并重新启动pet-timer。

所以,高通平台通过启动一个线程定时"ping"其他cpu的方式,确保所有在线的cpu都是活的。

那如果pet_timer定时到了,而喂狗线程得不到运行或者来不及喂狗触发狗咬是什么情况呢?
这要看看当时注册的狗叫(狗咬没注册)中断对应的句柄:

static irqreturn_t wdog_bark_handler(int irq, void *dev_id)
{struct msm_watchdog_data *wdog_dd = (struct msm_watchdog_data *)dev_id;unsigned long nanosec_rem;//获取当前时间unsigned long long t = sched_clock();//一下这段操作就是打印出上次喂狗的时间和,当前的时间。nanosec_rem = do_div(t, 1000000000);dev_info(wdog_dd->dev, "Watchdog bark! Now = %lu.%06lu\n",(unsigned long) t, nanosec_rem / 1000);nanosec_rem = do_div(wdog_dd->last_pet, 1000000000);dev_info(wdog_dd->dev, "Watchdog last pet at %lu.%06lu\n",(unsigned long) wdog_dd->last_pet, nanosec_rem / 1000);//打印上次喂狗时活着的CPU有哪些if (wdog_dd->do_ipi_ping)dump_cpu_alive_mask(wdog_dd);//触发狗咬msm_trigger_wdog_bite();panic("Failed to cause a watchdog bite! - Falling back to kernel panic!");return IRQ_HANDLED;
}static void dump_cpu_alive_mask(struct msm_watchdog_data *wdog_dd)
{static char alive_mask_buf[MASK_SIZE];//上次喂狗时,每个cpu执行keep_alive_response之后会对对应位置1scnprintf(alive_mask_buf, MASK_SIZE, "%*pb1", cpumask_pr_args(&wdog_dd->alive_mask));dev_info(wdog_dd->dev, "cpu alive mask from last pet %s\n",alive_mask_buf);
}

这么看的话,超时没有喂狗,如果有CPU能正常响应狗叫中断的话,是会打印一些信息,然后触发狗咬的,可见高通平台watchdog是直接连叫带咬的。

通过查看源码,可以了解到:

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

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

https://www.cnblogs.com/shihuvini/p/9974771.html

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

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

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

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

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

值得一提的是,关中断其实就是关抢占,显式调用关中断之后关抢占只是为了增加抢占点罢了。

喂狗线程所在cpu的调用栈(在进程上下文ping其他cpu并喂狗):

被喂狗进程ping的cpu的调用栈(在中断上下文响应ping):

高通看门狗驱动-MSM Watchdog相关推荐

  1. 第一个驱动程序:at91sam9g45核心板的看门狗驱动

    纪念第一个驱动程序:at91sam9g45核心板的看门狗驱动 看门狗的驱动一般来说比较简单,只要做寄存器的设置实现开启.关闭.喂狗功能.本项目中我们使用的是at91sam9g45处理器,带有看门狗定时 ...

  2. linux设备驱动归纳总结(十一):简单的看门狗驱动

    http://blog.chinaunix.net/uid-25014876-id-112879.html 设备驱动的归纳已经差不多了,趁着知识点还没有遗忘,写点代码巩固一下,来个简单的看门狗驱动-- ...

  3. linux内核看门狗关闭方法,详解linux 看门狗驱动编写

    看门狗是linux驱动的一个重要环节.某些特殊的设备,有时候需要放在一些环境恶劣的地方,比如电信设备.但是,任何软件都不可能100%没有bug.如何保证软件在遇到严重bug.死机的时候也能正常运行呢, ...

  4. Amlogic A311D 驱动分析系列(一)-看门狗驱动分析

    本系列文章基于Amlogic A311D SDK中的驱动,将我之前阅读代码的一些收获进行总结,算是学习笔记吧. 1.dts配置 先看dts调用的是看门狗的哪个驱动 wdt: watchdog@0xff ...

  5. linux 多线程看门狗,X86平台的看门狗驱动,在内核中开线程喂狗

    1.X86平台的看门狗驱动: 相关文件为:drivers/watchdog/iTCO_wdt.c 相关配置选项为: Device Drivers  ---> [*] Watchdog Timer ...

  6. Linux看门狗驱动程序设计(三) 测试验证

    测试驱动之前需要执行make menuconfig,去掉内核自带看门狗驱动(位于Device Drivers -> Character devices -> Watchdog Timer ...

  7. linux服务器看门狗服务,服务器watchdog看门狗的理解

    1.什么是watchdog? watchdog,中文名称叫做"看门狗",全称watchdog timer,从字面上我们可以知道其实它属于一种定时器.然而它与我们平常所接触的定时器在 ...

  8. 高通平台LCD驱动参数说明

    下面是高通平台LCD驱动参数的说明,此说明是根据经验值以及高通的寄存器说明得出的,并非高通官方说明,如有疑问,欢迎探讨.针对一些没有用过的参数,由于没有实践证明,故不做妄加的评论. mipi_xxx_ ...

  9. 看门狗(APB watchdog)简介,各个寄存器简介,把看门狗挂到APB总线上,看门狗的验证测试(1)

    看门狗简介: 在系统运行时,可能会在外界的干扰下,出现程序跑飞的情况,看门狗的出现就是为了解决这种故障.看门狗是一个独立于系统的计数器即它的时钟和系统时钟不是同一个时钟,在看门狗时钟的驱动下,计数器会 ...

最新文章

  1. 自动驾驶车辆何时实现?近期不会实现的五大原因
  2. openfalcon 组件监控_运维监控系统之Open-Falcon
  3. 9月22日 奇怪的贸易
  4. [Google Guava] 1.5-Throwables:简化异常和错误的传播与检查
  5. 用cxf公布和调用web service
  6. SAP Spartacus split view里每个元素宽度的计算方式
  7. CSS3 Media Query:移动 Web 的完美开端
  8. 酒店wifi代理服务器没有响应,wn10连接酒店wifi的登录界面无法弹出如何处理
  9. 在PyCharm下使用Jupyter Notebook
  10. setTimeOut与setInterval的区别
  11. 贾跃亭债务小组:美法院支持贾跃亭个人破产重组继续进行
  12. 阶乘浅析poj1150 3406 zoj1222 2358
  13. Mysql主键和外键
  14. AI智能写作系统文章生成器,写原创文章更快更简单
  15. 橙子君正在维护服务器,橙子VR常见问题有哪些 橙子VR常见问题答案汇总
  16. Linux基础知识与实操-篇三: 文件压缩打包与vim基本使用
  17. GIS+=地理信息+行业+大数据——纽约公开11亿条出租车和Uber原始数据下载及分析
  18. 《疯狂动物城》给我的感悟
  19. Demo_塔防(自动生成怪物,导航,炮塔攻击,怪物掉血死忙)
  20. 资深办公室人员搞定数字转换Excel的精品秘诀

热门文章

  1. 总结:自学前端的高效学习路线
  2. 年薪高达50W的测开,到底是做什么的?
  3. 自学编程,28岁从字节退休,期权过亿法拉利,日本开温泉酒店
  4. “不雅小视频”,小心是你演的...
  5. 怎么才能知道自己适合做项目经理?怎么做合格的项目经理?
  6. android自动申请悬浮窗权限,Android 悬浮窗--无需权限
  7. 我的大学时代.篇章一
  8. 公司各种员工工资计算
  9. 什么品牌的蓝牙耳机好用?国产无线蓝牙耳机品牌排行
  10. linux top命令 什么意思,Linux TOP 命令介绍理解