當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。