diff options
| author | Ingo Molnar <mingo@elte.hu> | 2009-04-08 11:25:42 -0400 |
|---|---|---|
| committer | Ingo Molnar <mingo@elte.hu> | 2009-04-08 11:26:00 -0400 |
| commit | 5af8c4e0fac9838428bd718040b664043a05f37c (patch) | |
| tree | 75a01d98ed244db45fe3c734c4a81c1a3d92ac37 /mm/debug-pagealloc.c | |
| parent | 46e0bb9c12f4bab539736f1714cbf16600f681ec (diff) | |
| parent | 577c9c456f0e1371cbade38eaf91ae8e8a308555 (diff) | |
Merge commit 'v2.6.30-rc1' into sched/urgent
Merge reason: update to latest upstream to queue up fix
Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'mm/debug-pagealloc.c')
| -rw-r--r-- | mm/debug-pagealloc.c | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/mm/debug-pagealloc.c b/mm/debug-pagealloc.c new file mode 100644 index 000000000000..a1e3324de2b5 --- /dev/null +++ b/mm/debug-pagealloc.c | |||
| @@ -0,0 +1,129 @@ | |||
| 1 | #include <linux/kernel.h> | ||
| 2 | #include <linux/mm.h> | ||
| 3 | #include <linux/page-debug-flags.h> | ||
| 4 | #include <linux/poison.h> | ||
| 5 | |||
| 6 | static inline void set_page_poison(struct page *page) | ||
| 7 | { | ||
| 8 | __set_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
| 9 | } | ||
| 10 | |||
| 11 | static inline void clear_page_poison(struct page *page) | ||
| 12 | { | ||
| 13 | __clear_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
| 14 | } | ||
| 15 | |||
| 16 | static inline bool page_poison(struct page *page) | ||
| 17 | { | ||
| 18 | return test_bit(PAGE_DEBUG_FLAG_POISON, &page->debug_flags); | ||
| 19 | } | ||
| 20 | |||
| 21 | static void poison_highpage(struct page *page) | ||
| 22 | { | ||
| 23 | /* | ||
| 24 | * Page poisoning for highmem pages is not implemented. | ||
| 25 | * | ||
| 26 | * This can be called from interrupt contexts. | ||
| 27 | * So we need to create a new kmap_atomic slot for this | ||
| 28 | * application and it will need interrupt protection. | ||
| 29 | */ | ||
| 30 | } | ||
| 31 | |||
| 32 | static void poison_page(struct page *page) | ||
| 33 | { | ||
| 34 | void *addr; | ||
| 35 | |||
| 36 | if (PageHighMem(page)) { | ||
| 37 | poison_highpage(page); | ||
| 38 | return; | ||
| 39 | } | ||
| 40 | set_page_poison(page); | ||
| 41 | addr = page_address(page); | ||
| 42 | memset(addr, PAGE_POISON, PAGE_SIZE); | ||
| 43 | } | ||
| 44 | |||
| 45 | static void poison_pages(struct page *page, int n) | ||
| 46 | { | ||
| 47 | int i; | ||
| 48 | |||
| 49 | for (i = 0; i < n; i++) | ||
| 50 | poison_page(page + i); | ||
| 51 | } | ||
| 52 | |||
| 53 | static bool single_bit_flip(unsigned char a, unsigned char b) | ||
| 54 | { | ||
| 55 | unsigned char error = a ^ b; | ||
| 56 | |||
| 57 | return error && !(error & (error - 1)); | ||
| 58 | } | ||
| 59 | |||
| 60 | static void check_poison_mem(unsigned char *mem, size_t bytes) | ||
| 61 | { | ||
| 62 | unsigned char *start; | ||
| 63 | unsigned char *end; | ||
| 64 | |||
| 65 | for (start = mem; start < mem + bytes; start++) { | ||
| 66 | if (*start != PAGE_POISON) | ||
| 67 | break; | ||
| 68 | } | ||
| 69 | if (start == mem + bytes) | ||
| 70 | return; | ||
| 71 | |||
| 72 | for (end = mem + bytes - 1; end > start; end--) { | ||
| 73 | if (*end != PAGE_POISON) | ||
| 74 | break; | ||
| 75 | } | ||
| 76 | |||
| 77 | if (!printk_ratelimit()) | ||
| 78 | return; | ||
| 79 | else if (start == end && single_bit_flip(*start, PAGE_POISON)) | ||
| 80 | printk(KERN_ERR "pagealloc: single bit error\n"); | ||
| 81 | else | ||
| 82 | printk(KERN_ERR "pagealloc: memory corruption\n"); | ||
| 83 | |||
| 84 | print_hex_dump(KERN_ERR, "", DUMP_PREFIX_ADDRESS, 16, 1, start, | ||
| 85 | end - start + 1, 1); | ||
| 86 | dump_stack(); | ||
| 87 | } | ||
| 88 | |||
| 89 | static void unpoison_highpage(struct page *page) | ||
| 90 | { | ||
| 91 | /* | ||
| 92 | * See comment in poison_highpage(). | ||
| 93 | * Highmem pages should not be poisoned for now | ||
| 94 | */ | ||
| 95 | BUG_ON(page_poison(page)); | ||
| 96 | } | ||
| 97 | |||
| 98 | static void unpoison_page(struct page *page) | ||
| 99 | { | ||
| 100 | if (PageHighMem(page)) { | ||
| 101 | unpoison_highpage(page); | ||
| 102 | return; | ||
| 103 | } | ||
| 104 | if (page_poison(page)) { | ||
| 105 | void *addr = page_address(page); | ||
| 106 | |||
| 107 | check_poison_mem(addr, PAGE_SIZE); | ||
| 108 | clear_page_poison(page); | ||
| 109 | } | ||
| 110 | } | ||
| 111 | |||
| 112 | static void unpoison_pages(struct page *page, int n) | ||
| 113 | { | ||
| 114 | int i; | ||
| 115 | |||
| 116 | for (i = 0; i < n; i++) | ||
| 117 | unpoison_page(page + i); | ||
| 118 | } | ||
| 119 | |||
| 120 | void kernel_map_pages(struct page *page, int numpages, int enable) | ||
| 121 | { | ||
| 122 | if (!debug_pagealloc_enabled) | ||
| 123 | return; | ||
| 124 | |||
| 125 | if (enable) | ||
| 126 | unpoison_pages(page, numpages); | ||
| 127 | else | ||
| 128 | poison_pages(page, numpages); | ||
| 129 | } | ||
