aboutsummaryrefslogtreecommitdiffstats
path: root/arch/x86/mm
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2010-03-28 22:42:55 -0400
committerIngo Molnar <mingo@elte.hu>2010-03-29 12:55:33 -0400
commitc967da6a0ba837f762042e931d4afcf72045547c (patch)
tree3576d2f97e20f3d8d647ea7381f5f99d96220fa2 /arch/x86/mm
parent596b711ed6b5235f8545680ef38ace00f9898c32 (diff)
x86: Make sure free_init_pages() frees pages on page boundary
When CONFIG_NO_BOOTMEM=y, it could use memory more effiently, or in a more compact fashion. Example: Allocated new RAMDISK: 00ec2000 - 0248ce57 Move RAMDISK from 000000002ea04000 - 000000002ffcee56 to 00ec2000 - 0248ce56 The new RAMDISK's end is not page aligned. Last page could be shared with other users. When free_init_pages are called for initrd or .init, the page could be freed and we could corrupt other data. code segment in free_init_pages(): | for (; addr < end; addr += PAGE_SIZE) { | ClearPageReserved(virt_to_page(addr)); | init_page_count(virt_to_page(addr)); | memset((void *)(addr & ~(PAGE_SIZE-1)), | POISON_FREE_INITMEM, PAGE_SIZE); | free_page(addr); | totalram_pages++; | } last half page could be used as one whole free page. So page align the boundaries. -v2: make the original initramdisk to be aligned, according to Johannes, otherwise we have the chance to lose one page. we still need to keep initrd_end not aligned, otherwise it could confuse decompressor. -v3: change to WARN_ON instead, suggested by Johannes. -v4: use PAGE_ALIGN, suggested by Johannes. We may fix that macro name later to PAGE_ALIGN_UP, and PAGE_ALIGN_DOWN Add comments about assuming ramdisk start is aligned in relocate_initrd(), change to re get ramdisk_image instead of save it to make diff smaller. Add warning for wrong range, suggested by Johannes. -v6: remove one WARN() We need to align beginning in free_init_pages() do not copy more than ramdisk_size, noticed by Johannes Reported-by: Stanislaw Gruszka <sgruszka@redhat.com> Tested-by: Stanislaw Gruszka <sgruszka@redhat.com> Signed-off-by: Yinghai Lu <yinghai@kernel.org> Acked-by: Johannes Weiner <hannes@cmpxchg.org> Cc: David Miller <davem@davemloft.net> Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org> Cc: Linus Torvalds <torvalds@linux-foundation.org> LKML-Reference: <1269830604-26214-3-git-send-email-yinghai@kernel.org> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch/x86/mm')
-rw-r--r--arch/x86/mm/init.c32
1 files changed, 26 insertions, 6 deletions
diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
index e71c5cbc8f35..452ee5b8f309 100644
--- a/arch/x86/mm/init.c
+++ b/arch/x86/mm/init.c
@@ -331,11 +331,23 @@ int devmem_is_allowed(unsigned long pagenr)
331 331
332void free_init_pages(char *what, unsigned long begin, unsigned long end) 332void free_init_pages(char *what, unsigned long begin, unsigned long end)
333{ 333{
334 unsigned long addr = begin; 334 unsigned long addr;
335 unsigned long begin_aligned, end_aligned;
335 336
336 if (addr >= end) 337 /* Make sure boundaries are page aligned */
338 begin_aligned = PAGE_ALIGN(begin);
339 end_aligned = end & PAGE_MASK;
340
341 if (WARN_ON(begin_aligned != begin || end_aligned != end)) {
342 begin = begin_aligned;
343 end = end_aligned;
344 }
345
346 if (begin >= end)
337 return; 347 return;
338 348
349 addr = begin;
350
339 /* 351 /*
340 * If debugging page accesses then do not free this memory but 352 * If debugging page accesses then do not free this memory but
341 * mark them not present - any buggy init-section access will 353 * mark them not present - any buggy init-section access will
@@ -343,7 +355,7 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
343 */ 355 */
344#ifdef CONFIG_DEBUG_PAGEALLOC 356#ifdef CONFIG_DEBUG_PAGEALLOC
345 printk(KERN_INFO "debug: unmapping init memory %08lx..%08lx\n", 357 printk(KERN_INFO "debug: unmapping init memory %08lx..%08lx\n",
346 begin, PAGE_ALIGN(end)); 358 begin, end);
347 set_memory_np(begin, (end - begin) >> PAGE_SHIFT); 359 set_memory_np(begin, (end - begin) >> PAGE_SHIFT);
348#else 360#else
349 /* 361 /*
@@ -358,8 +370,7 @@ void free_init_pages(char *what, unsigned long begin, unsigned long end)
358 for (; addr < end; addr += PAGE_SIZE) { 370 for (; addr < end; addr += PAGE_SIZE) {
359 ClearPageReserved(virt_to_page(addr)); 371 ClearPageReserved(virt_to_page(addr));
360 init_page_count(virt_to_page(addr)); 372 init_page_count(virt_to_page(addr));
361 memset((void *)(addr & ~(PAGE_SIZE-1)), 373 memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE);
362 POISON_FREE_INITMEM, PAGE_SIZE);
363 free_page(addr); 374 free_page(addr);
364 totalram_pages++; 375 totalram_pages++;
365 } 376 }
@@ -376,6 +387,15 @@ void free_initmem(void)
376#ifdef CONFIG_BLK_DEV_INITRD 387#ifdef CONFIG_BLK_DEV_INITRD
377void free_initrd_mem(unsigned long start, unsigned long end) 388void free_initrd_mem(unsigned long start, unsigned long end)
378{ 389{
379 free_init_pages("initrd memory", start, end); 390 /*
391 * end could be not aligned, and We can not align that,
392 * decompresser could be confused by aligned initrd_end
393 * We already reserve the end partial page before in
394 * - i386_start_kernel()
395 * - x86_64_start_kernel()
396 * - relocate_initrd()
397 * So here We can do PAGE_ALIGN() safely to get partial page to be freed
398 */
399 free_init_pages("initrd memory", start, PAGE_ALIGN(end));
380} 400}
381#endif 401#endif