percpu: add test module for various percpu operations
authorGreg Thelen <gthelen@google.com>
Tue, 12 Nov 2013 23:08:34 +0000 (15:08 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 13 Nov 2013 03:09:11 +0000 (12:09 +0900)
Tests various percpu operations.

Enable with CONFIG_PERCPU_TEST=m.

Signed-off-by: Greg Thelen <gthelen@google.com>
Acked-by: Tejun Heo <tj@kernel.org>
Acked-by: Johannes Weiner <hannes@cmpxchg.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
lib/Kconfig.debug
lib/Makefile
lib/percpu_test.c [new file with mode: 0644]

index ebef88f61b7d81b1497cc2619e8d84a525b3471d..db25707aa41bc021b8174391091178b85258d466 100644 (file)
@@ -1481,6 +1481,15 @@ config INTERVAL_TREE_TEST
        help
          A benchmark measuring the performance of the interval tree library
 
+config PERCPU_TEST
+       tristate "Per cpu operations test"
+       depends on m && DEBUG_KERNEL
+       help
+         Enable this option to build test module which validates per-cpu
+         operations.
+
+         If unsure, say N.
+
 config ATOMIC64_SELFTEST
        bool "Perform an atomic64_t self-test at boot"
        help
index f3bb2cb98adfd2a631144ecbde775f6f224bea4e..bb016e116ba43286ed28ca7bf2667c081d752e81 100644 (file)
@@ -157,6 +157,8 @@ obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
 
 interval_tree_test-objs := interval_tree_test_main.o interval_tree.o
 
+obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
+
 obj-$(CONFIG_ASN1) += asn1_decoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
diff --git a/lib/percpu_test.c b/lib/percpu_test.c
new file mode 100644 (file)
index 0000000..0b5d14d
--- /dev/null
@@ -0,0 +1,138 @@
+#include <linux/module.h>
+
+/* validate @native and @pcp counter values match @expected */
+#define CHECK(native, pcp, expected)                                    \
+       do {                                                            \
+               WARN((native) != (expected),                            \
+                    "raw %ld (0x%lx) != expected %lld (0x%llx)",       \
+                    (native), (native),                                \
+                    (long long)(expected), (long long)(expected));     \
+               WARN(__this_cpu_read(pcp) != (expected),                \
+                    "pcp %ld (0x%lx) != expected %lld (0x%llx)",       \
+                    __this_cpu_read(pcp), __this_cpu_read(pcp),        \
+                    (long long)(expected), (long long)(expected));     \
+       } while (0)
+
+static DEFINE_PER_CPU(long, long_counter);
+static DEFINE_PER_CPU(unsigned long, ulong_counter);
+
+static int __init percpu_test_init(void)
+{
+       /*
+        * volatile prevents compiler from optimizing it uses, otherwise the
+        * +ul_one/-ul_one below would replace with inc/dec instructions.
+        */
+       volatile unsigned int ui_one = 1;
+       long l = 0;
+       unsigned long ul = 0;
+
+       pr_info("percpu test start\n");
+
+       preempt_disable();
+
+       l += -1;
+       __this_cpu_add(long_counter, -1);
+       CHECK(l, long_counter, -1);
+
+       l += 1;
+       __this_cpu_add(long_counter, 1);
+       CHECK(l, long_counter, 0);
+
+       ul = 0;
+       __this_cpu_write(ulong_counter, 0);
+
+       ul += 1UL;
+       __this_cpu_add(ulong_counter, 1UL);
+       CHECK(ul, ulong_counter, 1);
+
+       ul += -1UL;
+       __this_cpu_add(ulong_counter, -1UL);
+       CHECK(ul, ulong_counter, 0);
+
+       ul += -(unsigned long)1;
+       __this_cpu_add(ulong_counter, -(unsigned long)1);
+       CHECK(ul, ulong_counter, -1);
+
+       ul = 0;
+       __this_cpu_write(ulong_counter, 0);
+
+       ul -= 1;
+       __this_cpu_dec(ulong_counter);
+       CHECK(ul, ulong_counter, -1);
+       CHECK(ul, ulong_counter, ULONG_MAX);
+
+       l += -ui_one;
+       __this_cpu_add(long_counter, -ui_one);
+       CHECK(l, long_counter, 0xffffffff);
+
+       l += ui_one;
+       __this_cpu_add(long_counter, ui_one);
+       CHECK(l, long_counter, (long)0x100000000LL);
+
+
+       l = 0;
+       __this_cpu_write(long_counter, 0);
+
+       l -= ui_one;
+       __this_cpu_sub(long_counter, ui_one);
+       CHECK(l, long_counter, -1);
+
+       l = 0;
+       __this_cpu_write(long_counter, 0);
+
+       l += ui_one;
+       __this_cpu_add(long_counter, ui_one);
+       CHECK(l, long_counter, 1);
+
+       l += -ui_one;
+       __this_cpu_add(long_counter, -ui_one);
+       CHECK(l, long_counter, (long)0x100000000LL);
+
+       l = 0;
+       __this_cpu_write(long_counter, 0);
+
+       l -= ui_one;
+       this_cpu_sub(long_counter, ui_one);
+       CHECK(l, long_counter, -1);
+       CHECK(l, long_counter, ULONG_MAX);
+
+       ul = 0;
+       __this_cpu_write(ulong_counter, 0);
+
+       ul += ui_one;
+       __this_cpu_add(ulong_counter, ui_one);
+       CHECK(ul, ulong_counter, 1);
+
+       ul = 0;
+       __this_cpu_write(ulong_counter, 0);
+
+       ul -= ui_one;
+       __this_cpu_sub(ulong_counter, ui_one);
+       CHECK(ul, ulong_counter, -1);
+       CHECK(ul, ulong_counter, ULONG_MAX);
+
+       ul = 3;
+       __this_cpu_write(ulong_counter, 3);
+
+       ul = this_cpu_sub_return(ulong_counter, ui_one);
+       CHECK(ul, ulong_counter, 2);
+
+       ul = __this_cpu_sub_return(ulong_counter, ui_one);
+       CHECK(ul, ulong_counter, 1);
+
+       preempt_enable();
+
+       pr_info("percpu test done\n");
+       return -EAGAIN;  /* Fail will directly unload the module */
+}
+
+static void __exit percpu_test_exit(void)
+{
+}
+
+module_init(percpu_test_init)
+module_exit(percpu_test_exit)
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Thelen");
+MODULE_DESCRIPTION("percpu operations test");