一种memory问题导致的kernel panic的处理方法

摘要:
以下是一个补丁,可以在kernelpanic或oops之后打印更多内存信息。它主要使用前面描述的dienotify函数来注册oops/main回调函数。

下面是一个在kernel panic或者oops之后,能够打印更多内存信息的patch,主要用到前面介绍的die notify功能注册oops/painc回调函数。

#include <linux/mm.h>
#include <linux/spinlock.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/module.h>
#include <linux/bitops.h>
#include <linux/version.h>
#include <linux/vmalloc.h>
#include <linux/kasan.h>
#include <linux/notifier.h>
#include <linux/uaccess.h>
#include <linux/kdebug.h>
#include <linux/rmap.h>
#include <linux/delay.h>
#include <asm/sections.h>
#include <linux/memblock.h>
#include <linux/kallsyms.h>
#include <asm-generic/kdebug.h>
#include <../mm/slab.h>
#include <asm/memory.h>

#include <asm/esr.h>
#include <asm/sysreg.h>
#include <asm/system_misc.h>

 

#define DUMP_MEMORY_SIZE 0x100

static struct pt_regs saved_regs;

static void _dump_register(struct pt_regs *regs)
{
    int i = 0;

    pr_err("sp=%pS(%px)
",(void *)regs->sp, (void *)regs->sp);
    pr_err("pc=%pS(%px)
",(void *)regs->pc, (void *)regs->pc);
    for (i = 0; i < 31; i++){
        pr_err("X%d%px ", i, (void *)regs->regs[i]);
    }
}

static int check_addr_valid(unsigned long ptr)
{
    unsigned long flags;
    unsigned long par;

    local_irq_save(flags);
    asm volatile("at s1e1r, %0" :: "r" (ptr));
    isb();
    par = read_sysreg(par_el1);
    local_irq_restore(flags);

    pr_info("PAR = %lx
", par);
    if (par & SYS_PAR_EL1_F)
        return false;
    return true;
}

//TODO: enable CONFIG_SLUB_DEBUG CONFIG_STACKTRACE
extern void print_page_info(struct page *page);
extern void *get_freepointer(struct kmem_cache *s, void *object);
extern void print_tracking(struct kmem_cache *s, void *object);
static phys_addr_t _find_prev_slab_and_print_slub(phys_addr_t pa_addr)
{
    void * va = __va(pa_addr);
    struct kmem_cache *slab_kmem_cache = NULL;
    struct page *page = phys_to_page(pa_addr);
    struct page *compound_page = compound_head(page);
    void *slub_info = NULL;
    void *s;

    if (pa_addr == 0 || va == NULL || compound_page == NULL) {
        return 0;
    }
    //TODO: EXPORT_SYMBOL(print_page_info)
    print_page_info(compound_page);
    if (!PageSlab(compound_page))
        return 0;
    slab_kmem_cache = compound_page->slab_cache;
    if (!slab_kmem_cache)
        return 0;

    slub_info = nearest_obj(slab_kmem_cache, compound_page, va);
    pr_info("slub_info=%px, from slab %s(%x)
",
      slub_info,
      slab_kmem_cache->memcg_params.root_cache? 
      slab_kmem_cache->memcg_params.root_cache->name:slab_kmem_cache->name,
      slab_kmem_cache->size);
    if (bit_spin_trylock(PG_locked, &compound_page->flags)) {
        //TODO: EXPORT_SYMBOL(get_freepointer)
        for (s = compound_page->freelist; s != NULL; s = get_freepointer(slab_kmem_cache, s))
            if (s == slub_info) break;
        if (s == slub_info)
            pr_info("slub_info %px be free.
", slub_info);
        else
            pr_info("slub_info %px don't free.
", slub_info);
        bit_spin_unlock(PG_locked, &compound_page->flags);
    }
    else
        pr_info("slub_info %px don't free.
", slub_info);

    //TODO: EXPORT_SYMBOL(print_tracking)
    if (slab_kmem_cache->flags & SLAB_STORE_USER)
        print_tracking(slab_kmem_cache, slub_info);

    //prev slab info
    return __pa(slub_info - slab_kmem_cache->size);
}

static void _check_and_print_slub_info(unsigned long ptr)
{
    struct memblock_region *region;
    phys_addr_t start,end;
    phys_addr_t prev_slub;

    for_each_memblock(memory, region)
    {
        start = PFN_PHYS(memblock_region_memory_base_pfn(region));
        end = PFN_PHYS(memblock_region_memory_end_pfn(region)) - 1;

        if (__va(start) <= (void*)ptr && __va(end) >= (void*)ptr) {
            pr_err("ptr:%px - pa:%px
",(void*)ptr, (void*)__pa(ptr));
            prev_slub = _find_prev_slab_and_print_slub(__pa(ptr));
            prev_slub = _find_prev_slab_and_print_slub(prev_slub);
            break;
        }
    }
}

static void _dump_register_data(const char *prefix, unsigned long ptr)
{
    uint64_t pa;
    void *buf;

    if (arm_va2pa_helper((void*)ptr, &pa) == true) {
        buf = __va(pa);
        print_hex_dump(KERN_ERR, prefix, DUMP_PREFIX_ADDRESS, 16, 8, buf - (DUMP_MEMORY_SIZE>>1), DUMP_MEMORY_SIZE, true);
    }
}

static void _dump_valid_memory_for_register(struct pt_regs *regs)
{
    int i;
    char buffer[64];

    pr_err("
SP register:%px memory:
",(void*)regs->sp);
    if (!check_addr_valid(regs->sp))
        _dump_register_data("SP ", regs->sp);

    pr_err("
PC register:%px memory:
",(void*)regs->pc);
    if (!check_addr_valid(regs->pc))
        _dump_register_data("PC ", regs->pc);

    for (i = 0; i < 31; i ++) {
        if (!check_addr_valid(regs->regs[i])){
            snprintf(buffer, sizeof(buffer), "X%d ", i);
            pr_err("
X%d register:%px memory:
", i, (void*)regs->sp);
            _dump_register_data((const char *)buffer, regs->regs[i]);
            _check_and_print_slub_info(regs->regs[i]);
        }
        else {
            pr_err("
X%d register:%px memory invalid.
", i, (void*)regs->sp);
        }
    }
}

static int notify_panic_handler(struct notifier_block *self, unsigned long val, void *data)
{
    struct die_args *pargs = (struct die_args*)data;

    memcpy(&saved_regs, pargs->regs, sizeof(saved_regs));
    if (val == DIE_OOPS)
        pr_err("System is die oops.
");
    else
        pr_err("System is die panic.
");

    _dump_register(&saved_regs);
    _dump_valid_memory_for_register(&saved_regs);
    return NOTIFY_OK;
}

static struct notifier_block notify_oops_panic = {
    .notifier_call = notify_panic_handler,
};

static int __init notify_oops_panic_init(void)
{
    atomic_notifier_chain_register(&panic_notifier_list,&notify_oops_panic);
    register_die_notifier(&notify_oops_panic);

    return 0;
}

void notify_oops_panic_exit(void)
{
    atomic_notifier_chain_unregister(&panic_notifier_list,&notify_oops_panic);
    unregister_die_notifier(&notify_oops_panic);
}

static int __init setup_oops_panic_notify(char *str)
{
    if (*str++ != '=' || !*str)
        return -EINVAL;
    if (!strcmp(str,"on") || *str == 1)
        oops_panic_notify = 1;
    return 0;
}
__setup("oops_panic_notify", setup_oops_panic_notify);
module_init(notify_oops_panic_init);
module_exit(notify_oops_panic_exit);
MODUL_LICENSE("GPL v2");

免责声明:文章转载自《一种memory问题导致的kernel panic的处理方法》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇QT_文本编辑器_源码下载eclipse创建maven项目(详细)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

linux下常用文件传输命令 (转)

因为工作原因,需要经常在不同的服务器见进行文件传输,特别是大文件的传输,因此对linux下不同服务器间数据传输命令和工具进行了研究和总结。主要是rcp,scp,rsync,ftp,sftp,lftp,wget,curl。 rcp rcp不是一种安全的的传输文件的方式,rcp通过rsh(rsh见下面)来执行远程命令,要使用rcp必须经过一些配置,现在rcp...

linux 没有yum命令,安装yum命令的方法

先说背景, 2020/12/26买了一个国外的linux云服务器, centos 7  64位,想用yum命令安装jdk环境,结果发现没有yum这个命令,这下操蛋了. 于是乎,百度,发现很多都是没有用的.说的不好听点,都是浪费时间的垃圾教程. 装个yum,搞了几个小时,原来自己是这么的菜, 但是,自己菜是一方面,我感觉这几年网络上乱七八糟的东西太多了.已经...

linux命令&amp;lt;服务进程、查看日志、文件编辑、赋权等&amp;gt;

sudo命令以系统管理者的身份执行指令,也就是说,经由 sudo 所执行的指令就好像是 root 亲自执行。 sudo apt-get update  更新 /etc/apt/sources.list 和 /etc/apt/sources.list.d 中列出的源的地址,这样才能获取到最新的软件包; sudo apt-get upgrade  升级已安装的...

通过MacOS的ssh远程打开linux的firefox(通过X11协议实现图形化显示)

1)X11有两个部分组成,一个是X server,一个是X client; --运行在Mac上的是X server,Xserver主要负责显示用户界面,管理显示器以及鼠标和键盘,把相关的动作告诉X client,通常比较多的Xserver是:windows的Xming和MobaXterm,MAC有Xquartz,以及其他各种Xserver的应用。 --运行...

深入分析解决Deepin 15环境变量修改问题,完成JAVA环境搭建

最近使用deepin配置JAVA环境时发现不论是修改/etc/profile还是~/.profile多次尝试后均无效,不得其解,最后通过官方论坛看到大神对deepin环境配置的解释,以及寻找到相关解决方案。为了解决这个问题跟着了baidu走了不少弯路,过程中也学到不少东西,所以梳理出来,希望不仅能对deepin用户起到帮助而且还能了解对LInux的一些运行...

Delve调试器 汇编

  目前Go语言支持GDB、LLDB和Delve几种调试器。其中GDB是最早支持的调试工具,LLDB是macOS系统推荐的标准调试工具。但是GDB和LLDB对Go语言的专有特性都缺乏很大支持,而只有Delve是专门为Go语言设计开发的调试工具。而且Delve本身也是采用Go语言开发,对Windows平台也提供了一样的支持。本节我们基于Delve简单解释如何...