In the kernel crash process, call generate_crash_dump_bitmap() to scans
all memory pages, clear the bit for all excluded memory pages in the
reserved memory.

Signed-off-by: Jingbai Ma <jingbai...@hp.com>
---
 kernel/crash_dump_bitmap.c |  156 ++++++++++++++++++++++++++++++++++++++++++++
 kernel/kexec.c             |    5 +
 2 files changed, 161 insertions(+), 0 deletions(-)

diff --git a/kernel/crash_dump_bitmap.c b/kernel/crash_dump_bitmap.c
index e743cdd..eed13ca 100644
--- a/kernel/crash_dump_bitmap.c
+++ b/kernel/crash_dump_bitmap.c
@@ -23,6 +23,8 @@
 
 #ifdef CONFIG_CRASH_DUMP_BITMAP
 
+#define virt_to_pfn(kaddr) (__pa(kaddr) >> PAGE_SHIFT)
+
 phys_addr_t crash_dump_bitmap_mem;
 EXPORT_SYMBOL(crash_dump_bitmap_mem);
 
@@ -35,6 +37,7 @@ EXPORT_SYMBOL(crash_dump_bitmap_ctrl);
 struct crash_dump_bitmap_info crash_dump_bitmap_info;
 EXPORT_SYMBOL(crash_dump_bitmap_info);
 
+
 /* Location of the reserved area for the crash_dump_bitmap */
 struct resource crash_dump_bitmap_res = {
        .name  = "Crash dump bitmap",
@@ -42,4 +45,157 @@ struct resource crash_dump_bitmap_res = {
        .end   = 0,
        .flags = IORESOURCE_BUSY | IORESOURCE_MEM
 };
+
+inline void set_crash_dump_bitmap(unsigned long pfn, int val)
+{
+       phys_addr_t paddr = crash_dump_bitmap_info.bitmap + (pfn >> 3);
+       unsigned char *vaddr;
+       unsigned char bit = (pfn & 7);
+
+       if (unlikely(paddr > (crash_dump_bitmap_mem
+               + crash_dump_bitmap_mem_size))) {
+               pr_err(
+               "crash_dump_bitmap: pfn exceed limit. pfn=%ld, addr=0x%llX\n",
+               pfn, paddr);
+               return;
+       }
+
+       vaddr = (unsigned char *)__va(paddr);
+
+       if (val)
+               *vaddr |= (1U << bit);
+       else
+               *vaddr &= (~(1U << bit));
+}
+
+void generate_crash_dump_bitmap(void)
+{
+       pg_data_t *pgdat;
+       struct zone *zone;
+       unsigned long flags;
+       int order, t;
+       struct list_head *curr;
+       unsigned long zone_free_pages;
+       phys_addr_t addr;
+
+       if (!crash_dump_bitmap_mem) {
+               pr_info("crash_dump_bitmap: no crash_dump_bitmap memory.\n");
+               return;
+       }
+
+       pr_info(
+       "Excluding pages: bitmap=%d, cache=%d, private=%d, user=%d, free=%d\n",
+               crash_dump_bitmap_ctrl.exclude_crash_dump_bitmap_pages,
+               crash_dump_bitmap_ctrl.exclude_cache_pages,
+               crash_dump_bitmap_ctrl.exclude_cache_private_pages,
+               crash_dump_bitmap_ctrl.exclude_user_pages,
+               crash_dump_bitmap_ctrl.exclude_free_pages);
+
+       crash_dump_bitmap_info.free_pages = 0;
+       crash_dump_bitmap_info.cache_pages = 0;
+       crash_dump_bitmap_info.cache_private_pages = 0;
+       crash_dump_bitmap_info.user_pages = 0;
+       crash_dump_bitmap_info.hwpoison_pages = 0;
+
+       /* Set all bits on bitmap */
+       memset(__va(crash_dump_bitmap_info.bitmap), 0xff,
+               crash_dump_bitmap_info.bitmap_size);
+
+       /* Exclude all crash_dump_bitmap pages */
+       if (crash_dump_bitmap_ctrl.exclude_crash_dump_bitmap_pages) {
+               for (addr = crash_dump_bitmap_mem; addr <
+                       crash_dump_bitmap_mem + crash_dump_bitmap_mem_size;
+                       addr += PAGE_SIZE)
+                               set_crash_dump_bitmap(
+                                       virt_to_pfn(__va(addr)), 0);
+       }
+
+       /* Exclude unnecessary pages */
+       for_each_online_pgdat(pgdat) {
+               unsigned long i;
+               unsigned long flags;
+
+               pgdat_resize_lock(pgdat, &flags);
+               for (i = 0; i < pgdat->node_spanned_pages; i++) {
+                       struct page *page;
+                       unsigned long pfn = pgdat->node_start_pfn + i;
+
+                       if (!pfn_valid(pfn))
+                               continue;
+
+                       page = pfn_to_page(pfn);
+
+                       /* Exclude the cache pages without the private page */
+                       if (crash_dump_bitmap_ctrl.exclude_cache_pages
+                               && (PageLRU(page) || PageSwapCache(page))
+                               && !page_has_private(page) && !PageAnon(page)) {
+                                       set_crash_dump_bitmap(pfn, 0);
+                                       crash_dump_bitmap_info.cache_pages++;
+                       }
+                       /* Exclude the cache pages with private page */
+                       else if (
+                       crash_dump_bitmap_ctrl.exclude_cache_private_pages
+                               && (PageLRU(page) || PageSwapCache(page))
+                               && !PageAnon(page)) {
+                                       set_crash_dump_bitmap(pfn, 0);
+                               crash_dump_bitmap_info.cache_private_pages++;
+                       }
+                       /* Exclude the pages used by user process */
+                       else if (crash_dump_bitmap_ctrl.exclude_user_pages
+                                       && PageAnon(page)) {
+                                       set_crash_dump_bitmap(pfn, 0);
+                                       crash_dump_bitmap_info.user_pages++;
+                       }
+#ifdef CONFIG_MEMORY_FAILURE
+                       /* Exclude the hwpoison pages */
+                       else if (PageHWPoison(page)) {
+                                       set_crash_dump_bitmap(pfn, 0);
+                                       crash_dump_bitmap_info.hwpoison_pages++;
+                       }
+#endif
+               }
+               pgdat_resize_unlock(pgdat, &flags);
+       }
+
+       /* Exclude the free pages managed by a buddy system */
+       if (crash_dump_bitmap_ctrl.exclude_free_pages) {
+               for_each_populated_zone(zone) {
+                       if (!zone->spanned_pages)
+                               continue;
+
+                       spin_lock_irqsave(&zone->lock, flags);
+
+                       zone_free_pages = 0;
+                       for_each_migratetype_order(order, t) {
+                               list_for_each(
+                               curr, &zone->free_area[order].free_list[t]) {
+                                       unsigned long i;
+                                       struct page *page = list_entry(curr,
+                                               struct page, lru);
+                                       for (i = 0; i < (1 << order); i++) {
+                                               set_crash_dump_bitmap(
+                                               page_to_pfn(page + i), 0);
+                                               zone_free_pages++;
+                                       crash_dump_bitmap_info.free_pages++;
+                                       }
+                               }
+                       }
+                       spin_unlock_irqrestore(&zone->lock, flags);
+               }
+       }
+
+       pr_info("crash_dump_bitmap: excluded pages: cache=%ld, private=%ld\n",
+               crash_dump_bitmap_info.cache_pages,
+               crash_dump_bitmap_info.cache_private_pages);
+       pr_info("crash_dump_bitmap: excluded pages: user=%ld, free=%ld\n",
+               crash_dump_bitmap_info.user_pages,
+               crash_dump_bitmap_info.free_pages);
+       pr_info("crash_dump_bitmap: excluded pages: hwpoison=%ld\n",
+               crash_dump_bitmap_info.hwpoison_pages);
+}
+EXPORT_SYMBOL(generate_crash_dump_bitmap);
+#else
+void generate_crash_dump_bitmap(void)
+{
+}
 #endif /* CONFIG_CRASH_DUMP_BITMAP */
diff --git a/kernel/kexec.c b/kernel/kexec.c
index bddd3d7..ce00f0f 100644
--- a/kernel/kexec.c
+++ b/kernel/kexec.c
@@ -32,6 +32,7 @@
 #include <linux/vmalloc.h>
 #include <linux/swap.h>
 #include <linux/syscore_ops.h>
+#include <linux/crash_dump_bitmap.h>
 
 #include <asm/page.h>
 #include <asm/uaccess.h>
@@ -1097,6 +1098,7 @@ void crash_kexec(struct pt_regs *regs)
                        crash_setup_regs(&fixed_regs, regs);
                        crash_save_vmcoreinfo();
                        machine_crash_shutdown(&fixed_regs);
+                       generate_crash_dump_bitmap();
                        machine_kexec(kexec_crash_image);
                }
                mutex_unlock(&kexec_mutex);
@@ -1495,6 +1497,9 @@ static int __init crash_save_vmcoreinfo_init(void)
        VMCOREINFO_SYMBOL(mem_map);
        VMCOREINFO_SYMBOL(contig_page_data);
 #endif
+#ifdef CONFIG_CRASH_DUMP_BITMAP
+       VMCOREINFO_SYMBOL(crash_dump_bitmap_info);
+#endif
 #ifdef CONFIG_SPARSEMEM
        VMCOREINFO_SYMBOL(mem_section);
        VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS);

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to