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