softlockup检测(watchdog)原理

softlockup(watchdog)用于检测系统调度是否正常,即软锁的情况,当发生softlockup时,内核不能调度,但还能响应中断,对用户的表现可能为:能ping通,但无法登陆系统,无法进行正常操作。
其基本原理为: 为每个CPU启动一个内核线程(watchdog/x) 此线程为优先级最高的实时线程,在该线程得到调度时,会更新相应的计数(时间戳),同时会启动定时器,当定时器到期时检查相应的时间戳,如果超过指定时间,都没有更新,则说明这段时间内都没有发生调度(因为此线程优先级最高) ,则打印相应告警或根据配置可以进入panic流程。
基本代码分析(2.6.32)
rest_init->kernel_init->lockup_detector_init->cpu_callback->watchdog_prepare_cpu (初始化watchdog定时器):
  1. static int watchdog_prepare_cpu(int cpu)
  2. {
  3.     struct hrtimer *hrtimer = &per_cpu(watchdog_hrtimer, cpu);

  4.     WARN_ON(per_cpu(softlockup_watchdog, cpu));
  5.     hrtimer_init(hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);//初始化高精度定时器
  6.     hrtimer->function = watchdog_timer_fn; //设置定时器处理函数

  7.     return 0;
  8. }
看门狗定时器处理函数:

/* watchdog kicker functions */
.hrtimer定时器调用watchdog_timer_fn进行清狗的时间检查,而线程则每次重置清狗时间,如果watchdog_timer_fn发现狗的重置时间已经和当前时间差出危险值,则根据开关进行panic处理。

  1. static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
  2. {
  3. //获取计数watchdog_touch_ts,该计数在watchdog内核线程被调度时更新
  4.      unsigned longtouch_ts = __get_cpu_var(watchdog_touch_ts);
  5.      struct pt_regs *regs = get_irq_regs();
  6.      int duration;

  7.      /* kick the hardlockup detector */
  8. //增加中断计数,证明没有发生硬锁(关中断死锁)
  9.     watchdog_interrupt_count();

  10.     /* kick the softlockup detector */
  11. //唤醒wathdog内核线程
  12.     wake_up_process(__get_cpu_var(softlockup_watchdog));//就是watchdog线程

  13.     /* .. and repeat */
  14. //重启定时器
  15.     hrtimer_forward_now(hrtimer, ns_to_ktime(get_sample_period()));
  16.     if (touch_ts == 0) {
  17.         if (unlikely(__get_cpu_var(softlockup_touch_sync))) {
  18.             /*
  19.              * If the time stamp was touched atomically
  20.              * make sure the scheduler tick is up to date.
  21.              */
  22.             __get_cpu_var(softlockup_touch_sync) = false;
  23.             sched_clock_tick();
  24.         }
  25.         __touch_watchdog();
  26.         return HRTIMER_RESTART;
  27.     }

  28.     /* check for a softlockup
  29.      * This is done by making sure a high priority task is
  30.      * being scheduled. The task touches the watchdog to
  31.      * indicate it is getting cpu time. If it hasn'then
  32.      * this is a good indication some task is hogging the cpu
  33.      */
  34. //判断是否发生了软锁,原理是判断touch_ts(时间戳)是否超过一定时间没有更新
  35.     duration = is_softlockup(touch_ts);
  36.     if (unlikely(duration)) {
  37.         /* only warn once */
  38. /*soft_watchdog_warn标识会在已经出现了一次看门狗超时的情况下置位,
  39. 此处的用意是对于同一个死锁进程,内核只做一次报警动作,如果死锁的进程发生了改变,
  40. 那该标识会重新设置为false,将可以重新触发报警。
  41. */
  42.         if (__get_cpu_var(soft_watchdog_warn) == true)
  43.             return HRTIMER_RESTART;

  44. //发生了软锁后,进行一些列的信息记录和告警。
  45.         printk(KERN_EMERG "BUG: soft lockup - CPU#%d stuck for %us! [%s:%d]\n",
  46.              smp_processor_id(), duration,current->comm, task_pid_nr(current));

  47.         print_modules();
  48.         print_irqtrace_events(current);
  49.         if (regs)
  50.             show_regs(regs);
  51.         else
  52.             dump_stack();
  53. //如果配置了softlockup_panic(proc中配置),则panic
  54.         if (softlockup_panic)
  55.             panic("softlockup: hung tasks");

  56.         __get_cpu_var(soft_watchdog_warn) = true; //出现了一次超时,softlock。
  57.     } else
  58.         __get_cpu_var(soft_watchdog_warn) = false;

  59.     return HRTIMER_RESTART;
  60. }

启动看门狗,即创建watchdog内核线程。
  1. static int watchdog_enable(int cpu)
  2. {
  3.     struct task_struct *= per_cpu(softlockup_watchdog, cpu);
  4.     int err = 0;

  5.     /* enable the perf event */
  6.     err = watchdog_nmi_enable(cpu);

  7.     /* Regardless of err above, fall through and start softlockup */
  8.     /* create the watchdog thread */
  9.     if (!p) {
  10. //创建watchdog内核线程
  11.         = kthread_create(watchdog, (void *)(unsigned long)cpu, "watchdog/%d", cpu);
  12.         if (IS_ERR(p)) {
  13.             printk(KERN_ERR "softlockup watchdog for %i failed\n", cpu);
  14.             if (!err)
  15.                 /* if hardlockup hasn't already set this */
  16.                 err = PTR_ERR(p);
  17.             goto out;
  18.         }
  19.         kthread_bind(p, cpu);
  20.         per_cpu(watchdog_touch_ts, cpu) = 0;
  21.         per_cpu(softlockup_watchdog, cpu) = p;
  22.         wake_up_process(p);
  23.     }

  24. out:
  25.     return err;
  26. }

watchdog内核线程执行主函数,主要是要更新计数(时间戳)
  1. static int watchdog(void *unused)
  2. {
  3. //设置为最高优先级
  4.     struct sched_param param = { .sched_priority = MAX_RT_PRIO-};
  5.     struct hrtimer *hrtimer = &__raw_get_cpu_var(watchdog_hrtimer);
  6. //设置为实时线程
  7.     sched_setscheduler(current, SCHED_FIFO, &param);

  8.     /* initialize timestamp */
  9. //初始化计数(时间戳)
  10.     __touch_watchdog();

  11.     /* kick off the timer for the hardlockup detector */
  12.     /* done here because hrtimer_start can only pin to smp_processor_id() */
  13. //启动定时器,用于检测是否发生软锁
  14.     hrtimer_start(hrtimer, ns_to_ktime(get_sample_period()),HRTIMER_MODE_REL_PINNED);

  15. //睡眠
  16.     set_current_state(TASK_INTERRUPTIBLE);

  17.     /*
  18.      * Run briefly once per second to reset the softlockup timestamp.
  19.      * If this gets delayed for more than 60 seconds then the
  20.      * debug-printout triggers in watchdog_timer_fn().
  21.      */
  22.     while (!kthread_should_stop()) 
  23. {
  24. //更新计数watchdog_touch_ts
  25.         __touch_watchdog();

  26.         schedule();

  27.         if (kthread_should_stop())
  28.             break;

  29.         set_current_state(TASK_INTERRUPTIBLE);
  30.     }
  31.     __set_current_state(TASK_RUNNING);

  32.     return 0;
  33. }

/* Commands for resetting the watchdog */
static void __touch_watchdog(void)
{
__this_cpu_write(watchdog_touch_ts, get_timestamp());
}

判断是否发生软锁: is_softlockup

点击( 此处 )折叠或打开
  1. static int is_softlockup(unsigned long touch_ts)
  2. {
  3.     unsigned long now = get_timestamp();

  4.     /* Warn about unreasonable delays: */
  5.     /*检测多久没有更新相关计数了,默认阈值为10s,touch_us为上一次更新的时间*/
  6.     if (time_after(now, touch_ts + get_softlockup_thresh()))
  7.          return now - touch_ts;

  8.     return 0;
  9. }
==============================================================================
root@lrc-0:~# ps -ewLo user,pid,ppid,tid,flags, pri,rtprio,cls,psr,pmem,time,args | grep watchdog ?? why is TS
root63 2 63 5 19 - TS 0 0.0 00:00:00 [watchdog/0]
root64 2 64 5 19 - TS 1 0.0 00:00:00 [watchdog/1]
root71 2 71 5 19 - TS 2 0.0 00:00:00 [watchdog/2]
root78 2 78 5 19 - TS 3 0.0 00:00:00 [watchdog/3]
root85 2 85 5 19 - TS 4 0.0 00:00:00 [watchdog/4]
root92 2 92 5 19 - TS 5 0.0 00:00:00 [watchdog/5]
root99 2 99 5 19 - TS 6 0.0 00:00:00 [watchdog/6]
root106 2 106 5 19 - TS 7 0.0 00:00:00 [watchdog/7]
root113 2 113 5 19 - TS 8 0.0 00:00:00 [watchdog/8]
root120 2 120 5 19 - TS 9 0.0 00:00:00 [watchdog/9]
root127 2 127 5 19 - TS 10 0.0 00:00:00 [watchdog/10]
root134 2 134 5 19 - TS 11 0.0 00:00:00 [watchdog/11]
root141 2 141 5 19 - TS 12 0.0 00:00:00 [watchdog/12]
root148 2 148 5 19 - TS 13 0.0 00:00:00 [watchdog/13]
root155 2 155 5 19 - TS 14 0.0 00:00:00 [watchdog/14]
root162 2 162 5 19 - TS 15 0.0 00:00:00 [watchdog/15]
root10670 1189 10670 0

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值