2983 lines · 81.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * Procedures for maintaining information about logical memory blocks. * * Peter Bergner, IBM Corp.	June 2001. * Copyright (C) 2001 Peter Bergner. */ #include <linux/kernel.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/bitops.h>#include <linux/poison.h>#include <linux/pfn.h>#include <linux/debugfs.h>#include <linux/kmemleak.h>#include <linux/seq_file.h>#include <linux/memblock.h>#include <linux/mutex.h>#include <linux/string_helpers.h> #include <linux/libfdt.h>#include <linux/kexec_handover.h>#include <linux/kho/abi/memblock.h> #include <asm/sections.h>#include <linux/io.h> #include "internal.h"#include "mm_init.h" #define INIT_MEMBLOCK_REGIONS			128#define INIT_PHYSMEM_REGIONS			4 #ifndef INIT_MEMBLOCK_RESERVED_REGIONS# define INIT_MEMBLOCK_RESERVED_REGIONS		INIT_MEMBLOCK_REGIONS#endif #ifndef INIT_MEMBLOCK_MEMORY_REGIONS#define INIT_MEMBLOCK_MEMORY_REGIONS		INIT_MEMBLOCK_REGIONS#endif /** * DOC: memblock overview * * Memblock is a method of managing memory regions during the early * boot period when the usual kernel memory allocators are not up and * running. * * Memblock views the system memory as collections of contiguous * regions. There are several types of these collections: * * * ``memory`` - describes the physical memory available to the *   kernel; this may differ from the actual physical memory installed *   in the system, for instance when the memory is restricted with *   ``mem=`` command line parameter * * ``reserved`` - describes the regions that were allocated * * ``physmem`` - describes the actual physical memory available during *   boot regardless of the possible restrictions and memory hot(un)plug; *   the ``physmem`` type is only available on some architectures. * * Each region is represented by struct memblock_region that * defines the region extents, its attributes and NUMA node id on NUMA * systems. Every memory type is described by the struct memblock_type * which contains an array of memory regions along with * the allocator metadata. The "memory" and "reserved" types are nicely * wrapped with struct memblock. This structure is statically * initialized at build time. The region arrays are initially sized to * %INIT_MEMBLOCK_MEMORY_REGIONS for "memory" and * %INIT_MEMBLOCK_RESERVED_REGIONS for "reserved". The region array * for "physmem" is initially sized to %INIT_PHYSMEM_REGIONS. * The memblock_allow_resize() enables automatic resizing of the region * arrays during addition of new regions. This feature should be used * with care so that memory allocated for the region array will not * overlap with areas that should be reserved, for example initrd. * * The early architecture setup should tell memblock what the physical * memory layout is by using memblock_add() or memblock_add_node() * functions. The first function does not assign the region to a NUMA * node and it is appropriate for UMA systems. Yet, it is possible to * use it on NUMA systems as well and assign the region to a NUMA node * later in the setup process using memblock_set_node(). The * memblock_add_node() performs such an assignment directly. * * Once memblock is setup the memory can be allocated using one of the * API variants: * * * memblock_phys_alloc*() - these functions return the **physical** *   address of the allocated memory * * memblock_alloc*() - these functions return the **virtual** address *   of the allocated memory. * * Note, that both API variants use implicit assumptions about allowed * memory ranges and the fallback methods. Consult the documentation * of memblock_alloc_internal() and memblock_alloc_range_nid() * functions for more elaborate description. * * As the system boot progresses, the architecture specific mem_init() * function frees all the memory to the buddy page allocator. * * Unless an architecture enables %CONFIG_ARCH_KEEP_MEMBLOCK, the * memblock data structures (except "physmem") will be discarded after the * system initialization completes. */ #ifndef CONFIG_NUMAstruct pglist_data __refdata contig_page_data;EXPORT_SYMBOL(contig_page_data);#endif unsigned long max_low_pfn;unsigned long min_low_pfn;unsigned long max_pfn;unsigned long long max_possible_pfn; #ifdef CONFIG_MEMBLOCK_KHO_SCRATCH/* When set to true, only allocate from MEMBLOCK_KHO_SCRATCH ranges */static bool kho_scratch_only;#else#define kho_scratch_only false#endif static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS] __initdata_memblock;static struct memblock_region memblock_reserved_init_regions[INIT_MEMBLOCK_RESERVED_REGIONS] __initdata_memblock;#ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAPstatic struct memblock_region memblock_physmem_init_regions[INIT_PHYSMEM_REGIONS];#endif struct memblock memblock __initdata_memblock = {	.memory.regions		= memblock_memory_init_regions,	.memory.max		= INIT_MEMBLOCK_MEMORY_REGIONS,	.memory.name		= "memory", 	.reserved.regions	= memblock_reserved_init_regions,	.reserved.max		= INIT_MEMBLOCK_RESERVED_REGIONS,	.reserved.name		= "reserved", 	.bottom_up		= false,	.current_limit		= MEMBLOCK_ALLOC_ANYWHERE,}; #ifdef CONFIG_HAVE_MEMBLOCK_PHYS_MAPstruct memblock_type physmem = {	.regions		= memblock_physmem_init_regions,	.max			= INIT_PHYSMEM_REGIONS,	.name			= "physmem",};#endif /* * keep a pointer to &memblock.memory in the text section to use it in * __next_mem_range() and its helpers. *  For architectures that do not keep memblock data after init, this * pointer will be reset to NULL at memblock_discard() */static __refdata struct memblock_type *memblock_memory = &memblock.memory; #define for_each_memblock_type(i, memblock_type, rgn)			\	for (i = 0, rgn = &memblock_type->regions[0];			\	     i < memblock_type->cnt;					\	     i++, rgn = &memblock_type->regions[i]) #define memblock_dbg(fmt, ...)						\	do {								\		if (memblock_debug)					\			pr_info(fmt, ##__VA_ARGS__);			\	} while (0) static int memblock_debug __initdata_memblock;static bool system_has_some_mirror __initdata_memblock;static int memblock_can_resize __initdata_memblock;static int memblock_memory_in_slab __initdata_memblock;static int memblock_reserved_in_slab __initdata_memblock; bool __init_memblock memblock_has_mirror(void){	return system_has_some_mirror;} static enum memblock_flags __init_memblock choose_memblock_flags(void){	/* skip non-scratch memory for kho early boot allocations */	if (kho_scratch_only)		return MEMBLOCK_KHO_SCRATCH; 	return system_has_some_mirror ? MEMBLOCK_MIRROR : MEMBLOCK_NONE;} /* adjust *@size so that (@base + *@size) doesn't overflow, return new size */static inline phys_addr_t memblock_cap_size(phys_addr_t base, phys_addr_t *size){	return *size = min(*size, PHYS_ADDR_MAX - base);} /* * Address comparison utilities */unsigned long __init_memblockmemblock_addrs_overlap(phys_addr_t base1, phys_addr_t size1, phys_addr_t base2,		       phys_addr_t size2){	return ((base1 < (base2 + size2)) && (base2 < (base1 + size1)));} bool __init_memblock memblock_overlaps_region(struct memblock_type *type,					phys_addr_t base, phys_addr_t size){	unsigned long i; 	memblock_cap_size(base, &size); 	for (i = 0; i < type->cnt; i++)		if (memblock_addrs_overlap(base, size, type->regions[i].base,					   type->regions[i].size))			return true;	return false;} /** * __memblock_find_range_bottom_up - find free area utility in bottom-up * @start: start of candidate range * @end: end of candidate range, can be %MEMBLOCK_ALLOC_ANYWHERE or *       %MEMBLOCK_ALLOC_ACCESSIBLE * @size: size of free area to find * @align: alignment of free area to find * @nid: nid of the free area to find, %NUMA_NO_NODE for any node * @flags: pick from blocks based on memory attributes * * Utility called from memblock_find_in_range_node(), find free area bottom-up. * * Return: * Found address on success, 0 on failure. */static phys_addr_t __init_memblock__memblock_find_range_bottom_up(phys_addr_t start, phys_addr_t end,				phys_addr_t size, phys_addr_t align, int nid,				enum memblock_flags flags){	phys_addr_t this_start, this_end, cand;	u64 i; 	for_each_free_mem_range(i, nid, flags, &this_start, &this_end, NULL) {		this_start = clamp(this_start, start, end);		this_end = clamp(this_end, start, end); 		cand = round_up(this_start, align);		if (cand < this_end && this_end - cand >= size)			return cand;	} 	return 0;} /** * __memblock_find_range_top_down - find free area utility, in top-down * @start: start of candidate range * @end: end of candidate range, can be %MEMBLOCK_ALLOC_ANYWHERE or *       %MEMBLOCK_ALLOC_ACCESSIBLE * @size: size of free area to find * @align: alignment of free area to find * @nid: nid of the free area to find, %NUMA_NO_NODE for any node * @flags: pick from blocks based on memory attributes * * Utility called from memblock_find_in_range_node(), find free area top-down. * * Return: * Found address on success, 0 on failure. */static phys_addr_t __init_memblock__memblock_find_range_top_down(phys_addr_t start, phys_addr_t end,			       phys_addr_t size, phys_addr_t align, int nid,			       enum memblock_flags flags){	phys_addr_t this_start, this_end, cand;	u64 i; 	for_each_free_mem_range_reverse(i, nid, flags, &this_start, &this_end,					NULL) {		this_start = clamp(this_start, start, end);		this_end = clamp(this_end, start, end); 		if (this_end < size)			continue; 		cand = round_down(this_end - size, align);		if (cand >= this_start)			return cand;	} 	return 0;} /** * memblock_find_in_range_node - find free area in given range and node * @size: size of free area to find * @align: alignment of free area to find * @start: start of candidate range * @end: end of candidate range, can be %MEMBLOCK_ALLOC_ANYWHERE or *       %MEMBLOCK_ALLOC_ACCESSIBLE * @nid: nid of the free area to find, %NUMA_NO_NODE for any node * @flags: pick from blocks based on memory attributes * * Find @size free area aligned to @align in the specified range and node. * * Return: * Found address on success, 0 on failure. */static phys_addr_t __init_memblock memblock_find_in_range_node(phys_addr_t size,					phys_addr_t align, phys_addr_t start,					phys_addr_t end, int nid,					enum memblock_flags flags){	/* pump up @end */	if (end == MEMBLOCK_ALLOC_ACCESSIBLE ||	    end == MEMBLOCK_ALLOC_NOLEAKTRACE)		end = memblock.current_limit; 	/* avoid allocating the first page */	start = max_t(phys_addr_t, start, PAGE_SIZE);	end = max(start, end); 	if (memblock_bottom_up())		return __memblock_find_range_bottom_up(start, end, size, align,						       nid, flags);	else		return __memblock_find_range_top_down(start, end, size, align,						      nid, flags);} /** * memblock_find_in_range - find free area in given range * @start: start of candidate range * @end: end of candidate range, can be %MEMBLOCK_ALLOC_ANYWHERE or *       %MEMBLOCK_ALLOC_ACCESSIBLE * @size: size of free area to find * @align: alignment of free area to find * * Find @size free area aligned to @align in the specified range. * * Return: * Found address on success, 0 on failure. */static phys_addr_t __init_memblock memblock_find_in_range(phys_addr_t start,					phys_addr_t end, phys_addr_t size,					phys_addr_t align){	phys_addr_t ret;	enum memblock_flags flags = choose_memblock_flags(); again:	ret = memblock_find_in_range_node(size, align, start, end,					    NUMA_NO_NODE, flags); 	if (!ret && (flags & MEMBLOCK_MIRROR)) {		pr_warn_ratelimited("Could not allocate %pap bytes of mirrored memory\n",			&size);		flags &= ~MEMBLOCK_MIRROR;		goto again;	} 	return ret;} static void __init_memblock memblock_remove_region(struct memblock_type *type, unsigned long r){	type->total_size -= type->regions[r].size;	memmove(&type->regions[r], &type->regions[r + 1],		(type->cnt - (r + 1)) * sizeof(type->regions[r]));	type->cnt--; 	/* Special case for empty arrays */	if (type->cnt == 0) {		WARN_ON(type->total_size != 0);		type->regions[0].base = 0;		type->regions[0].size = 0;		type->regions[0].flags = 0;		memblock_set_region_node(&type->regions[0], MAX_NUMNODES);	}} #ifndef CONFIG_ARCH_KEEP_MEMBLOCK/** * memblock_discard - discard memory and reserved arrays if they were allocated */void __init memblock_discard(void){	phys_addr_t size;	void *addr; 	if (memblock.reserved.regions != memblock_reserved_init_regions) {		addr = memblock.reserved.regions;		size = PAGE_ALIGN(sizeof(struct memblock_region) *				  memblock.reserved.max);		if (memblock_reserved_in_slab)			kfree(addr);		else			memblock_free(addr, size);	} 	if (memblock.memory.regions != memblock_memory_init_regions) {		addr = memblock.memory.regions;		size = PAGE_ALIGN(sizeof(struct memblock_region) *				  memblock.memory.max);		if (memblock_memory_in_slab)			kfree(addr);		else			memblock_free(addr, size);	} 	memblock_memory = NULL;}#endif /** * memblock_double_array - double the size of the memblock regions array * @type: memblock type of the regions array being doubled * @new_area_start: starting address of memory range to avoid overlap with * @new_area_size: size of memory range to avoid overlap with * * Double the size of the @type regions array. If memblock is being used to * allocate memory for a new reserved regions array and there is a previously * allocated memory range [@new_area_start, @new_area_start + @new_area_size] * waiting to be reserved, ensure the memory used by the new array does * not overlap. * * Return: * 0 on success, -1 on failure. */static int __init_memblock memblock_double_array(struct memblock_type *type,						phys_addr_t new_area_start,						phys_addr_t new_area_size){	struct memblock_region *new_array, *old_array;	phys_addr_t old_alloc_size, new_alloc_size;	phys_addr_t old_size, new_size, addr, new_end;	int use_slab = slab_is_available();	int *in_slab; 	/* We don't allow resizing until we know about the reserved regions	 * of memory that aren't suitable for allocation	 */	if (!memblock_can_resize)		panic("memblock: cannot resize %s array\n", type->name); 	/* Calculate new doubled size */	old_size = type->max * sizeof(struct memblock_region);	new_size = old_size << 1;	/*	 * We need to allocated new one align to PAGE_SIZE,	 *   so we can free them completely later.	 */	old_alloc_size = PAGE_ALIGN(old_size);	new_alloc_size = PAGE_ALIGN(new_size); 	/* Retrieve the slab flag */	if (type == &memblock.memory)		in_slab = &memblock_memory_in_slab;	else		in_slab = &memblock_reserved_in_slab; 	/* Try to find some space for it */	if (use_slab) {		new_array = kmalloc(new_size, GFP_KERNEL);		addr = new_array ? __pa(new_array) : 0;	} else {		/* only exclude range when trying to double reserved.regions */		if (type != &memblock.reserved)			new_area_start = new_area_size = 0; 		addr = memblock_find_in_range(new_area_start + new_area_size,						memblock.current_limit,						new_alloc_size, PAGE_SIZE);		if (!addr && new_area_size)			addr = memblock_find_in_range(0,				min(new_area_start, memblock.current_limit),				new_alloc_size, PAGE_SIZE); 		if (addr) {			/* The memory may not have been accepted, yet. */			accept_memory(addr, new_alloc_size); 			new_array = __va(addr);		} else {			new_array = NULL;		}	}	if (!addr) {		pr_err("memblock: Failed to double %s array from %ld to %ld entries !\n",		       type->name, type->max, type->max * 2);		return -1;	} 	new_end = addr + new_size - 1;	memblock_dbg("memblock: %s is doubled to %ld at [%pa-%pa]",			type->name, type->max * 2, &addr, &new_end); 	/*	 * 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.

Lines 1–500 of 2983.