系統的PANIC

panic並沒有什麼大不了的,就是禁搶佔,善後,
禁止了搶佔實際上就禁止了調度,因爲禁止搶佔的情況下除非自己放棄cpu才能調度,
但是我們看看那個死循環根本沒有放棄cpu的意思。
發生panic無非就兩種情形,一個是在中斷上下文,另一個是在非中斷上下文。
在中斷上下文中 panic的話,此時的in_interrupt()一定返回真,如果此硬件中斷觸發了一個軟中斷,那麼這個軟中斷是永遠不會被執行的,
因爲導致 panic的中斷永遠不會調用irq_exit來使得in_interrupt()返回假。
這就解釋了爲何在中斷中panic的系統ping不通,而在非中斷上下文中panic雖然內核死掉了但是還是可以從外部ping通的。
想想ping的執行,ping依靠的是icmp協議,沒有進程上下文,它是ip層的內容,而ip層是不區分進程的,
於是icmp的處理只在軟中斷上下文進行,既然硬件中斷中panic了,根據上面的論述,軟中斷就沒有機會執行了,
於是就ping不通了;而非中斷的panic由於軟中斷可以執行,所以可以ping通,但是內核還是不能恢復了,
因爲已經關閉可搶佔,無法調度了,於是執行流出去中斷後便會一直執行那個死循環,沒有機會執行別的。


目前有三種記錄的方式: kdump; mtdoops; crashlog(這是openwrt特別的功能,正式linux內核中沒有)
大家對kdump比較瞭解。它主要使用於x86系統。因爲它使用佔用大量內存和硬盤。
kdump是一種先進的基於kexec的內核崩潰轉儲機制。當系統崩潰時,kdump使用kexec 啓動到第二個內核。第二個內核通常叫做捕獲內核,以很小內存啓動以捕獲轉儲鏡像。
mtdoops和crashlog主要用於嵌入式的環境。也只是記錄文本日誌。

SSD在硬件內部的微代碼中實現了wear leveling等分佈寫操作的技術,因此係統無須再使用特殊的MTD驅動和FTL層

系統重啓原因:

    軟件故障:在設置了panic參數時,panic會觸發重啓,不掉電重啓,這時候panic會打印棧調用信息,
        不論是否掉電,串口和顯示器都會有信息;掉電那麼執行命令3就會是空的
    
    硬件故障:這種很難判斷原因,需要硬件廠家的協助復現。
    
    硬件狗:餵狗程序自己有問題,那也會觸發重啓,只能關掉硬件狗再看(命令2)。
    

===背景知識==

panic:
    引起原因通常兩種:
        非法內存訪問 (比如訪問地址0)
        非法指令
        
    內核進入恐慌,執行以下動作:
        關閉搶佔-->棧調用信息-->停止別的cpu-->panic通知鏈-->kdump-->倒計時重啓-->死循環
        
        棧調用信息我們是放到內存sg_res_mem,系統啓動會保存到對應文件
            /media/debug/log/sgkdump.log,不過掉電重啓消失
        /proc/sys/kernel/panic 表示重啓倒計時的時間,爲0則不重啓,但此時CPU無法調度了
        kdump使用kexec啓動到第二個內核,方便查看panic信息,佔用空間較大,沒有設置
        
    發生位置
        非中斷上下文,從外部可以ping通
        中斷上下文

panic_on_oops:
    內核驚歎不一定進入panic,可以通過參數控制/proc/sys/vm/panic_on_oom
    中斷上下文,oops必須拋panic
    進程上下文,可以只打印oops

lockup
    由於某種原因導致系統處於內核態超過20s導致進程無法運行(soft lockup)
    由於某種原因導致系統處於內核態超過10s導致中斷無法運行(hard lockup)
    
soft lockup
    系統會爲每個cpu core註冊一個一般的kernel線程,名字叫watchdog/0,watchdog/1…以此類推。
    這個線程會定期得調用watchdog函數,某個cpu上超過20s沒有執行,更新watchdog_touch_ts變量

hard lockup
    基於PMU的NMI perf event,當PMU的計數器溢出時會觸發NMI中斷,因爲NMI中斷是不可屏蔽的
    在中斷中去檢查hrtimer_interrupts變量
    
softlockup_panic
    參數讓機器發生softlock後進入panic

2.6內核中:
    softlockup_thresh = kernel.watchdog_thresh,默認60秒;

3.10內核中:
    kernel.watchdog_thresh = hard lockup threshold,默認10秒;
    soft lockup threshold = 2*kernel.watchdog_thresh),即默認20秒。


kernel/panic.c

#################### insmod hello.ko #################

#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
        printk(KERN_ALERT "Hello, world !/n");
        return 0;
}
static void hello_exit(void)
{
        printk(KERN_ALERT "Goodbye, cruel world !/n");
}
module_init(hello_init);
module_exit(hello_exit);
#######################  makefile  ###################
obj-m := hello.o
KERNELDR := /usr/src/linux
PWD := $(shell pwd)
modules:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules
moduels_install:
    $(MAKE) -C $(KERNELDR) M=$(PWD) modules_install
clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
#########################  notify.c  #############################
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/notifier.h>
#include <linux/module.h>
#include <linux/syscalls.h>

#include <linux/fs.h>
#include <linux/fcntl.h>
#include <asm/uaccess.h>

#include <linux/file.h>
MODULE_LICENSE("Dual BSD/GPL");

char log_file[100] = "/media/boot/panic.log";
char init_msg[100] = "NOTIFY START\n";
char panic_msg[100] = "KERNEL PANIC\n";
int i;
int count;
struct file *phMscd_Filp = NULL;
mm_segment_t old_fs;

// ****************************************************************************
// Here is where i receive the panic notification (from panic.c)
// ****************************************************************************

static int record_msg(char *msg, int len)
{
    int ret = 0;
    printk("############## start record panic ###########\n");
    printk("msg: %s, len : %d\n", msg ,len);

    // request file
    phMscd_Filp = filp_open(log_file, O_RDWR | O_LARGEFILE | O_CREAT | O_APPEND , 0);
    if (phMscd_Filp == NULL) {
        printk("filp_open error!!.\n");
        return 0;
    }

    // save actual space
    printk("############## save actual space ###########\n");
    old_fs=get_fs();

    // jump to other space
    printk("############## jump to other space ###########\n");
    set_fs(get_ds());

    // write the file
    printk("############## write the file ###########\n");
    ret = vfs_write(phMscd_Filp, msg, len, &phMscd_Filp->f_pos);
    if (ret)
        printk("############## ret: %d ###########\n", ret);

    // close the file
    printk("############## close the file ###########\n");
    filp_close(phMscd_Filp,NULL);

    // back to old space
    printk("############## back to old space ###########\n");
    set_fs(old_fs);
    return 0;
}
static int panic_happened(struct notifier_block *n, unsigned long val, void *message)
{
    return record_msg(panic_msg, sizeof(panic_msg));
}

// ****************************************************************************
// block
// ****************************************************************************
static struct notifier_block panic_notifier = { panic_happened, NULL, 1 };

// ****************************************************************************
// load
// ****************************************************************************
static int __init register_my_panic(void)
{
    atomic_notifier_chain_register(&panic_notifier_list, &panic_notifier);
    printk("register panic notify\n");
    record_msg(init_msg, sizeof(init_msg));
    return 0;
}

// ****************************************************************************
// unload
// ****************************************************************************
static void __exit unregister_my_panic(void)
{
    atomic_notifier_chain_unregister(&panic_notifier_list, &panic_notifier);
    printk("unregister panic notify");
}

module_init(register_my_panic);
module_exit(unregister_my_panic);

--------------------------------------------------------------------------------
A simple way to dump kmsg into mmc storage
--------------------------------------------------------------------------------
kernel space
--------------------------------------------------------------------------------
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/console.h>
#include <linux/vmalloc.h>
#include <linux/seq_file.h>
#include <linux/workqueue.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/kmsg_dump.h>
#include <linux/proc_fs.h>
 
static struct kmsg_dumper dump;
static struct proc_dir_entry *my_proc;
static int is_panic = 0;
 
static int my_proc_show(struct seq_file *m, void *v)
{
    seq_printf(m, "%d", is_panic);
    return 0;
}
static int my_proc_open(struct inode *inode, struct file *file)
{
    return single_open(file, my_proc_show, NULL);
}
static const struct file_operations my_proc_ops = {
    .open        = my_proc_open,
    .read        = seq_read,
    .llseek        = seq_lseek,
    .release    = single_release,
};
static void oops_do_dump(struct kmsg_dumper *dumper,
        enum kmsg_dump_reason reason, const char *s1, unsigned long l1,
        const char *s2, unsigned long l2)
{
    int i;
    printk("### [%s:%d] reason = %d\n", __func__, __LINE__, reason);
    is_panic = 1;
 
    for (i = 0;i < 10; i++)
        msleep(1000);
    printk("### [%s:%d] should be done\n", __func__, __LINE__);
}
static int __init my_oops_init(void)
{
    int err;
 
    dump.dump = oops_do_dump;
    err = kmsg_dump_register(&dump);
    if (err) {
        printk(KERN_ERR "oops: registering kmsg dumper failed, error %d\n", err);
        return -EINVAL;
    }
    my_proc = proc_create("dump_tester", 0, NULL, &my_proc_ops);
 
    return 0;
}
static void __exit my_oops_exit(void)
{
    printk("### [%s:%d]\n", __func__, __LINE__);
    if (my_proc)
        remove_proc_entry( "dump_tester", NULL);
    kmsg_dump_unregister(&dump);
 
}
module_init(my_oops_init);
module_exit(my_oops_exit);
MODULE_LICENSE("GPL");
--------------------------------------------------------------------------------
User space
--------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>
 
#define BUF_LEN 40960
 
 
void main(int argc, char **argv)
{
    char tmp = 'X';
    char buf[BUF_LEN];
    int fd_src, fd_trg;
    int fd = open("/proc/dump_tester", O_RDONLY, 0);
    while(1) {
        lseek(fd, 0, SEEK_SET);
        read(fd, &tmp, 1);
        //printf("### [%s:%d] ==> '%c'\n", __FUNCTION__, __LINE__, tmp);
 
        if (tmp == '1') {
            fd_src = open("/proc/kmsg", O_RDONLY, 0);
            fd_trg = open("/dev/block/mmcblk0p6",  O_RDWR, 0);
            memset(buf, 0, BUF_LEN);
            write(fd_trg, buf, BUF_LEN);
            lseek(fd_trg, 0, SEEK_SET);
 
            read(fd_src, buf, BUF_LEN);
            write(fd_trg, buf, BUF_LEN);
            close(fd_src);
            close(fd_trg);
            sleep(1);
            printf("### dump panic log into %s\n", "/dev/block/mmcblk0p6");
            break;
        }
        sleep(1);
    }
    close(fd);
}

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