powerpc: Fix boot freeze on machine with empty memory node
I got a bug report about a distro kernel not booting on a particular
machine. It would freeze during boot:
> ...
> Could not find start_pfn for node 1
> [boot]0015 Setup Done
> Built 2 zonelists in Node order, mobility grouping on. Total pages: 123783
> Policy zone: DMA
> Kernel command line:
> [boot]0020 XICS Init
> [boot]0021 XICS Done
> PID hash table entries: 4096 (order: 12, 32768 bytes)
> clocksource: timebase mult[7d0000] shift[22] registered
> Console: colour dummy device 80x25
> console handover: boot [udbg0] -> real [hvc0]
> Dentry cache hash table entries: 1048576 (order: 7, 8388608 bytes)
> Inode-cache hash table entries: 524288 (order: 6, 4194304 bytes)
> freeing bootmem node 0
I've reproduced this on 2.6.27.7. It is caused by commit
8f64e1f2d1
("powerpc: Reserve in bootmem
lmb reserved regions that cross NUMA nodes").
The problem is that Jon took a loop which was (in pseudocode):
for_each_node(nid)
NODE_DATA(nid) = careful_alloc(nid);
setup_bootmem(nid);
reserve_node_bootmem(nid);
and broke it up into:
for_each_node(nid)
NODE_DATA(nid) = careful_alloc(nid);
setup_bootmem(nid);
for_each_node(nid)
reserve_node_bootmem(nid);
The issue comes in when the 'careful_alloc()' is called on a node with
no memory. It falls back to using bootmem from a previously-initialized
node. But, bootmem has not yet been reserved when Jon's patch is
applied. It gives back bogus memory (0xc000000000000000) and pukes
later in boot.
The following patch collapses the loop back together. It also breaks
the mark_reserved_regions_for_nid() code out into a function and adds
some comments. I think a huge part of introducing this bug is because
for loop was too long and hard to read.
The actual bug fix here is the:
+ if (end_pfn <= node->node_start_pfn ||
+ start_pfn >= node_end_pfn)
+ continue;
Signed-off-by: Dave Hansen <dave@linux.vnet.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
This commit is contained in:
parent
4b824de9b1
commit
4a6186696e
|
@ -865,72 +865,30 @@ static struct notifier_block __cpuinitdata ppc64_numa_nb = {
|
|||
.priority = 1 /* Must run before sched domains notifier. */
|
||||
};
|
||||
|
||||
void __init do_init_bootmem(void)
|
||||
static void mark_reserved_regions_for_nid(int nid)
|
||||
{
|
||||
int nid;
|
||||
unsigned int i;
|
||||
struct pglist_data *node = NODE_DATA(nid);
|
||||
int i;
|
||||
|
||||
min_low_pfn = 0;
|
||||
max_low_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
|
||||
max_pfn = max_low_pfn;
|
||||
|
||||
if (parse_numa_properties())
|
||||
setup_nonnuma();
|
||||
else
|
||||
dump_numa_memory_topology();
|
||||
|
||||
register_cpu_notifier(&ppc64_numa_nb);
|
||||
cpu_numa_callback(&ppc64_numa_nb, CPU_UP_PREPARE,
|
||||
(void *)(unsigned long)boot_cpuid);
|
||||
|
||||
for_each_online_node(nid) {
|
||||
unsigned long start_pfn, end_pfn;
|
||||
unsigned long bootmem_paddr;
|
||||
unsigned long bootmap_pages;
|
||||
|
||||
get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
|
||||
|
||||
/* Allocate the node structure node local if possible */
|
||||
NODE_DATA(nid) = careful_allocation(nid,
|
||||
sizeof(struct pglist_data),
|
||||
SMP_CACHE_BYTES, end_pfn);
|
||||
NODE_DATA(nid) = __va(NODE_DATA(nid));
|
||||
memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
|
||||
|
||||
dbg("node %d\n", nid);
|
||||
dbg("NODE_DATA() = %p\n", NODE_DATA(nid));
|
||||
|
||||
NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
|
||||
NODE_DATA(nid)->node_start_pfn = start_pfn;
|
||||
NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
|
||||
|
||||
if (NODE_DATA(nid)->node_spanned_pages == 0)
|
||||
continue;
|
||||
|
||||
dbg("start_paddr = %lx\n", start_pfn << PAGE_SHIFT);
|
||||
dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT);
|
||||
|
||||
bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
|
||||
bootmem_paddr = (unsigned long)careful_allocation(nid,
|
||||
bootmap_pages << PAGE_SHIFT,
|
||||
PAGE_SIZE, end_pfn);
|
||||
memset(__va(bootmem_paddr), 0, bootmap_pages << PAGE_SHIFT);
|
||||
|
||||
dbg("bootmap_paddr = %lx\n", bootmem_paddr);
|
||||
|
||||
init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT,
|
||||
start_pfn, end_pfn);
|
||||
|
||||
free_bootmem_with_active_regions(nid, end_pfn);
|
||||
}
|
||||
|
||||
/* Mark reserved regions */
|
||||
for (i = 0; i < lmb.reserved.cnt; i++) {
|
||||
unsigned long physbase = lmb.reserved.region[i].base;
|
||||
unsigned long size = lmb.reserved.region[i].size;
|
||||
unsigned long start_pfn = physbase >> PAGE_SHIFT;
|
||||
unsigned long end_pfn = ((physbase + size) >> PAGE_SHIFT);
|
||||
struct node_active_region node_ar;
|
||||
unsigned long node_end_pfn = node->node_start_pfn +
|
||||
node->node_spanned_pages;
|
||||
|
||||
/*
|
||||
* Check to make sure that this lmb.reserved area is
|
||||
* within the bounds of the node that we care about.
|
||||
* Checking the nid of the start and end points is not
|
||||
* sufficient because the reserved area could span the
|
||||
* entire node.
|
||||
*/
|
||||
if (end_pfn <= node->node_start_pfn ||
|
||||
start_pfn >= node_end_pfn)
|
||||
continue;
|
||||
|
||||
get_node_active_region(start_pfn, &node_ar);
|
||||
while (start_pfn < end_pfn &&
|
||||
|
@ -964,11 +922,81 @@ void __init do_init_bootmem(void)
|
|||
size = size - reserve_size;
|
||||
get_node_active_region(start_pfn, &node_ar);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
for_each_online_node(nid)
|
||||
|
||||
void __init do_init_bootmem(void)
|
||||
{
|
||||
int nid;
|
||||
unsigned int i;
|
||||
|
||||
min_low_pfn = 0;
|
||||
max_low_pfn = lmb_end_of_DRAM() >> PAGE_SHIFT;
|
||||
max_pfn = max_low_pfn;
|
||||
|
||||
if (parse_numa_properties())
|
||||
setup_nonnuma();
|
||||
else
|
||||
dump_numa_memory_topology();
|
||||
|
||||
register_cpu_notifier(&ppc64_numa_nb);
|
||||
cpu_numa_callback(&ppc64_numa_nb, CPU_UP_PREPARE,
|
||||
(void *)(unsigned long)boot_cpuid);
|
||||
|
||||
for_each_online_node(nid) {
|
||||
unsigned long start_pfn, end_pfn;
|
||||
unsigned long bootmem_paddr;
|
||||
unsigned long bootmap_pages;
|
||||
|
||||
get_pfn_range_for_nid(nid, &start_pfn, &end_pfn);
|
||||
|
||||
/*
|
||||
* Allocate the node structure node local if possible
|
||||
*
|
||||
* Be careful moving this around, as it relies on all
|
||||
* previous nodes' bootmem to be initialized and have
|
||||
* all reserved areas marked.
|
||||
*/
|
||||
NODE_DATA(nid) = careful_allocation(nid,
|
||||
sizeof(struct pglist_data),
|
||||
SMP_CACHE_BYTES, end_pfn);
|
||||
NODE_DATA(nid) = __va(NODE_DATA(nid));
|
||||
memset(NODE_DATA(nid), 0, sizeof(struct pglist_data));
|
||||
|
||||
dbg("node %d\n", nid);
|
||||
dbg("NODE_DATA() = %p\n", NODE_DATA(nid));
|
||||
|
||||
NODE_DATA(nid)->bdata = &bootmem_node_data[nid];
|
||||
NODE_DATA(nid)->node_start_pfn = start_pfn;
|
||||
NODE_DATA(nid)->node_spanned_pages = end_pfn - start_pfn;
|
||||
|
||||
if (NODE_DATA(nid)->node_spanned_pages == 0)
|
||||
continue;
|
||||
|
||||
dbg("start_paddr = %lx\n", start_pfn << PAGE_SHIFT);
|
||||
dbg("end_paddr = %lx\n", end_pfn << PAGE_SHIFT);
|
||||
|
||||
bootmap_pages = bootmem_bootmap_pages(end_pfn - start_pfn);
|
||||
bootmem_paddr = (unsigned long)careful_allocation(nid,
|
||||
bootmap_pages << PAGE_SHIFT,
|
||||
PAGE_SIZE, end_pfn);
|
||||
memset(__va(bootmem_paddr), 0, bootmap_pages << PAGE_SHIFT);
|
||||
|
||||
dbg("bootmap_paddr = %lx\n", bootmem_paddr);
|
||||
|
||||
init_bootmem_node(NODE_DATA(nid), bootmem_paddr >> PAGE_SHIFT,
|
||||
start_pfn, end_pfn);
|
||||
|
||||
free_bootmem_with_active_regions(nid, end_pfn);
|
||||
/*
|
||||
* Be very careful about moving this around. Future
|
||||
* calls to careful_allocation() depend on this getting
|
||||
* done correctly.
|
||||
*/
|
||||
mark_reserved_regions_for_nid(nid);
|
||||
sparse_memory_present_with_active_regions(nid);
|
||||
}
|
||||
}
|
||||
|
||||
void __init paging_init(void)
|
||||
|
|
Loading…
Reference in a new issue