| // SPDX-License-Identifier: GPL-2.0-only | |
| #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"); |