1*09c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2623fd807SGreg Thelen #include <linux/module.h>
3623fd807SGreg Thelen
4623fd807SGreg Thelen /* validate @native and @pcp counter values match @expected */
5623fd807SGreg Thelen #define CHECK(native, pcp, expected) \
6623fd807SGreg Thelen do { \
7623fd807SGreg Thelen WARN((native) != (expected), \
8623fd807SGreg Thelen "raw %ld (0x%lx) != expected %lld (0x%llx)", \
9623fd807SGreg Thelen (native), (native), \
10623fd807SGreg Thelen (long long)(expected), (long long)(expected)); \
11623fd807SGreg Thelen WARN(__this_cpu_read(pcp) != (expected), \
12623fd807SGreg Thelen "pcp %ld (0x%lx) != expected %lld (0x%llx)", \
13623fd807SGreg Thelen __this_cpu_read(pcp), __this_cpu_read(pcp), \
14623fd807SGreg Thelen (long long)(expected), (long long)(expected)); \
15623fd807SGreg Thelen } while (0)
16623fd807SGreg Thelen
17623fd807SGreg Thelen static DEFINE_PER_CPU(long, long_counter);
18623fd807SGreg Thelen static DEFINE_PER_CPU(unsigned long, ulong_counter);
19623fd807SGreg Thelen
percpu_test_init(void)20623fd807SGreg Thelen static int __init percpu_test_init(void)
21623fd807SGreg Thelen {
22623fd807SGreg Thelen /*
23623fd807SGreg Thelen * volatile prevents compiler from optimizing it uses, otherwise the
24623fd807SGreg Thelen * +ul_one/-ul_one below would replace with inc/dec instructions.
25623fd807SGreg Thelen */
26623fd807SGreg Thelen volatile unsigned int ui_one = 1;
27623fd807SGreg Thelen long l = 0;
28623fd807SGreg Thelen unsigned long ul = 0;
29623fd807SGreg Thelen
30623fd807SGreg Thelen pr_info("percpu test start\n");
31623fd807SGreg Thelen
32623fd807SGreg Thelen preempt_disable();
33623fd807SGreg Thelen
34623fd807SGreg Thelen l += -1;
35623fd807SGreg Thelen __this_cpu_add(long_counter, -1);
36623fd807SGreg Thelen CHECK(l, long_counter, -1);
37623fd807SGreg Thelen
38623fd807SGreg Thelen l += 1;
39623fd807SGreg Thelen __this_cpu_add(long_counter, 1);
40623fd807SGreg Thelen CHECK(l, long_counter, 0);
41623fd807SGreg Thelen
42623fd807SGreg Thelen ul = 0;
43623fd807SGreg Thelen __this_cpu_write(ulong_counter, 0);
44623fd807SGreg Thelen
45623fd807SGreg Thelen ul += 1UL;
46623fd807SGreg Thelen __this_cpu_add(ulong_counter, 1UL);
47623fd807SGreg Thelen CHECK(ul, ulong_counter, 1);
48623fd807SGreg Thelen
49623fd807SGreg Thelen ul += -1UL;
50623fd807SGreg Thelen __this_cpu_add(ulong_counter, -1UL);
51623fd807SGreg Thelen CHECK(ul, ulong_counter, 0);
52623fd807SGreg Thelen
53623fd807SGreg Thelen ul += -(unsigned long)1;
54623fd807SGreg Thelen __this_cpu_add(ulong_counter, -(unsigned long)1);
55623fd807SGreg Thelen CHECK(ul, ulong_counter, -1);
56623fd807SGreg Thelen
57623fd807SGreg Thelen ul = 0;
58623fd807SGreg Thelen __this_cpu_write(ulong_counter, 0);
59623fd807SGreg Thelen
60623fd807SGreg Thelen ul -= 1;
61623fd807SGreg Thelen __this_cpu_dec(ulong_counter);
62623fd807SGreg Thelen CHECK(ul, ulong_counter, -1);
63623fd807SGreg Thelen CHECK(ul, ulong_counter, ULONG_MAX);
64623fd807SGreg Thelen
65623fd807SGreg Thelen l += -ui_one;
66623fd807SGreg Thelen __this_cpu_add(long_counter, -ui_one);
67623fd807SGreg Thelen CHECK(l, long_counter, 0xffffffff);
68623fd807SGreg Thelen
69623fd807SGreg Thelen l += ui_one;
70623fd807SGreg Thelen __this_cpu_add(long_counter, ui_one);
71623fd807SGreg Thelen CHECK(l, long_counter, (long)0x100000000LL);
72623fd807SGreg Thelen
73623fd807SGreg Thelen
74623fd807SGreg Thelen l = 0;
75623fd807SGreg Thelen __this_cpu_write(long_counter, 0);
76623fd807SGreg Thelen
77623fd807SGreg Thelen l -= ui_one;
78623fd807SGreg Thelen __this_cpu_sub(long_counter, ui_one);
79623fd807SGreg Thelen CHECK(l, long_counter, -1);
80623fd807SGreg Thelen
81623fd807SGreg Thelen l = 0;
82623fd807SGreg Thelen __this_cpu_write(long_counter, 0);
83623fd807SGreg Thelen
84623fd807SGreg Thelen l += ui_one;
85623fd807SGreg Thelen __this_cpu_add(long_counter, ui_one);
86623fd807SGreg Thelen CHECK(l, long_counter, 1);
87623fd807SGreg Thelen
88623fd807SGreg Thelen l += -ui_one;
89623fd807SGreg Thelen __this_cpu_add(long_counter, -ui_one);
90623fd807SGreg Thelen CHECK(l, long_counter, (long)0x100000000LL);
91623fd807SGreg Thelen
92623fd807SGreg Thelen l = 0;
93623fd807SGreg Thelen __this_cpu_write(long_counter, 0);
94623fd807SGreg Thelen
95623fd807SGreg Thelen l -= ui_one;
96623fd807SGreg Thelen this_cpu_sub(long_counter, ui_one);
97623fd807SGreg Thelen CHECK(l, long_counter, -1);
98623fd807SGreg Thelen CHECK(l, long_counter, ULONG_MAX);
99623fd807SGreg Thelen
100623fd807SGreg Thelen ul = 0;
101623fd807SGreg Thelen __this_cpu_write(ulong_counter, 0);
102623fd807SGreg Thelen
103623fd807SGreg Thelen ul += ui_one;
104623fd807SGreg Thelen __this_cpu_add(ulong_counter, ui_one);
105623fd807SGreg Thelen CHECK(ul, ulong_counter, 1);
106623fd807SGreg Thelen
107623fd807SGreg Thelen ul = 0;
108623fd807SGreg Thelen __this_cpu_write(ulong_counter, 0);
109623fd807SGreg Thelen
110623fd807SGreg Thelen ul -= ui_one;
111623fd807SGreg Thelen __this_cpu_sub(ulong_counter, ui_one);
112623fd807SGreg Thelen CHECK(ul, ulong_counter, -1);
113623fd807SGreg Thelen CHECK(ul, ulong_counter, ULONG_MAX);
114623fd807SGreg Thelen
115623fd807SGreg Thelen ul = 3;
116623fd807SGreg Thelen __this_cpu_write(ulong_counter, 3);
117623fd807SGreg Thelen
118623fd807SGreg Thelen ul = this_cpu_sub_return(ulong_counter, ui_one);
119623fd807SGreg Thelen CHECK(ul, ulong_counter, 2);
120623fd807SGreg Thelen
121623fd807SGreg Thelen ul = __this_cpu_sub_return(ulong_counter, ui_one);
122623fd807SGreg Thelen CHECK(ul, ulong_counter, 1);
123623fd807SGreg Thelen
124623fd807SGreg Thelen preempt_enable();
125623fd807SGreg Thelen
126623fd807SGreg Thelen pr_info("percpu test done\n");
127623fd807SGreg Thelen return -EAGAIN; /* Fail will directly unload the module */
128623fd807SGreg Thelen }
129623fd807SGreg Thelen
percpu_test_exit(void)130623fd807SGreg Thelen static void __exit percpu_test_exit(void)
131623fd807SGreg Thelen {
132623fd807SGreg Thelen }
133623fd807SGreg Thelen
134623fd807SGreg Thelen module_init(percpu_test_init)
135623fd807SGreg Thelen module_exit(percpu_test_exit)
136623fd807SGreg Thelen
137623fd807SGreg Thelen MODULE_LICENSE("GPL");
138623fd807SGreg Thelen MODULE_AUTHOR("Greg Thelen");
139623fd807SGreg Thelen MODULE_DESCRIPTION("percpu operations test");
140