162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci#include <linux/module.h>
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci/* validate @native and @pcp counter values match @expected */
562306a36Sopenharmony_ci#define CHECK(native, pcp, expected)                                    \
662306a36Sopenharmony_ci	do {                                                            \
762306a36Sopenharmony_ci		WARN((native) != (expected),                            \
862306a36Sopenharmony_ci		     "raw %ld (0x%lx) != expected %lld (0x%llx)",	\
962306a36Sopenharmony_ci		     (native), (native),				\
1062306a36Sopenharmony_ci		     (long long)(expected), (long long)(expected));	\
1162306a36Sopenharmony_ci		WARN(__this_cpu_read(pcp) != (expected),                \
1262306a36Sopenharmony_ci		     "pcp %ld (0x%lx) != expected %lld (0x%llx)",	\
1362306a36Sopenharmony_ci		     __this_cpu_read(pcp), __this_cpu_read(pcp),	\
1462306a36Sopenharmony_ci		     (long long)(expected), (long long)(expected));	\
1562306a36Sopenharmony_ci	} while (0)
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_cistatic DEFINE_PER_CPU(long, long_counter);
1862306a36Sopenharmony_cistatic DEFINE_PER_CPU(unsigned long, ulong_counter);
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic int __init percpu_test_init(void)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	/*
2362306a36Sopenharmony_ci	 * volatile prevents compiler from optimizing it uses, otherwise the
2462306a36Sopenharmony_ci	 * +ul_one/-ul_one below would replace with inc/dec instructions.
2562306a36Sopenharmony_ci	 */
2662306a36Sopenharmony_ci	volatile unsigned int ui_one = 1;
2762306a36Sopenharmony_ci	long l = 0;
2862306a36Sopenharmony_ci	unsigned long ul = 0;
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	pr_info("percpu test start\n");
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	preempt_disable();
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	l += -1;
3562306a36Sopenharmony_ci	__this_cpu_add(long_counter, -1);
3662306a36Sopenharmony_ci	CHECK(l, long_counter, -1);
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci	l += 1;
3962306a36Sopenharmony_ci	__this_cpu_add(long_counter, 1);
4062306a36Sopenharmony_ci	CHECK(l, long_counter, 0);
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	ul = 0;
4362306a36Sopenharmony_ci	__this_cpu_write(ulong_counter, 0);
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	ul += 1UL;
4662306a36Sopenharmony_ci	__this_cpu_add(ulong_counter, 1UL);
4762306a36Sopenharmony_ci	CHECK(ul, ulong_counter, 1);
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci	ul += -1UL;
5062306a36Sopenharmony_ci	__this_cpu_add(ulong_counter, -1UL);
5162306a36Sopenharmony_ci	CHECK(ul, ulong_counter, 0);
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	ul += -(unsigned long)1;
5462306a36Sopenharmony_ci	__this_cpu_add(ulong_counter, -(unsigned long)1);
5562306a36Sopenharmony_ci	CHECK(ul, ulong_counter, -1);
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	ul = 0;
5862306a36Sopenharmony_ci	__this_cpu_write(ulong_counter, 0);
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	ul -= 1;
6162306a36Sopenharmony_ci	__this_cpu_dec(ulong_counter);
6262306a36Sopenharmony_ci	CHECK(ul, ulong_counter, -1);
6362306a36Sopenharmony_ci	CHECK(ul, ulong_counter, ULONG_MAX);
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	l += -ui_one;
6662306a36Sopenharmony_ci	__this_cpu_add(long_counter, -ui_one);
6762306a36Sopenharmony_ci	CHECK(l, long_counter, 0xffffffff);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	l += ui_one;
7062306a36Sopenharmony_ci	__this_cpu_add(long_counter, ui_one);
7162306a36Sopenharmony_ci	CHECK(l, long_counter, (long)0x100000000LL);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	l = 0;
7562306a36Sopenharmony_ci	__this_cpu_write(long_counter, 0);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	l -= ui_one;
7862306a36Sopenharmony_ci	__this_cpu_sub(long_counter, ui_one);
7962306a36Sopenharmony_ci	CHECK(l, long_counter, -1);
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	l = 0;
8262306a36Sopenharmony_ci	__this_cpu_write(long_counter, 0);
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	l += ui_one;
8562306a36Sopenharmony_ci	__this_cpu_add(long_counter, ui_one);
8662306a36Sopenharmony_ci	CHECK(l, long_counter, 1);
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	l += -ui_one;
8962306a36Sopenharmony_ci	__this_cpu_add(long_counter, -ui_one);
9062306a36Sopenharmony_ci	CHECK(l, long_counter, (long)0x100000000LL);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	l = 0;
9362306a36Sopenharmony_ci	__this_cpu_write(long_counter, 0);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	l -= ui_one;
9662306a36Sopenharmony_ci	this_cpu_sub(long_counter, ui_one);
9762306a36Sopenharmony_ci	CHECK(l, long_counter, -1);
9862306a36Sopenharmony_ci	CHECK(l, long_counter, ULONG_MAX);
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	ul = 0;
10162306a36Sopenharmony_ci	__this_cpu_write(ulong_counter, 0);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	ul += ui_one;
10462306a36Sopenharmony_ci	__this_cpu_add(ulong_counter, ui_one);
10562306a36Sopenharmony_ci	CHECK(ul, ulong_counter, 1);
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	ul = 0;
10862306a36Sopenharmony_ci	__this_cpu_write(ulong_counter, 0);
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	ul -= ui_one;
11162306a36Sopenharmony_ci	__this_cpu_sub(ulong_counter, ui_one);
11262306a36Sopenharmony_ci	CHECK(ul, ulong_counter, -1);
11362306a36Sopenharmony_ci	CHECK(ul, ulong_counter, ULONG_MAX);
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	ul = 3;
11662306a36Sopenharmony_ci	__this_cpu_write(ulong_counter, 3);
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	ul = this_cpu_sub_return(ulong_counter, ui_one);
11962306a36Sopenharmony_ci	CHECK(ul, ulong_counter, 2);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	ul = __this_cpu_sub_return(ulong_counter, ui_one);
12262306a36Sopenharmony_ci	CHECK(ul, ulong_counter, 1);
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_ci	preempt_enable();
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	pr_info("percpu test done\n");
12762306a36Sopenharmony_ci	return -EAGAIN;  /* Fail will directly unload the module */
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic void __exit percpu_test_exit(void)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_cimodule_init(percpu_test_init)
13562306a36Sopenharmony_cimodule_exit(percpu_test_exit)
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
13862306a36Sopenharmony_ciMODULE_AUTHOR("Greg Thelen");
13962306a36Sopenharmony_ciMODULE_DESCRIPTION("percpu operations test");
140