Memory-Hotplug: Fix the bug on interface /dev/mem for 64-bit kernel The new added memory can not be access by interface /dev/mem, because we do not update the variable high_memory. This patch add a new e820 entry in e820 table, and update max_pfn, max_low_pfn and high_memory. Memory hotplug still has critical issues for 32-bit kernel, and it is more important for 64-bit kernel, we fix it on 64-bit first. We add a function update_end_of_memory_vars in file arch/x86/mm/init.c to update these variables. Signed-off-by: Shaohui Zheng CC: Andi Kleen CC: Li Haicheng Reviewed-by: Wu Fengguang diff --git a/arch/x86/kernel/e820.c b/arch/x86/kernel/e820.c index a1a7876..a9b6bae 100644 --- a/arch/x86/kernel/e820.c +++ b/arch/x86/kernel/e820.c @@ -110,8 +110,8 @@ int __init e820_all_mapped(u64 start, u64 end, unsigned type) /* * Add a memory region to the kernel e820 map. */ -static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size, - int type) +static void __meminit __e820_add_region(struct e820map *e820x, u64 start, + u64 size, int type) { int x = e820x->nr_map; @@ -126,7 +126,7 @@ static void __init __e820_add_region(struct e820map *e820x, u64 start, u64 size, e820x->nr_map++; } -void __init e820_add_region(u64 start, u64 size, int type) +void __meminit e820_add_region(u64 start, u64 size, int type) { __e820_add_region(&e820, start, size, type); } diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index d406c52..51ff734 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -386,3 +387,31 @@ void free_initrd_mem(unsigned long start, unsigned long end) free_init_pages("initrd memory", start, end); } #endif + +/** + * After memory hotplug, the variable max_pfn, max_low_pfn and high_memory will + * be affected, it will be updated in this function. Memory hotplug does not + * make sense on 32-bit kernel, so we do did not concern it in this function. + */ +void __meminit __attribute__((weak)) update_end_of_memory_vars(u64 start, + u64 size) +{ +#ifdef CONFIG_X86_64 + unsigned long limit_low_pfn = 1UL<<(32 - PAGE_SHIFT); + unsigned long start_pfn = start >> PAGE_SHIFT; + unsigned long end_pfn = PFN_UP(start + size); + + if (end_pfn > max_pfn) { + max_pfn = end_pfn; + high_memory = (void *)__va(max_pfn * PAGE_SIZE - 1) + 1; + } + + /* if add to low memory, update max_low_pfn */ + if (unlikely(start_pfn < limit_low_pfn)) { + if (end_pfn <= limit_low_pfn) + max_low_pfn = end_pfn; + else + max_low_pfn = limit_low_pfn; + } +#endif /* CONFIG_X86_64 */ +} diff --git a/include/linux/bootmem.h b/include/linux/bootmem.h index b10ec49..84533a5 100644 --- a/include/linux/bootmem.h +++ b/include/linux/bootmem.h @@ -13,6 +13,7 @@ extern unsigned long max_low_pfn; extern unsigned long min_low_pfn; +extern void update_end_of_memory_vars(u64 start, u64 size); /* * highest page diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 030ce8a..cd54ad1 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -523,6 +523,13 @@ int __ref add_memory(int nid, u64 start, u64 size) BUG_ON(ret); } + printk(KERN_INFO "Adding memory region to e820 table (start:%016Lx, size:%016Lx).\n", + (unsigned long long)start, (unsigned long long)size); + e820_add_region(start, size, E820_RAM); + + /* update max_pfn, max_low_pfn and high_memory */ + update_end_of_memory_vars(start, size); + goto out; error: