Subject: [PATCH] memblock: free allocated memblock_reserved_regions later In memblock_free_reserved_regions, will call memblock_free(), but memblock_free() would double reserved.regions too, so we could free old range for reserved.regions. Also tj said there is another bug could be related to this too. | I don't think we're saving any noticeable | amount by doing this "free - give it to page allocator - reserve | again" dancing. We should just allocate regions aligned to page | boundaries and free them later when memblock is no longer in use. So try to allocate that in PAGE_SIZE alignment and free that later. Cc: Tejun Heo Cc: Benjamin Herrenschmidt Cc: Andrew Morton Signed-off-by: Yinghai Lu --- include/linux/memblock.h | 4 --- mm/memblock.c | 59 +++++++++++++++++++---------------------------- mm/nobootmem.c | 36 +++++++++++++++++----------- 3 files changed, 48 insertions(+), 51 deletions(-) Index: linux-2.6/include/linux/memblock.h =================================================================== --- linux-2.6.orig/include/linux/memblock.h +++ linux-2.6/include/linux/memblock.h @@ -50,9 +50,7 @@ phys_addr_t memblock_find_in_range_node( phys_addr_t size, phys_addr_t align, int nid); phys_addr_t memblock_find_in_range(phys_addr_t start, phys_addr_t end, phys_addr_t size, phys_addr_t align); -int memblock_free_reserved_regions(void); -int memblock_reserve_reserved_regions(void); - +phys_addr_t get_allocated_memblock_reserved_regions_info(phys_addr_t *addr); void memblock_allow_resize(void); int memblock_add_node(phys_addr_t base, phys_addr_t size, int nid); int memblock_add(phys_addr_t base, phys_addr_t size); Index: linux-2.6/mm/memblock.c =================================================================== --- linux-2.6.orig/mm/memblock.c +++ linux-2.6/mm/memblock.c @@ -143,30 +143,6 @@ phys_addr_t __init_memblock memblock_fin MAX_NUMNODES); } -/* - * Free memblock.reserved.regions - */ -int __init_memblock memblock_free_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_free(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - -/* - * Reserve memblock.reserved.regions - */ -int __init_memblock memblock_reserve_reserved_regions(void) -{ - if (memblock.reserved.regions == memblock_reserved_init_regions) - return 0; - - return memblock_reserve(__pa(memblock.reserved.regions), - sizeof(struct memblock_region) * memblock.reserved.max); -} - static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r) { type->total_size -= type->regions[r].size; @@ -184,6 +160,18 @@ static void __init_memblock memblock_rem } } +phys_addr_t __init_memblock get_allocated_memblock_reserved_regions_info( + phys_addr_t *addr) +{ + if (memblock.reserved.regions == memblock_reserved_init_regions) + return 0; + + *addr = __pa(memblock.reserved.regions); + + return PAGE_ALIGN(sizeof(struct memblock_region) * + memblock.reserved.max); +} + /** * memblock_double_array - double the size of the memblock regions array * @type: memblock type of the regions array being doubled @@ -216,7 +204,7 @@ static int __init_memblock memblock_doub /* Calculate new doubled size */ old_size = type->max * sizeof(struct memblock_region); - new_size = old_size << 1; + new_size = PAGE_ALIGN(old_size << 1); /* Retrieve the slab flag */ if (type == &memblock.memory) @@ -245,32 +233,35 @@ static int __init_memblock memblock_doub addr = memblock_find_in_range(new_area_start + new_area_size, memblock.current_limit, - new_size, sizeof(phys_addr_t)); + new_size, PAGE_SIZE); if (!addr && new_area_size) addr = memblock_find_in_range(0, min(new_area_start, memblock.current_limit), - new_size, sizeof(phys_addr_t)); + new_size, PAGE_SIZE); new_array = addr ? __va(addr) : 0; } if (!addr) { - pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n", - memblock_type_name(type), type->max, type->max * 2); + pr_err("memblock: Failed to double %s array from %ld to %lld entries !\n", + memblock_type_name(type), type->max, + new_size/sizeof(struct memblock_region)); return -1; } - memblock_dbg("memblock: %s array is doubled to %ld at [%#010llx-%#010llx]", - memblock_type_name(type), type->max * 2, (u64)addr, (u64)addr + new_size - 1); + memblock_dbg("memblock: %s array is doubled to %lld at [%#010llx-%#010llx]", + memblock_type_name(type), + new_size/sizeof(struct memblock_region), + (u64)addr, (u64)addr + new_size - 1); /* Found space, we now need to move the array over before * we add the reserved region since it may be our reserved * array itself that is full. */ memcpy(new_array, type->regions, old_size); - memset(new_array + type->max, 0, old_size); + memset(new_array + type->max, 0, new_size - old_size); old_array = type->regions; type->regions = new_array; - type->max <<= 1; + type->max = new_size/sizeof(struct memblock_region); /* Free old array. We needn't free it if the array is the * static one @@ -279,7 +270,7 @@ static int __init_memblock memblock_doub kfree(old_array); else if (old_array != memblock_memory_init_regions && old_array != memblock_reserved_init_regions) - memblock_free(__pa(old_array), old_size); + memblock_free(__pa(old_array), PAGE_ALIGN(old_size)); /* Reserve the new array if that comes from the memblock. * Otherwise, we needn't do it Index: linux-2.6/mm/nobootmem.c =================================================================== --- linux-2.6.orig/mm/nobootmem.c +++ linux-2.6/mm/nobootmem.c @@ -105,27 +105,35 @@ static void __init __free_pages_memory(u __free_pages_bootmem(pfn_to_page(i), 0); } +static unsigned long __init __free_memory_core(phys_addr_t start, + phys_addr_t end) +{ + unsigned long start_pfn = PFN_UP(start); + unsigned long end_pfn = min_t(unsigned long, + PFN_DOWN(end), max_low_pfn); + + if (start_pfn > end_pfn) + return 0; + + __free_pages_memory(start_pfn, end_pfn); + + return end_pfn - start_pfn; +} + unsigned long __init free_low_memory_core_early(int nodeid) { unsigned long count = 0; - phys_addr_t start, end; + phys_addr_t start, end, size; u64 i; - /* free reserved array temporarily so that it's treated as free area */ - memblock_free_reserved_regions(); + for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) + count += __free_memory_core(start, end); - for_each_free_mem_range(i, MAX_NUMNODES, &start, &end, NULL) { - unsigned long start_pfn = PFN_UP(start); - unsigned long end_pfn = min_t(unsigned long, - PFN_DOWN(end), max_low_pfn); - if (start_pfn < end_pfn) { - __free_pages_memory(start_pfn, end_pfn); - count += end_pfn - start_pfn; - } - } + /* free range that is used for reserved array if we allocate it */ + size = get_allocated_memblock_reserved_regions_info(&start); + if (size) + count += __free_memory_core(start, start + size); - /* put region array back? */ - memblock_reserve_reserved_regions(); return count; }