Skip to content

Commit b92df1d

Browse files
paulburtontorvalds
authored andcommitted
mm: page_alloc: skip over regions of invalid pfns where possible
When using a sparse memory model memmap_init_zone() when invoked with the MEMMAP_EARLY context will skip over pages which aren't valid - ie. which aren't in a populated region of the sparse memory map. However if the memory map is extremely sparse then it can spend a long time linearly checking each PFN in a large non-populated region of the memory map & skipping it in turn. When CONFIG_HAVE_MEMBLOCK_NODE_MAP is enabled, we have sufficient information to quickly discover the next valid PFN given an invalid one by searching through the list of memory regions & skipping forwards to the first PFN covered by the memory region to the right of the non-populated region. Implement this in order to speed up memmap_init_zone() for systems with extremely sparse memory maps. James said "I have tested this patch on a virtual model of a Samurai CPU with a sparse memory map. The kernel boot time drops from 109 to 62 seconds. " Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Paul Burton <[email protected]> Tested-by: James Hartley <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 7f354a5 commit b92df1d

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

include/linux/memblock.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ int memblock_search_pfn_nid(unsigned long pfn, unsigned long *start_pfn,
203203
unsigned long *end_pfn);
204204
void __next_mem_pfn_range(int *idx, int nid, unsigned long *out_start_pfn,
205205
unsigned long *out_end_pfn, int *out_nid);
206+
unsigned long memblock_next_valid_pfn(unsigned long pfn, unsigned long max_pfn);
206207

207208
/**
208209
* for_each_mem_pfn_range - early memory pfn range iterator

mm/memblock.c

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,6 +1105,31 @@ void __init_memblock __next_mem_pfn_range(int *idx, int nid,
11051105
*out_nid = r->nid;
11061106
}
11071107

1108+
unsigned long __init_memblock memblock_next_valid_pfn(unsigned long pfn,
1109+
unsigned long max_pfn)
1110+
{
1111+
struct memblock_type *type = &memblock.memory;
1112+
unsigned int right = type->cnt;
1113+
unsigned int mid, left = 0;
1114+
phys_addr_t addr = PFN_PHYS(pfn + 1);
1115+
1116+
do {
1117+
mid = (right + left) / 2;
1118+
1119+
if (addr < type->regions[mid].base)
1120+
right = mid;
1121+
else if (addr >= (type->regions[mid].base +
1122+
type->regions[mid].size))
1123+
left = mid + 1;
1124+
else {
1125+
/* addr is within the region, so pfn + 1 is valid */
1126+
return min(pfn + 1, max_pfn);
1127+
}
1128+
} while (left < right);
1129+
1130+
return min(PHYS_PFN(type->regions[right].base), max_pfn);
1131+
}
1132+
11081133
/**
11091134
* memblock_set_node - set node ID on memblock regions
11101135
* @base: base of area to set node ID for

mm/page_alloc.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5103,8 +5103,17 @@ void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,
51035103
if (context != MEMMAP_EARLY)
51045104
goto not_early;
51055105

5106-
if (!early_pfn_valid(pfn))
5106+
if (!early_pfn_valid(pfn)) {
5107+
#ifdef CONFIG_HAVE_MEMBLOCK_NODE_MAP
5108+
/*
5109+
* Skip to the pfn preceding the next valid one (or
5110+
* end_pfn), such that we hit a valid pfn (or end_pfn)
5111+
* on our next iteration of the loop.
5112+
*/
5113+
pfn = memblock_next_valid_pfn(pfn, end_pfn) - 1;
5114+
#endif
51075115
continue;
5116+
}
51085117
if (!early_pfn_in_nid(pfn, nid))
51095118
continue;
51105119
if (!update_defer_init(pgdat, pfn, end_pfn, &nr_initialised))

0 commit comments

Comments
 (0)