diff options
author | Yinghai Lu <yinghai@kernel.org> | 2010-03-28 22:42:55 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2010-03-29 12:55:33 -0400 |
commit | c967da6a0ba837f762042e931d4afcf72045547c (patch) | |
tree | 3576d2f97e20f3d8d647ea7381f5f99d96220fa2 /arch/x86/mm | |
parent | 596b711ed6b5235f8545680ef38ace00f9898c32 (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.c | 32 |
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 | ||
332 | void free_init_pages(char *what, unsigned long begin, unsigned long end) | 332 | void 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 |
377 | void free_initrd_mem(unsigned long start, unsigned long end) | 388 | void 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 |