diff options
Diffstat (limited to 'mm/sparse.c')
| -rw-r--r-- | mm/sparse.c | 290 | 
1 files changed, 104 insertions, 186 deletions
| diff --git a/mm/sparse.c b/mm/sparse.c index f13f2723950a..10b07eea9a6e 100644 --- a/mm/sparse.c +++ b/mm/sparse.c @@ -200,6 +200,11 @@ static inline int next_present_section_nr(int section_nr)  	      (section_nr <= __highest_present_section_nr));	\  	     section_nr = next_present_section_nr(section_nr)) +static inline unsigned long first_present_section_nr(void) +{ +	return next_present_section_nr(-1); +} +  /* Record a memory area against a node. */  void __init memory_present(int nid, unsigned long start, unsigned long end)  { @@ -257,19 +262,14 @@ struct page *sparse_decode_mem_map(unsigned long coded_mem_map, unsigned long pn  	return ((struct page *)coded_mem_map) + section_nr_to_pfn(pnum);  } -static int __meminit sparse_init_one_section(struct mem_section *ms, +static void __meminit sparse_init_one_section(struct mem_section *ms,  		unsigned long pnum, struct page *mem_map,  		unsigned long *pageblock_bitmap)  { -	if (!present_section(ms)) -		return -EINVAL; -  	ms->section_mem_map &= ~SECTION_MAP_MASK;  	ms->section_mem_map |= sparse_encode_mem_map(mem_map, pnum) |  							SECTION_HAS_MEM_MAP;   	ms->pageblock_flags = pageblock_bitmap; - -	return 1;  }  unsigned long usemap_size(void) @@ -370,160 +370,121 @@ static void __init check_usemap_section_nr(int nid, unsigned long *usemap)  }  #endif /* CONFIG_MEMORY_HOTREMOVE */ -static void __init sparse_early_usemaps_alloc_node(void *data, -				 unsigned long pnum_begin, -				 unsigned long pnum_end, -				 unsigned long usemap_count, int nodeid) +#ifdef CONFIG_SPARSEMEM_VMEMMAP +static unsigned long __init section_map_size(void)  { -	void *usemap; -	unsigned long pnum; -	unsigned long **usemap_map = (unsigned long **)data; -	int size = usemap_size(); - -	usemap = sparse_early_usemaps_alloc_pgdat_section(NODE_DATA(nodeid), -							  size * usemap_count); -	if (!usemap) { -		pr_warn("%s: allocation failed\n", __func__); -		return; -	} +	return ALIGN(sizeof(struct page) * PAGES_PER_SECTION, PMD_SIZE); +} -	for (pnum = pnum_begin; pnum < pnum_end; pnum++) { -		if (!present_section_nr(pnum)) -			continue; -		usemap_map[pnum] = usemap; -		usemap += size; -		check_usemap_section_nr(nodeid, usemap_map[pnum]); -	} +#else +static unsigned long __init section_map_size(void) +{ +	return PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION);  } -#ifndef CONFIG_SPARSEMEM_VMEMMAP  struct page __init *sparse_mem_map_populate(unsigned long pnum, int nid,  		struct vmem_altmap *altmap)  { -	struct page *map; -	unsigned long size; +	unsigned long size = section_map_size(); +	struct page *map = sparse_buffer_alloc(size); + +	if (map) +		return map; -	size = PAGE_ALIGN(sizeof(struct page) * PAGES_PER_SECTION);  	map = memblock_virt_alloc_try_nid(size,  					  PAGE_SIZE, __pa(MAX_DMA_ADDRESS),  					  BOOTMEM_ALLOC_ACCESSIBLE, nid);  	return map;  } -void __init sparse_mem_maps_populate_node(struct page **map_map, -					  unsigned long pnum_begin, -					  unsigned long pnum_end, -					  unsigned long map_count, int nodeid) -{ -	void *map; -	unsigned long pnum; -	unsigned long size = sizeof(struct page) * PAGES_PER_SECTION; - -	size = PAGE_ALIGN(size); -	map = memblock_virt_alloc_try_nid_raw(size * map_count, -					      PAGE_SIZE, __pa(MAX_DMA_ADDRESS), -					      BOOTMEM_ALLOC_ACCESSIBLE, nodeid); -	if (map) { -		for (pnum = pnum_begin; pnum < pnum_end; pnum++) { -			if (!present_section_nr(pnum)) -				continue; -			map_map[pnum] = map; -			map += size; -		} -		return; -	} +#endif /* !CONFIG_SPARSEMEM_VMEMMAP */ -	/* fallback */ -	for (pnum = pnum_begin; pnum < pnum_end; pnum++) { -		struct mem_section *ms; +static void *sparsemap_buf __meminitdata; +static void *sparsemap_buf_end __meminitdata; -		if (!present_section_nr(pnum)) -			continue; -		map_map[pnum] = sparse_mem_map_populate(pnum, nodeid, NULL); -		if (map_map[pnum]) -			continue; -		ms = __nr_to_section(pnum); -		pr_err("%s: sparsemem memory map backing failed some memory will not be available\n", -		       __func__); -		ms->section_mem_map = 0; -	} +static void __init sparse_buffer_init(unsigned long size, int nid) +{ +	WARN_ON(sparsemap_buf);	/* forgot to call sparse_buffer_fini()? */ +	sparsemap_buf = +		memblock_virt_alloc_try_nid_raw(size, PAGE_SIZE, +						__pa(MAX_DMA_ADDRESS), +						BOOTMEM_ALLOC_ACCESSIBLE, nid); +	sparsemap_buf_end = sparsemap_buf + size;  } -#endif /* !CONFIG_SPARSEMEM_VMEMMAP */ -#ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER -static void __init sparse_early_mem_maps_alloc_node(void *data, -				 unsigned long pnum_begin, -				 unsigned long pnum_end, -				 unsigned long map_count, int nodeid) +static void __init sparse_buffer_fini(void)  { -	struct page **map_map = (struct page **)data; -	sparse_mem_maps_populate_node(map_map, pnum_begin, pnum_end, -					 map_count, nodeid); +	unsigned long size = sparsemap_buf_end - sparsemap_buf; + +	if (sparsemap_buf && size > 0) +		memblock_free_early(__pa(sparsemap_buf), size); +	sparsemap_buf = NULL;  } -#else -static struct page __init *sparse_early_mem_map_alloc(unsigned long pnum) -{ -	struct page *map; -	struct mem_section *ms = __nr_to_section(pnum); -	int nid = sparse_early_nid(ms); -	map = sparse_mem_map_populate(pnum, nid, NULL); -	if (map) -		return map; +void * __meminit sparse_buffer_alloc(unsigned long size) +{ +	void *ptr = NULL; -	pr_err("%s: sparsemem memory map backing failed some memory will not be available\n", -	       __func__); -	ms->section_mem_map = 0; -	return NULL; +	if (sparsemap_buf) { +		ptr = PTR_ALIGN(sparsemap_buf, size); +		if (ptr + size > sparsemap_buf_end) +			ptr = NULL; +		else +			sparsemap_buf = ptr + size; +	} +	return ptr;  } -#endif  void __weak __meminit vmemmap_populate_print_last(void)  {  } -/** - *  alloc_usemap_and_memmap - memory alloction for pageblock flags and vmemmap - *  @map: usemap_map for pageblock flags or mmap_map for vmemmap +/* + * Initialize sparse on a specific node. The node spans [pnum_begin, pnum_end) + * And number of present sections in this node is map_count.   */ -static void __init alloc_usemap_and_memmap(void (*alloc_func) -					(void *, unsigned long, unsigned long, -					unsigned long, int), void *data) +static void __init sparse_init_nid(int nid, unsigned long pnum_begin, +				   unsigned long pnum_end, +				   unsigned long map_count)  { -	unsigned long pnum; -	unsigned long map_count; -	int nodeid_begin = 0; -	unsigned long pnum_begin = 0; - -	for_each_present_section_nr(0, pnum) { -		struct mem_section *ms; +	unsigned long pnum, usemap_longs, *usemap; +	struct page *map; -		ms = __nr_to_section(pnum); -		nodeid_begin = sparse_early_nid(ms); -		pnum_begin = pnum; -		break; +	usemap_longs = BITS_TO_LONGS(SECTION_BLOCKFLAGS_BITS); +	usemap = sparse_early_usemaps_alloc_pgdat_section(NODE_DATA(nid), +							  usemap_size() * +							  map_count); +	if (!usemap) { +		pr_err("%s: node[%d] usemap allocation failed", __func__, nid); +		goto failed; +	} +	sparse_buffer_init(map_count * section_map_size(), nid); +	for_each_present_section_nr(pnum_begin, pnum) { +		if (pnum >= pnum_end) +			break; + +		map = sparse_mem_map_populate(pnum, nid, NULL); +		if (!map) { +			pr_err("%s: node[%d] memory map backing failed. Some memory will not be available.", +			       __func__, nid); +			pnum_begin = pnum; +			goto failed; +		} +		check_usemap_section_nr(nid, usemap); +		sparse_init_one_section(__nr_to_section(pnum), pnum, map, usemap); +		usemap += usemap_longs;  	} -	map_count = 1; -	for_each_present_section_nr(pnum_begin + 1, pnum) { +	sparse_buffer_fini(); +	return; +failed: +	/* We failed to allocate, mark all the following pnums as not present */ +	for_each_present_section_nr(pnum_begin, pnum) {  		struct mem_section *ms; -		int nodeid; +		if (pnum >= pnum_end) +			break;  		ms = __nr_to_section(pnum); -		nodeid = sparse_early_nid(ms); -		if (nodeid == nodeid_begin) { -			map_count++; -			continue; -		} -		/* ok, we need to take cake of from pnum_begin to pnum - 1*/ -		alloc_func(data, pnum_begin, pnum, -						map_count, nodeid_begin); -		/* new start, update count etc*/ -		nodeid_begin = nodeid; -		pnum_begin = pnum; -		map_count = 1; +		ms->section_mem_map = 0;  	} -	/* ok, last chunk */ -	alloc_func(data, pnum_begin, __highest_present_section_nr+1, -						map_count, nodeid_begin);  }  /* @@ -532,72 +493,29 @@ static void __init alloc_usemap_and_memmap(void (*alloc_func)   */  void __init sparse_init(void)  { -	unsigned long pnum; -	struct page *map; -	unsigned long *usemap; -	unsigned long **usemap_map; -	int size; -#ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER -	int size2; -	struct page **map_map; -#endif - -	/* see include/linux/mmzone.h 'struct mem_section' definition */ -	BUILD_BUG_ON(!is_power_of_2(sizeof(struct mem_section))); +	unsigned long pnum_begin = first_present_section_nr(); +	int nid_begin = sparse_early_nid(__nr_to_section(pnum_begin)); +	unsigned long pnum_end, map_count = 1;  	/* Setup pageblock_order for HUGETLB_PAGE_SIZE_VARIABLE */  	set_pageblock_order(); -	/* -	 * map is using big page (aka 2M in x86 64 bit) -	 * usemap is less one page (aka 24 bytes) -	 * so alloc 2M (with 2M align) and 24 bytes in turn will -	 * make next 2M slip to one more 2M later. -	 * then in big system, the memory will have a lot of holes... -	 * here try to allocate 2M pages continuously. -	 * -	 * powerpc need to call sparse_init_one_section right after each -	 * sparse_early_mem_map_alloc, so allocate usemap_map at first. -	 */ -	size = sizeof(unsigned long *) * NR_MEM_SECTIONS; -	usemap_map = memblock_virt_alloc(size, 0); -	if (!usemap_map) -		panic("can not allocate usemap_map\n"); -	alloc_usemap_and_memmap(sparse_early_usemaps_alloc_node, -							(void *)usemap_map); - -#ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER -	size2 = sizeof(struct page *) * NR_MEM_SECTIONS; -	map_map = memblock_virt_alloc(size2, 0); -	if (!map_map) -		panic("can not allocate map_map\n"); -	alloc_usemap_and_memmap(sparse_early_mem_maps_alloc_node, -							(void *)map_map); -#endif - -	for_each_present_section_nr(0, pnum) { -		usemap = usemap_map[pnum]; -		if (!usemap) -			continue; +	for_each_present_section_nr(pnum_begin + 1, pnum_end) { +		int nid = sparse_early_nid(__nr_to_section(pnum_end)); -#ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER -		map = map_map[pnum]; -#else -		map = sparse_early_mem_map_alloc(pnum); -#endif -		if (!map) +		if (nid == nid_begin) { +			map_count++;  			continue; - -		sparse_init_one_section(__nr_to_section(pnum), pnum, map, -								usemap); +		} +		/* Init node with sections in range [pnum_begin, pnum_end) */ +		sparse_init_nid(nid_begin, pnum_begin, pnum_end, map_count); +		nid_begin = nid; +		pnum_begin = pnum_end; +		map_count = 1;  	} - +	/* cover the last node */ +	sparse_init_nid(nid_begin, pnum_begin, pnum_end, map_count);  	vmemmap_populate_print_last(); - -#ifdef CONFIG_SPARSEMEM_ALLOC_MEM_MAP_TOGETHER -	memblock_free_early(__pa(map_map), size2); -#endif -	memblock_free_early(__pa(usemap_map), size);  }  #ifdef CONFIG_MEMORY_HOTPLUG @@ -760,6 +678,7 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat,  	ret = sparse_index_init(section_nr, pgdat->node_id);  	if (ret < 0 && ret != -EEXIST)  		return ret; +	ret = 0;  	memmap = kmalloc_section_memmap(section_nr, pgdat->node_id, altmap);  	if (!memmap)  		return -ENOMEM; @@ -786,12 +705,11 @@ int __meminit sparse_add_one_section(struct pglist_data *pgdat,  #endif  	section_mark_present(ms); - -	ret = sparse_init_one_section(ms, section_nr, memmap, usemap); +	sparse_init_one_section(ms, section_nr, memmap, usemap);  out:  	pgdat_resize_unlock(pgdat, &flags); -	if (ret <= 0) { +	if (ret < 0) {  		kfree(usemap);  		__kfree_section_memmap(memmap, altmap);  	} |