zoukankan      html  css  js  c++  java
  • 一种memory问题导致的kernel panic的处理方法

    下面是一个在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");
  • 相关阅读:
    javascript字符串加密解密函数
    javascript实现blob加密视频源地址
    HTML网页实现flv视频播放
    DELL r720远控装系统
    nginx笔记
    Centos7防火墙配置
    CentOS7.x搭建LNMP
    搭建可道云私人云盘系统
    网络设备巡检常用命令-摘自星球成员马磊分享
    部署Windows Server 2012的WSUS补丁服务器
  • 原文地址:https://www.cnblogs.com/smilingsusu/p/14604094.html
Copyright © 2011-2022 走看看