Commit c967da6a0ba837f762042e931d4afcf72045547c
Committed by
Ingo Molnar
1 parent
596b711ed6
Exists in
master
and in
39 other branches
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>
Showing 4 changed files with 37 additions and 12 deletions Side-by-side Diff
arch/x86/kernel/head32.c
... | ... | @@ -7,6 +7,7 @@ |
7 | 7 | |
8 | 8 | #include <linux/init.h> |
9 | 9 | #include <linux/start_kernel.h> |
10 | +#include <linux/mm.h> | |
10 | 11 | |
11 | 12 | #include <asm/setup.h> |
12 | 13 | #include <asm/sections.h> |
13 | 14 | |
... | ... | @@ -44,9 +45,10 @@ |
44 | 45 | #ifdef CONFIG_BLK_DEV_INITRD |
45 | 46 | /* Reserve INITRD */ |
46 | 47 | if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) { |
48 | + /* Assume only end is not page aligned */ | |
47 | 49 | u64 ramdisk_image = boot_params.hdr.ramdisk_image; |
48 | 50 | u64 ramdisk_size = boot_params.hdr.ramdisk_size; |
49 | - u64 ramdisk_end = ramdisk_image + ramdisk_size; | |
51 | + u64 ramdisk_end = PAGE_ALIGN(ramdisk_image + ramdisk_size); | |
50 | 52 | reserve_early(ramdisk_image, ramdisk_end, "RAMDISK"); |
51 | 53 | } |
52 | 54 | #endif |
arch/x86/kernel/head64.c
... | ... | @@ -103,9 +103,10 @@ |
103 | 103 | #ifdef CONFIG_BLK_DEV_INITRD |
104 | 104 | /* Reserve INITRD */ |
105 | 105 | if (boot_params.hdr.type_of_loader && boot_params.hdr.ramdisk_image) { |
106 | + /* Assume only end is not page aligned */ | |
106 | 107 | unsigned long ramdisk_image = boot_params.hdr.ramdisk_image; |
107 | 108 | unsigned long ramdisk_size = boot_params.hdr.ramdisk_size; |
108 | - unsigned long ramdisk_end = ramdisk_image + ramdisk_size; | |
109 | + unsigned long ramdisk_end = PAGE_ALIGN(ramdisk_image + ramdisk_size); | |
109 | 110 | reserve_early(ramdisk_image, ramdisk_end, "RAMDISK"); |
110 | 111 | } |
111 | 112 | #endif |
arch/x86/kernel/setup.c
... | ... | @@ -314,16 +314,17 @@ |
314 | 314 | #define MAX_MAP_CHUNK (NR_FIX_BTMAPS << PAGE_SHIFT) |
315 | 315 | static void __init relocate_initrd(void) |
316 | 316 | { |
317 | - | |
317 | + /* Assume only end is not page aligned */ | |
318 | 318 | u64 ramdisk_image = boot_params.hdr.ramdisk_image; |
319 | 319 | u64 ramdisk_size = boot_params.hdr.ramdisk_size; |
320 | + u64 area_size = PAGE_ALIGN(ramdisk_size); | |
320 | 321 | u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT; |
321 | 322 | u64 ramdisk_here; |
322 | 323 | unsigned long slop, clen, mapaddr; |
323 | 324 | char *p, *q; |
324 | 325 | |
325 | 326 | /* We need to move the initrd down into lowmem */ |
326 | - ramdisk_here = find_e820_area(0, end_of_lowmem, ramdisk_size, | |
327 | + ramdisk_here = find_e820_area(0, end_of_lowmem, area_size, | |
327 | 328 | PAGE_SIZE); |
328 | 329 | |
329 | 330 | if (ramdisk_here == -1ULL) |
... | ... | @@ -332,7 +333,7 @@ |
332 | 333 | |
333 | 334 | /* Note: this includes all the lowmem currently occupied by |
334 | 335 | the initrd, we rely on that fact to keep the data intact. */ |
335 | - reserve_early(ramdisk_here, ramdisk_here + ramdisk_size, | |
336 | + reserve_early(ramdisk_here, ramdisk_here + area_size, | |
336 | 337 | "NEW RAMDISK"); |
337 | 338 | initrd_start = ramdisk_here + PAGE_OFFSET; |
338 | 339 | initrd_end = initrd_start + ramdisk_size; |
339 | 340 | |
... | ... | @@ -376,9 +377,10 @@ |
376 | 377 | |
377 | 378 | static void __init reserve_initrd(void) |
378 | 379 | { |
380 | + /* Assume only end is not page aligned */ | |
379 | 381 | u64 ramdisk_image = boot_params.hdr.ramdisk_image; |
380 | 382 | u64 ramdisk_size = boot_params.hdr.ramdisk_size; |
381 | - u64 ramdisk_end = ramdisk_image + ramdisk_size; | |
383 | + u64 ramdisk_end = PAGE_ALIGN(ramdisk_image + ramdisk_size); | |
382 | 384 | u64 end_of_lowmem = max_low_pfn_mapped << PAGE_SHIFT; |
383 | 385 | |
384 | 386 | if (!boot_params.hdr.type_of_loader || |
arch/x86/mm/init.c
... | ... | @@ -331,11 +331,23 @@ |
331 | 331 | |
332 | 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 | 347 | return; |
338 | 348 | |
349 | + addr = begin; | |
350 | + | |
339 | 351 | /* |
340 | 352 | * If debugging page accesses then do not free this memory but |
341 | 353 | * mark them not present - any buggy init-section access will |
... | ... | @@ -343,7 +355,7 @@ |
343 | 355 | */ |
344 | 356 | #ifdef CONFIG_DEBUG_PAGEALLOC |
345 | 357 | printk(KERN_INFO "debug: unmapping init memory %08lx..%08lx\n", |
346 | - begin, PAGE_ALIGN(end)); | |
358 | + begin, end); | |
347 | 359 | set_memory_np(begin, (end - begin) >> PAGE_SHIFT); |
348 | 360 | #else |
349 | 361 | /* |
... | ... | @@ -358,8 +370,7 @@ |
358 | 370 | for (; addr < end; addr += PAGE_SIZE) { |
359 | 371 | ClearPageReserved(virt_to_page(addr)); |
360 | 372 | init_page_count(virt_to_page(addr)); |
361 | - memset((void *)(addr & ~(PAGE_SIZE-1)), | |
362 | - POISON_FREE_INITMEM, PAGE_SIZE); | |
373 | + memset((void *)addr, POISON_FREE_INITMEM, PAGE_SIZE); | |
363 | 374 | free_page(addr); |
364 | 375 | totalram_pages++; |
365 | 376 | } |
... | ... | @@ -376,7 +387,16 @@ |
376 | 387 | #ifdef CONFIG_BLK_DEV_INITRD |
377 | 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 | 401 | #endif |