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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章