>From ba05910c7915e3f95a0cd0893b9abc6cd98ab22e Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Sun, 2 Dec 2012 21:26:21 +0800 Subject: [PATCH 2/3] x86, memhotplug: reserve memory from bootmem allocator for memory hotplug There's no mechanism to migrate pages allocated from bootmem allocator, thus a memory device may become irremovable if bootmem allocates any pages from it. This patch introduces a mechanism to 1) reserve memory from bootmem allocator for hotplug early 'enough' during boot. 2) free reserve memory into buddy system at late when memory hogplug infrastructure has been initialized. Signed-off-by: Jiang Liu --- arch/x86/kernel/setup.c | 11 ++++++++ arch/x86/mm/init.c | 56 ++++++++++++++++++++++++++++++++++++++++ arch/x86/mm/init_32.c | 2 ++ arch/x86/mm/init_64.c | 2 ++ include/linux/memblock.h | 1 + include/linux/memory_hotplug.h | 5 ++++ mm/Kconfig | 1 + 7 files changed, 78 insertions(+) diff --git a/arch/x86/kernel/setup.c b/arch/x86/kernel/setup.c index ca45696..93f6f10 100644 --- a/arch/x86/kernel/setup.c +++ b/arch/x86/kernel/setup.c @@ -940,6 +940,17 @@ void __init setup_arch(char **cmdline_p) max_low_pfn = max_pfn; } #endif + + /* + * Try to reserve memory from bootmem allocator for memory hotplug + * before updating memblock.current_limit to cover all low memory. + * Until now memblock.current_limit is still set to the initial value + * of max_pfn_mapped, which is 512M on x86_64 and xxx on i386. And + * memblock allocates available memory in reverse order, so we almost + * have no chance to reserve memory below 512M for memory hotplug. + */ + reserve_memory_for_hotplug(); + memblock.current_limit = get_max_mapped(); dma_contiguous_reserve(0); diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c index d7aea41..36bb5c2 100644 --- a/arch/x86/mm/init.c +++ b/arch/x86/mm/init.c @@ -424,3 +424,59 @@ void __init zone_sizes_init(void) free_area_init_nodes(max_zone_pfns); } +#ifdef CONFIG_MEMORY_HOTREMOVE +static int __init reserve_bootmem_for_hotplug(phys_addr_t base, + phys_addr_t size) +{ + if (memblock_is_region_reserved(base, size) || + memblock_reserve(base, size) < 0) + return -EBUSY; + + BUG_ON(memblock_mark_tag(base, size, MEMBLOCK_TAG_HOTPLUG, NULL)); + + return 0; +} + +/* + * Try to reserve low memory for hotplug according to user configured + * movablecore_map. Movable zone hasn't been determined yet, so can't rely + * on zone_movable_is_highmem() but to reserve all low memory configured by + * movablecore_map parameter. + * Assume entries in movablecore_map.map are sorted in increasing order. + */ +static int __init reserve_hotplug_memory_from_movable_map(void) +{ + int i; + phys_addr_t start, end; + struct movablecore_entry *ep; + + if (movablecore_map.nr_map == 0) + return 0; + + for (i = 0; i < movablecore_map.nr_map; i++) { + ep = &movablecore_map.map[i]; + start = ep->start << PAGE_SHIFT; + end = (min(ep->end, max_low_pfn) + 1) << PAGE_SHIFT; + if (end <= start) + break; + + if (reserve_bootmem_for_hotplug(start, end - start)) + pr_warn("mm: failed to reserve lowmem [%#016llx-%#016llx] for hotplug.", + (unsigned long long)start, + (unsigned long long)end - 1); + } + + return 1; +} + +void __init reserve_memory_for_hotplug(void) +{ + if (reserve_hotplug_memory_from_movable_map()) + return; +} + +void __init free_memory_reserved_for_hotplug(void) +{ + memblock_free_all_with_tag(MEMBLOCK_TAG_HOTPLUG); +} +#endif diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 11a5800..815700a 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -745,6 +745,8 @@ void __init mem_init(void) */ set_highmem_pages_init(); + free_memory_reserved_for_hotplug(); + /* this will put all low memory onto the freelists */ totalram_pages += free_all_bootmem(); diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 3baff25..1a92fd6 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -695,6 +695,8 @@ void __init mem_init(void) reservedpages = 0; + free_memory_reserved_for_hotplug(); + /* this will put all low memory onto the freelists */ #ifdef CONFIG_NUMA totalram_pages = numa_free_all_bootmem(); diff --git a/include/linux/memblock.h b/include/linux/memblock.h index 40dea53..5420ed9 100644 --- a/include/linux/memblock.h +++ b/include/linux/memblock.h @@ -124,6 +124,7 @@ void __next_free_mem_range_rev(u64 *idx, int nid, phys_addr_t *out_start, #ifdef CONFIG_HAVE_MEMBLOCK_TAG #define MEMBLOCK_TAG_DEFAULT 0x0 /* default tag for bootmem allocatror */ +#define MEMBLOCK_TAG_HOTPLUG 0x1 /* reserved for memory hotplug */ int memblock_mark_tag(phys_addr_t base, phys_addr_t size, int tag, void *data); void memblock_free_all_with_tag(int tag); diff --git a/include/linux/memory_hotplug.h b/include/linux/memory_hotplug.h index 95573ec..edf183d 100644 --- a/include/linux/memory_hotplug.h +++ b/include/linux/memory_hotplug.h @@ -222,6 +222,8 @@ static inline void unlock_memory_hotplug(void) {} #ifdef CONFIG_MEMORY_HOTREMOVE extern int is_mem_section_removable(unsigned long pfn, unsigned long nr_pages); +extern void reserve_memory_for_hotplug(void); +extern void free_memory_reserved_for_hotplug(void); #else static inline int is_mem_section_removable(unsigned long pfn, @@ -229,6 +231,9 @@ static inline int is_mem_section_removable(unsigned long pfn, { return 0; } + +static inline void reserve_memory_for_hotplug(void) {} +static inline void free_memory_reserved_for_hotplug(void) {} #endif /* CONFIG_MEMORY_HOTREMOVE */ extern int mem_online_node(int nid); diff --git a/mm/Kconfig b/mm/Kconfig index 5080390..9d69e5d 100644 --- a/mm/Kconfig +++ b/mm/Kconfig @@ -160,6 +160,7 @@ config MEMORY_HOTPLUG_SPARSE config MEMORY_HOTREMOVE bool "Allow for memory hot remove" + select HAVE_MEMBLOCK_TAG depends on MEMORY_HOTPLUG && ARCH_ENABLE_MEMORY_HOTREMOVE depends on MIGRATION -- 1.7.9.5