summaryrefslogtreecommitdiffstats
path: root/mm/page_alloc.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-02-18 12:58:02 -0500
committerLinus Torvalds <torvalds@linux-foundation.org>2013-02-18 12:58:02 -0500
commit7c45512df987c5619db041b5c9b80d281e26d3db (patch)
tree8687da177b111914b165bb723cea20e299731191 /mm/page_alloc.c
parentf741656d646f7a5b56ba86765205b954237e7956 (diff)
mm: fix pageblock bitmap allocation
Commit c060f943d092 ("mm: use aligned zone start for pfn_to_bitidx calculation") fixed out calculation of the index into the pageblock bitmap when a !SPARSEMEM zome was not aligned to pageblock_nr_pages. However, the _allocation_ of that bitmap had never taken this alignment requirement into accout, so depending on the exact size and alignment of the zone, the use of that index could then access past the allocation, resulting in some very subtle memory corruption. This was reported (and bisected) by Ingo Molnar: one of his random config builds would hang with certain very specific kernel command line options. In the meantime, commit c060f943d092 has been marked for stable, so this fix needs to be back-ported to the stable kernels that backported the commit to use the right alignment. Bisected-and-tested-by: Ingo Molnar <mingo@kernel.org> Acked-by: Mel Gorman <mgorman@suse.de> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'mm/page_alloc.c')
-rw-r--r--mm/page_alloc.c15
1 files changed, 9 insertions, 6 deletions
diff --git a/mm/page_alloc.c b/mm/page_alloc.c
index 9673d96b1ba7..6a83cd35cfde 100644
--- a/mm/page_alloc.c
+++ b/mm/page_alloc.c
@@ -4420,10 +4420,11 @@ static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
4420 * round what is now in bits to nearest long in bits, then return it in 4420 * round what is now in bits to nearest long in bits, then return it in
4421 * bytes. 4421 * bytes.
4422 */ 4422 */
4423static unsigned long __init usemap_size(unsigned long zonesize) 4423static unsigned long __init usemap_size(unsigned long zone_start_pfn, unsigned long zonesize)
4424{ 4424{
4425 unsigned long usemapsize; 4425 unsigned long usemapsize;
4426 4426
4427 zonesize += zone_start_pfn & (pageblock_nr_pages-1);
4427 usemapsize = roundup(zonesize, pageblock_nr_pages); 4428 usemapsize = roundup(zonesize, pageblock_nr_pages);
4428 usemapsize = usemapsize >> pageblock_order; 4429 usemapsize = usemapsize >> pageblock_order;
4429 usemapsize *= NR_PAGEBLOCK_BITS; 4430 usemapsize *= NR_PAGEBLOCK_BITS;
@@ -4433,17 +4434,19 @@ static unsigned long __init usemap_size(unsigned long zonesize)
4433} 4434}
4434 4435
4435static void __init setup_usemap(struct pglist_data *pgdat, 4436static void __init setup_usemap(struct pglist_data *pgdat,
4436 struct zone *zone, unsigned long zonesize) 4437 struct zone *zone,
4438 unsigned long zone_start_pfn,
4439 unsigned long zonesize)
4437{ 4440{
4438 unsigned long usemapsize = usemap_size(zonesize); 4441 unsigned long usemapsize = usemap_size(zone_start_pfn, zonesize);
4439 zone->pageblock_flags = NULL; 4442 zone->pageblock_flags = NULL;
4440 if (usemapsize) 4443 if (usemapsize)
4441 zone->pageblock_flags = alloc_bootmem_node_nopanic(pgdat, 4444 zone->pageblock_flags = alloc_bootmem_node_nopanic(pgdat,
4442 usemapsize); 4445 usemapsize);
4443} 4446}
4444#else 4447#else
4445static inline void setup_usemap(struct pglist_data *pgdat, 4448static inline void setup_usemap(struct pglist_data *pgdat, struct zone *zone,
4446 struct zone *zone, unsigned long zonesize) {} 4449 unsigned long zone_start_pfn, unsigned long zonesize) {}
4447#endif /* CONFIG_SPARSEMEM */ 4450#endif /* CONFIG_SPARSEMEM */
4448 4451
4449#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE 4452#ifdef CONFIG_HUGETLB_PAGE_SIZE_VARIABLE
@@ -4594,7 +4597,7 @@ static void __paginginit free_area_init_core(struct pglist_data *pgdat,
4594 continue; 4597 continue;
4595 4598
4596 set_pageblock_order(); 4599 set_pageblock_order();
4597 setup_usemap(pgdat, zone, size); 4600 setup_usemap(pgdat, zone, zone_start_pfn, size);
4598 ret = init_currently_empty_zone(zone, zone_start_pfn, 4601 ret = init_currently_empty_zone(zone, zone_start_pfn,
4599 size, MEMMAP_EARLY); 4602 size, MEMMAP_EARLY);
4600 BUG_ON(ret); 4603 BUG_ON(ret);