diff options
author | Ingo Molnar <mingo@elte.hu> | 2008-02-06 16:39:45 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@elte.hu> | 2008-02-06 16:39:45 -0500 |
commit | 971a52d66a3e87d4d2f5d3455e62680447cdb8e9 (patch) | |
tree | c55f87abe255ef85854ab36e7d081fd8e4d2aec1 | |
parent | f1fbabb312d657262322f4ce68b30a95f501945c (diff) |
x86: delay CPA self-test and repeat it
delay the CPA self-test so that any impact (corruption) of
user-space pagetables can be triggered. Repeat the test
every 30 seconds.
this would have prevented the bug fixed by 8cb2a7c1e95e472b5,
at its source.
Signed-off-by: Ingo Molnar <mingo@elte.hu>
-rw-r--r-- | arch/x86/Kconfig.debug | 4 | ||||
-rw-r--r-- | arch/x86/mm/pageattr-test.c | 65 |
2 files changed, 53 insertions, 16 deletions
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug index 2e1e3af28c3a..fa555148823d 100644 --- a/arch/x86/Kconfig.debug +++ b/arch/x86/Kconfig.debug | |||
@@ -220,9 +220,9 @@ config DEBUG_BOOT_PARAMS | |||
220 | This option will cause struct boot_params to be exported via debugfs. | 220 | This option will cause struct boot_params to be exported via debugfs. |
221 | 221 | ||
222 | config CPA_DEBUG | 222 | config CPA_DEBUG |
223 | bool "CPA self test code" | 223 | bool "CPA self-test code" |
224 | depends on DEBUG_KERNEL | 224 | depends on DEBUG_KERNEL |
225 | help | 225 | help |
226 | Do change_page_attr self tests at boot. | 226 | Do change_page_attr() self-tests every 30 seconds. |
227 | 227 | ||
228 | endmenu | 228 | endmenu |
diff --git a/arch/x86/mm/pageattr-test.c b/arch/x86/mm/pageattr-test.c index 398f3a578dde..ed8201600354 100644 --- a/arch/x86/mm/pageattr-test.c +++ b/arch/x86/mm/pageattr-test.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * and compares page tables forwards and afterwards. | 5 | * and compares page tables forwards and afterwards. |
6 | */ | 6 | */ |
7 | #include <linux/bootmem.h> | 7 | #include <linux/bootmem.h> |
8 | #include <linux/kthread.h> | ||
8 | #include <linux/random.h> | 9 | #include <linux/random.h> |
9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
10 | #include <linux/init.h> | 11 | #include <linux/init.h> |
@@ -14,8 +15,13 @@ | |||
14 | #include <asm/pgtable.h> | 15 | #include <asm/pgtable.h> |
15 | #include <asm/kdebug.h> | 16 | #include <asm/kdebug.h> |
16 | 17 | ||
18 | /* | ||
19 | * Only print the results of the first pass: | ||
20 | */ | ||
21 | static __read_mostly int print = 1; | ||
22 | |||
17 | enum { | 23 | enum { |
18 | NTEST = 4000, | 24 | NTEST = 400, |
19 | #ifdef CONFIG_X86_64 | 25 | #ifdef CONFIG_X86_64 |
20 | LPS = (1 << PMD_SHIFT), | 26 | LPS = (1 << PMD_SHIFT), |
21 | #elif defined(CONFIG_X86_PAE) | 27 | #elif defined(CONFIG_X86_PAE) |
@@ -31,7 +37,7 @@ struct split_state { | |||
31 | long min_exec, max_exec; | 37 | long min_exec, max_exec; |
32 | }; | 38 | }; |
33 | 39 | ||
34 | static __init int print_split(struct split_state *s) | 40 | static int print_split(struct split_state *s) |
35 | { | 41 | { |
36 | long i, expected, missed = 0; | 42 | long i, expected, missed = 0; |
37 | int printed = 0; | 43 | int printed = 0; |
@@ -82,10 +88,13 @@ static __init int print_split(struct split_state *s) | |||
82 | s->max_exec = addr; | 88 | s->max_exec = addr; |
83 | } | 89 | } |
84 | } | 90 | } |
85 | printk(KERN_INFO | 91 | if (print) { |
86 | "CPA mapping 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n", | 92 | printk(KERN_INFO |
87 | s->spg, s->lpg, s->gpg, s->exec, | 93 | " 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n", |
88 | s->min_exec != ~0UL ? s->min_exec : 0, s->max_exec, missed); | 94 | s->spg, s->lpg, s->gpg, s->exec, |
95 | s->min_exec != ~0UL ? s->min_exec : 0, | ||
96 | s->max_exec, missed); | ||
97 | } | ||
89 | 98 | ||
90 | expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed; | 99 | expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed; |
91 | if (expected != i) { | 100 | if (expected != i) { |
@@ -96,11 +105,11 @@ static __init int print_split(struct split_state *s) | |||
96 | return err; | 105 | return err; |
97 | } | 106 | } |
98 | 107 | ||
99 | static unsigned long __initdata addr[NTEST]; | 108 | static unsigned long addr[NTEST]; |
100 | static unsigned int __initdata len[NTEST]; | 109 | static unsigned int len[NTEST]; |
101 | 110 | ||
102 | /* Change the global bit on random pages in the direct mapping */ | 111 | /* Change the global bit on random pages in the direct mapping */ |
103 | static __init int exercise_pageattr(void) | 112 | static int pageattr_test(void) |
104 | { | 113 | { |
105 | struct split_state sa, sb, sc; | 114 | struct split_state sa, sb, sc; |
106 | unsigned long *bm; | 115 | unsigned long *bm; |
@@ -110,7 +119,8 @@ static __init int exercise_pageattr(void) | |||
110 | int i, k; | 119 | int i, k; |
111 | int err; | 120 | int err; |
112 | 121 | ||
113 | printk(KERN_INFO "CPA exercising pageattr\n"); | 122 | if (print) |
123 | printk(KERN_INFO "CPA self-test:\n"); | ||
114 | 124 | ||
115 | bm = vmalloc((max_pfn_mapped + 7) / 8); | 125 | bm = vmalloc((max_pfn_mapped + 7) / 8); |
116 | if (!bm) { | 126 | if (!bm) { |
@@ -186,7 +196,6 @@ static __init int exercise_pageattr(void) | |||
186 | 196 | ||
187 | failed += print_split(&sb); | 197 | failed += print_split(&sb); |
188 | 198 | ||
189 | printk(KERN_INFO "CPA reverting everything\n"); | ||
190 | for (i = 0; i < NTEST; i++) { | 199 | for (i = 0; i < NTEST; i++) { |
191 | if (!addr[i]) | 200 | if (!addr[i]) |
192 | continue; | 201 | continue; |
@@ -214,12 +223,40 @@ static __init int exercise_pageattr(void) | |||
214 | failed += print_split(&sc); | 223 | failed += print_split(&sc); |
215 | 224 | ||
216 | if (failed) { | 225 | if (failed) { |
217 | printk(KERN_ERR "CPA selftests NOT PASSED. Please report.\n"); | 226 | printk(KERN_ERR "NOT PASSED. Please report.\n"); |
218 | WARN_ON(1); | 227 | WARN_ON(1); |
228 | return -EINVAL; | ||
219 | } else { | 229 | } else { |
220 | printk(KERN_INFO "CPA selftests PASSED\n"); | 230 | if (print) |
231 | printk(KERN_INFO "ok.\n"); | ||
221 | } | 232 | } |
222 | 233 | ||
223 | return 0; | 234 | return 0; |
224 | } | 235 | } |
225 | module_init(exercise_pageattr); | 236 | |
237 | static int do_pageattr_test(void *__unused) | ||
238 | { | ||
239 | while (!kthread_should_stop()) { | ||
240 | schedule_timeout_interruptible(HZ*30); | ||
241 | if (pageattr_test() < 0) | ||
242 | break; | ||
243 | if (print) | ||
244 | print--; | ||
245 | } | ||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static int start_pageattr_test(void) | ||
250 | { | ||
251 | struct task_struct *p; | ||
252 | |||
253 | p = kthread_create(do_pageattr_test, NULL, "pageattr-test"); | ||
254 | if (!IS_ERR(p)) | ||
255 | wake_up_process(p); | ||
256 | else | ||
257 | WARN_ON(1); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | module_init(start_pageattr_test); | ||