标签搜索

目 录CONTENT

文章目录

Linux内核检测到hung task之后panic重启问题的排查

沙漠渔
2023-06-21 10:33:54 / 0 评论 / 0 点赞 / 213 阅读 / 4,036 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2023-06-21,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

自动化任务执行过程中出现了几次晚上无故自动重启的情况,今天正好看到相关的日志输出,才发现是触发了内核panic导致重启,目前已进行相关配置,正在重新验证。

问题过程

自动化任务执行中有一个过程是需要将系统设置为只读,然后利用dd命令将磁盘分区备份出来,其中设置文件系统只读使用的是:

echo u > /proc/sysrq-trigger

具体这个过程,后面再详细说明,执行该命令可以将文件系统重新挂载为只读,避免dd命令过程中系统操作磁盘内容。
经过一段时间运行,会偶尔出现dd命令执行过程中无故重启,今天排查重启前后日志时,正好发现一个问题,就是内核日志提示:

[  960.650132] INFO: task kworker/u8:2:1022 blocked for more than 120 seconds.
[  960.657894] "echo 0 > /proc/sys/kernel/hung_task_timeout_secs" disables this message.
[  960.668277] BUG: using smp_processor_id() in preemptible [00000000] code: khungtaskd/662
[  960.698381] Kernel panic - not syncing: hung_task: blocked tasks

于是查找kernel panic相关话题,发现了问题,是执行流程中的只读配置触发了内核重启。

hung task 检测机制

我们知道进程等待IO时,经常处于D状态,即TASK_UNINTERRUPTIBLE状态,处于这种状态的进程不处理信号,所以kill不掉,如果进程长期处于D状态,那么肯定不正常,原因可能有如下两种:

  1. IO路径上的硬件出问题了,比如磁盘损坏;
  2. 内核自己出问题了。

这种问题一般不好定位,而且一旦出现就通常不可恢复,kill不掉,只能重启恢复了。

内核针对这种问题,开发了一种hung task的检测机制,基本原理就是定时检测系统中处于D状态的晋城,如果其处于D状态的时间超过了制定时间(一般默认120s,可以配置),则打印相关堆栈信息,也可以通过proc参数配置使其之间直接panic。

内核panic是什么

panic函数主要是内核出现异常的时候输出异常信息、异常栈信息、挂起系统,代码如下:

/**
*     panic - halt the system
*     @fmt: The text string to print
*
*     Display a message, then perform cleanups.
*
*     This function never returns.
*/
void panic(const char *fmt, ...)
{
     static DEFINE_SPINLOCK(panic_lock);
     static char buf[1024];
     va_list args;
     long i, i_next = 0;
     int state = 0;

     /*
     * It's possible to come here directly from a panic-assertion and
     * not have preempt disabled. Some functions called from here want
     * preempt to be disabled. No point enabling it later though...
     *
     * Only one CPU is allowed to execute the panic code from here. For
     * multiple parallel invocations of panic, all other CPUs either
     * stop themself or will wait until they are stopped by the 1st CPU
     * with smp_send_stop().
     */
     if (!spin_trylock(&panic_lock))
          panic_smp_self_stop();  /*同一时间只有一个核能执行panic代码,获取到锁的cpu先把自己stop掉,其他核要么自己stop自己  要么等待拿到锁的核执行smp_send_stop*/

     console_verbose();/* 提高打印等级 */

     bust_spinlocks(1); /*bust_spinlocks()机制,用来避免系统crash以至于无法正常工作的时候还要进行不必要的等待spinlock操作*/

     va_start(args, fmt);
    /* 利用va_start指针遍历函数栈方式处理多参数
     * 函数栈: (高地址)|-最后一个参数--... ...--第一个参数-|---返回地址---|---函数执行代码-      --|(低地址)
     */
     vsnprintf(buf, sizeof(buf), fmt, args);
     va_end(args);

     printk(KERN_EMERG "Kernel panic - not syncing: %s\n",buf);
#ifdef CONFIG_DEBUG_BUGVERBOSE        /* 是否输出BUG行号和文件名 */
     /*
     * Avoid nested stack-dumping if a panic occurs during oops processing
     */
     if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
          dump_stack();   /*打印堆栈*/
#endif

     /*
     * If we have crashed and we have a crash kernel loaded let it handle
     * everything else.
     * Do we want to call this before we try to display a message?
     */
     crash_kexec(NULL);/* 内核crash的操作 */

     kmsg_dump(KMSG_DUMP_PANIC);/*kmsg日志保存到mtd设备去,需要提前分配好mtd存储*/

     /*
     * Note smp_send_stop is the usual smp shutdown function, which
     * unfortunately means it may not be hardened to work in a panic
     * situation.
     */
     smp_send_stop(); /* 停止其他多核的工作 */

     atomic_notifier_call_chain(&panic_notifier_list, 0, buf);/*注册了异常handle的函数都调用一次*/

     bust_spinlocks(0);

     if (!panic_blink)
          panic_blink = no_blink;

     if (panic_timeout > 0) {
          /*
          * Delay timeout seconds before rebooting the machine.
          * We can't use the "normal" timers since we just panicked.
          */
          printk(KERN_EMERG "Rebooting in %d seconds..", panic_timeout);

          for (i = 0; i < panic_timeout * 1000; i += PANIC_TIMER_STEP) {
               touch_nmi_watchdog();
               if (i >= i_next) {
                    i += panic_blink(state ^= 1);
                    i_next = i + 3600 / PANIC_BLINK_SPD;
               }
               mdelay(PANIC_TIMER_STEP);
          }
     }
     if (panic_timeout != 0) {
          /*
          * This will not be a clean reboot, with everything
          * shutting down.  But if there is a chance of
          * rebooting the system it will be rebooted.
          */
          emergency_restart();
     }

    /**
     *  panic_timeout = 0 进行死循环,抢占被禁止,CPU一直运行panic程序中运行。
     */
     local_irq_enable();
     for (i = 0; ; i += PANIC_TIMER_STEP) {
          touch_softlockup_watchdog();
          if (i >= i_next) {
               i += panic_blink(state ^= 1);
               i_next = i + 3600 / PANIC_BLINK_SPD;
          }
          mdelay(PANIC_TIMER_STEP);
     }
}

EXPORT_SYMBOL(panic);

hung task相关配置

  1. 设置timeout时间:
    echo xx > /proc/sys/kernel/hung_task_timeout_secs
    xx单位为s。
  2. 设置hung task后是否触发panic
    echo 1 > /proc/sys/kernel/hung_task_panic

当前解决措施

因为自动化流程中是故意将文件系统设置为只读,正常情况下大概率会触发hung task 导致panic,所以直接设置为不触发panic,避免系统挂起,即:

echo 0 > /proc/sys/kernel/hung_task_panic
0
广告 广告

评论区