xref: /linux/lib/percpu_test.c (revision 9208c05f9fdfd927ea160b97dfef3c379049fff2)
1 // SPDX-License-Identifier: GPL-2.0-only
2 #include <linux/limits.h>
3 #include <linux/module.h>
4 
5 /* validate @native and @pcp counter values match @expected */
6 #define CHECK(native, pcp, expected)                                    \
7 	do {                                                            \
8 		WARN((native) != (expected),                            \
9 		     "raw %ld (0x%lx) != expected %lld (0x%llx)",	\
10 		     (native), (native),				\
11 		     (long long)(expected), (long long)(expected));	\
12 		WARN(__this_cpu_read(pcp) != (expected),                \
13 		     "pcp %ld (0x%lx) != expected %lld (0x%llx)",	\
14 		     __this_cpu_read(pcp), __this_cpu_read(pcp),	\
15 		     (long long)(expected), (long long)(expected));	\
16 	} while (0)
17 
18 static DEFINE_PER_CPU(long, long_counter);
19 static DEFINE_PER_CPU(unsigned long, ulong_counter);
20 
21 static int __init percpu_test_init(void)
22 {
23 	/*
24 	 * volatile prevents compiler from optimizing it uses, otherwise the
25 	 * +ul_one/-ul_one below would replace with inc/dec instructions.
26 	 */
27 	volatile unsigned int ui_one = 1;
28 	unsigned long long ull = 0;
29 	unsigned long ul = 0;
30 	long l = 0;
31 
32 	pr_info("percpu test start\n");
33 
34 	preempt_disable();
35 
36 	l += -1;
37 	__this_cpu_add(long_counter, -1);
38 	CHECK(l, long_counter, -1);
39 
40 	l += 1;
41 	__this_cpu_add(long_counter, 1);
42 	CHECK(l, long_counter, 0);
43 
44 	ul = 0;
45 	__this_cpu_write(ulong_counter, 0);
46 
47 	ul += 1UL;
48 	__this_cpu_add(ulong_counter, 1UL);
49 	CHECK(ul, ulong_counter, 1);
50 
51 	ul += -1UL;
52 	__this_cpu_add(ulong_counter, -1UL);
53 	CHECK(ul, ulong_counter, 0);
54 
55 	ul += -(unsigned long)1;
56 	__this_cpu_add(ulong_counter, -(unsigned long)1);
57 	CHECK(ul, ulong_counter, -1);
58 
59 	ul = 0;
60 	__this_cpu_write(ulong_counter, 0);
61 
62 	ul -= 1;
63 	__this_cpu_dec(ulong_counter);
64 	CHECK(ul, ulong_counter, -1);
65 	CHECK(ul, ulong_counter, ULONG_MAX);
66 
67 	l += -ui_one;
68 	__this_cpu_add(long_counter, -ui_one);
69 	CHECK(l, long_counter, 0xffffffff);
70 
71 	l += ui_one;
72 	__this_cpu_add(long_counter, ui_one);
73 	CHECK(l, long_counter, (long)0x100000000LL);
74 
75 
76 	l = 0;
77 	__this_cpu_write(long_counter, 0);
78 
79 	l -= ui_one;
80 	__this_cpu_sub(long_counter, ui_one);
81 	CHECK(l, long_counter, -1);
82 
83 	l = 0;
84 	__this_cpu_write(long_counter, 0);
85 
86 	l += ui_one;
87 	__this_cpu_add(long_counter, ui_one);
88 	CHECK(l, long_counter, 1);
89 
90 	l += -ui_one;
91 	__this_cpu_add(long_counter, -ui_one);
92 	CHECK(l, long_counter, (long)0x100000000LL);
93 
94 	l = 0;
95 	__this_cpu_write(long_counter, 0);
96 
97 	l -= ui_one;
98 	this_cpu_sub(long_counter, ui_one);
99 	CHECK(l, long_counter, -1);
100 	CHECK(l, long_counter, ULONG_MAX);
101 
102 	ul = 0;
103 	__this_cpu_write(ulong_counter, 0);
104 
105 	ul += ui_one;
106 	__this_cpu_add(ulong_counter, ui_one);
107 	CHECK(ul, ulong_counter, 1);
108 
109 	ul = 0;
110 	__this_cpu_write(ulong_counter, 0);
111 
112 	ul -= ui_one;
113 	__this_cpu_sub(ulong_counter, ui_one);
114 	CHECK(ul, ulong_counter, -1);
115 	CHECK(ul, ulong_counter, ULONG_MAX);
116 
117 	ul = ull = 0;
118 	__this_cpu_write(ulong_counter, 0);
119 
120 	ul = ull += UINT_MAX;
121 	__this_cpu_add(ulong_counter, ull);
122 	CHECK(ul, ulong_counter, UINT_MAX);
123 
124 	ul = 3;
125 	__this_cpu_write(ulong_counter, 3);
126 
127 	ul = this_cpu_sub_return(ulong_counter, ui_one);
128 	CHECK(ul, ulong_counter, 2);
129 
130 	ul = __this_cpu_sub_return(ulong_counter, ui_one);
131 	CHECK(ul, ulong_counter, 1);
132 
133 	preempt_enable();
134 
135 	pr_info("percpu test done\n");
136 	return -EAGAIN;  /* Fail will directly unload the module */
137 }
138 
139 static void __exit percpu_test_exit(void)
140 {
141 }
142 
143 module_init(percpu_test_init)
144 module_exit(percpu_test_exit)
145 
146 MODULE_LICENSE("GPL");
147 MODULE_AUTHOR("Greg Thelen");
148 MODULE_DESCRIPTION("percpu operations test");
149