Linux kernel panic

当Linux内核发生严重错误的时候,系统无法继续运行下去,此时内核会主动触发一个panic操作,它的执行流程分析过程如下所示:

kernel/panic.c:

void panic(const char *fmt, ...)
{
    pr_emerg("Kernel panic - not syncing: %s\n", buf);
    ...
        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();
    }
    ...
    pr_emerg("---[ end Kernel panic - not syncing: %s ]---\n", buf);
    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);
    }
}

系统发生了panic后是会跑到panic函数中来的,在我使用的高通平台上 panic_timeout=-1 ,然后调用进入emergency_restart,进行restart操作。

kernel/reboot.c:

void emergency_restart(void)
{
    kmsg_dump(KMSG_DUMP_EMERG);
    machine_emergency_restart();
}
EXPORT_SYMBOL_GPL(emergency_restart);
include/asm-generic/emergency-restart.h:

static inline void machine_emergency_restart(void)
{
    machine_restart(NULL);
}

arch/arm64/kernel/process.c:
void machine_restart(char *cmd)
{
    /* Disable interrupts first */
    local_irq_disable();
    smp_send_stop();

    /*
    * UpdateCapsule() depends on the system being reset via
    * ResetSystem().
    */
    if (efi_enabled(EFI_RUNTIME_SERVICES))
        efi_reboot(reboot_mode, NULL);

    /* Now call the architecture specific reboot code. */
    if (arm_pm_restart)
        arm_pm_restart(reboot_mode, cmd);
    else
        do_kernel_restart(cmd);

    /*
    * Whoops - the architecture was unable to reboot.
    */
    printk("Reboot failed -- System halted\n");
    while (1);
}

从函数调用栈来看最终它会执行到machine_restart,也就是重启操作。那么panic重启和正常重启一样都会调用到machine_restart函数,区别在于高通平台上的panic重启是会触发设备进入warm reset进而抓取crash dump数据用于稳定性分析,而普通重启操作并不会触发warm reset。实际上不同的restart方式是由平台相关的代码来实现的,正常情况下进入之后就不应该再返回,但是如果restart失败返回的话,那么就会导致system halt。平台相关的操作如下所示:

    /* Now call the architecture specific reboot code. */
    if (arm_pm_restart)
        arm_pm_restart(reboot_mode, cmd);

在高通平台相关代码中会对arm_pm_restart赋值:

drivers/power/reset/msm-poweroff.c:

static int msm_restart_probe(struct platform_device *pdev)
{
    ...
    pm_power_off = do_msm_poweroff;
    arm_pm_restart = do_msm_restart;
    ...
}

平台相关实现主要在do_msm_restart中:

static void do_msm_restart(enum reboot_mode reboot_mode, const char *cmd)
{
    pr_notice("Going down for restart now\n");

    msm_restart_prepare(cmd);

    /*
     * Trigger a watchdog bite here and if this fails,
     * device will take the usual restart path.
     */
    if (WDOG_BITE_ON_PANIC && in_panic)
        msm_trigger_wdog_bite();

    scm_disable_sdi();
    halt_spmi_pmic_arbiter();
    deassert_ps_hold();

    msleep(10000);
}

从前面的分析可以看出,这个函数实现并不仅仅用于panic,普通reset也会调用到。那么此函数是如何处理不同情况的呢?

  • 1.重启类型的配置,对于高通平台上的panic就是配置PS_HOLD的 reset type为warm reset。
    这一步骤是在msm_restart_prepare函数中实现的,最终会设置到PMIC寄存器中,以保证Ram不掉电,也就是热重启。

  • 2.这里针对panic做了区分的还有watchdog处理。

    /*
     * Trigger a watchdog bite here and if this fails,
     * device will take the usual restart path.
     */
    if (WDOG_BITE_ON_PANIC && in_panic)
        msm_trigger_wdog_bite();

为什么要watchdog处理,watchdog能触发secure world的重启操作,高通的soc中包含了很多子系统,并不仅仅只有ap,还有modem、sensor hub、cdsp、adsp等等,只有通过secure world中的tz来执行reset,才能够保证最后重启之前由tz向各个子系统发送消息保存panic现场数据。否则我们只能保证是non secure world的reset操作。

  • 3.拉高PS HOLD引脚,这个引脚是从AP输出给到PMIC的重启信号

PMIC接收该信号后,会按照配置的重启类型执行掉电,重上电操作,从而可以区分hard reset还是warm reset。

发布了234 篇原创文章 · 获赞 78 · 访问量 23万+

猜你喜欢

转载自blog.csdn.net/rikeyone/article/details/103386665