Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ARM: 6890/1: memmap: only free allocated memmap entries when using SPARSEMEM

The SPARSEMEM code allocates memmap entries only for sections which are
present (i.e. those which contain some valid memory). The membank checks
in free_unused_memmap do not take this into account and can incorrectly
attempt to free memory which is not allocated, resulting in a BUG() in
the bootmem code.

However, if memory is configured as follows:

|<----section---->|<----hole---->|<----section---->|
+--------+--------+--------------+--------+--------+
| bank 0 | unused | | bank 1 | unused |
+--------+--------+--------------+--------+--------+

where a bank only occupies part of a section, the memmap allocated for
the remainder of the section *can* be freed.

This patch modifies the checks in free_unused_memmap so that only valid
memmap entries are considered for removal.

Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Will Deacon <will.deacon@arm.com>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>

authored by

Will Deacon and committed by
Russell King
9af386c8 362607df

+15 -1
+15 -1
arch/arm/mm/init.c
··· 392 392 * Convert start_pfn/end_pfn to a struct page pointer. 393 393 */ 394 394 start_pg = pfn_to_page(start_pfn - 1) + 1; 395 - end_pg = pfn_to_page(end_pfn); 395 + end_pg = pfn_to_page(end_pfn - 1) + 1; 396 396 397 397 /* 398 398 * Convert to physical addresses, and ··· 426 426 427 427 bank_start = bank_pfn_start(bank); 428 428 429 + #ifdef CONFIG_SPARSEMEM 430 + /* 431 + * Take care not to free memmap entries that don't exist 432 + * due to SPARSEMEM sections which aren't present. 433 + */ 434 + bank_start = min(bank_start, 435 + ALIGN(prev_bank_end, PAGES_PER_SECTION)); 436 + #endif 429 437 /* 430 438 * If we had a previous bank, and there is a space 431 439 * between the current bank and the previous, free it. ··· 448 440 */ 449 441 prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES); 450 442 } 443 + 444 + #ifdef CONFIG_SPARSEMEM 445 + if (!IS_ALIGNED(prev_bank_end, PAGES_PER_SECTION)) 446 + free_memmap(prev_bank_end, 447 + ALIGN(prev_bank_end, PAGES_PER_SECTION)); 448 + #endif 451 449 } 452 450 453 451 static void __init free_highpages(void)