x86: delay CPA self-test and repeat it
authorIngo Molnar <mingo@elte.hu>
Wed, 6 Feb 2008 21:39:45 +0000 (22:39 +0100)
committerIngo Molnar <mingo@elte.hu>
Wed, 6 Feb 2008 21:39:45 +0000 (22:39 +0100)
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>
arch/x86/Kconfig.debug
arch/x86/mm/pageattr-test.c

index 2e1e3af28c3a2d5c455b048a330b720a74d1f8bf..fa555148823de98079ced8464fb31a28c25c4063 100644 (file)
@@ -220,9 +220,9 @@ config DEBUG_BOOT_PARAMS
          This option will cause struct boot_params to be exported via debugfs.
 
 config CPA_DEBUG
-       bool "CPA self test code"
+       bool "CPA self-test code"
        depends on DEBUG_KERNEL
        help
-         Do change_page_attr self tests at boot.
+         Do change_page_attr() self-tests every 30 seconds.
 
 endmenu
index 398f3a578dde446e292109a6463409f1b70d3415..ed82016003540ec66ccd7b18157188f6a9d7a5cf 100644 (file)
@@ -5,6 +5,7 @@
  * and compares page tables forwards and afterwards.
  */
 #include <linux/bootmem.h>
+#include <linux/kthread.h>
 #include <linux/random.h>
 #include <linux/kernel.h>
 #include <linux/init.h>
 #include <asm/pgtable.h>
 #include <asm/kdebug.h>
 
+/*
+ * Only print the results of the first pass:
+ */
+static __read_mostly int print = 1;
+
 enum {
-       NTEST                   = 4000,
+       NTEST                   = 400,
 #ifdef CONFIG_X86_64
        LPS                     = (1 << PMD_SHIFT),
 #elif defined(CONFIG_X86_PAE)
@@ -31,7 +37,7 @@ struct split_state {
        long min_exec, max_exec;
 };
 
-static __init int print_split(struct split_state *s)
+static int print_split(struct split_state *s)
 {
        long i, expected, missed = 0;
        int printed = 0;
@@ -82,10 +88,13 @@ static __init int print_split(struct split_state *s)
                                s->max_exec = addr;
                }
        }
-       printk(KERN_INFO
-               "CPA mapping 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n",
-               s->spg, s->lpg, s->gpg, s->exec,
-               s->min_exec != ~0UL ? s->min_exec : 0, s->max_exec, missed);
+       if (print) {
+               printk(KERN_INFO
+                       " 4k %lu large %lu gb %lu x %lu[%lx-%lx] miss %lu\n",
+                       s->spg, s->lpg, s->gpg, s->exec,
+                       s->min_exec != ~0UL ? s->min_exec : 0,
+                       s->max_exec, missed);
+       }
 
        expected = (s->gpg*GPS + s->lpg*LPS)/PAGE_SIZE + s->spg + missed;
        if (expected != i) {
@@ -96,11 +105,11 @@ static __init int print_split(struct split_state *s)
        return err;
 }
 
-static unsigned long __initdata addr[NTEST];
-static unsigned int __initdata len[NTEST];
+static unsigned long addr[NTEST];
+static unsigned int len[NTEST];
 
 /* Change the global bit on random pages in the direct mapping */
-static __init int exercise_pageattr(void)
+static int pageattr_test(void)
 {
        struct split_state sa, sb, sc;
        unsigned long *bm;
@@ -110,7 +119,8 @@ static __init int exercise_pageattr(void)
        int i, k;
        int err;
 
-       printk(KERN_INFO "CPA exercising pageattr\n");
+       if (print)
+               printk(KERN_INFO "CPA self-test:\n");
 
        bm = vmalloc((max_pfn_mapped + 7) / 8);
        if (!bm) {
@@ -186,7 +196,6 @@ static __init int exercise_pageattr(void)
 
        failed += print_split(&sb);
 
-       printk(KERN_INFO "CPA reverting everything\n");
        for (i = 0; i < NTEST; i++) {
                if (!addr[i])
                        continue;
@@ -214,12 +223,40 @@ static __init int exercise_pageattr(void)
        failed += print_split(&sc);
 
        if (failed) {
-               printk(KERN_ERR "CPA selftests NOT PASSED. Please report.\n");
+               printk(KERN_ERR "NOT PASSED. Please report.\n");
                WARN_ON(1);
+               return -EINVAL;
        } else {
-               printk(KERN_INFO "CPA selftests PASSED\n");
+               if (print)
+                       printk(KERN_INFO "ok.\n");
        }
 
        return 0;
 }
-module_init(exercise_pageattr);
+
+static int do_pageattr_test(void *__unused)
+{
+       while (!kthread_should_stop()) {
+               schedule_timeout_interruptible(HZ*30);
+               if (pageattr_test() < 0)
+                       break;
+               if (print)
+                       print--;
+       }
+       return 0;
+}
+
+static int start_pageattr_test(void)
+{
+       struct task_struct *p;
+
+       p = kthread_create(do_pageattr_test, NULL, "pageattr-test");
+       if (!IS_ERR(p))
+               wake_up_process(p);
+       else
+               WARN_ON(1);
+
+       return 0;
+}
+
+module_init(start_pageattr_test);