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