xref: /linux/lib/atomic64_test.c (revision fbc872c38c8fed31948c85683b5326ee5ab9fccc)
1 /*
2  * Testsuite for atomic64_t functions
3  *
4  * Copyright © 2010  Luca Barbieri
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  */
11 
12 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
13 
14 #include <linux/init.h>
15 #include <linux/bug.h>
16 #include <linux/kernel.h>
17 #include <linux/atomic.h>
18 
19 #ifdef CONFIG_X86
20 #include <asm/cpufeature.h>	/* for boot_cpu_has below */
21 #endif
22 
23 #define TEST(bit, op, c_op, val)				\
24 do {								\
25 	atomic##bit##_set(&v, v0);				\
26 	r = v0;							\
27 	atomic##bit##_##op(val, &v);				\
28 	r c_op val;						\
29 	WARN(atomic##bit##_read(&v) != r, "%Lx != %Lx\n",	\
30 		(unsigned long long)atomic##bit##_read(&v),	\
31 		(unsigned long long)r);				\
32 } while (0)
33 
34 /*
35  * Test for a atomic operation family,
36  * @test should be a macro accepting parameters (bit, op, ...)
37  */
38 
39 #define FAMILY_TEST(test, bit, op, args...)	\
40 do {						\
41 	test(bit, op, ##args);		\
42 	test(bit, op##_acquire, ##args);	\
43 	test(bit, op##_release, ##args);	\
44 	test(bit, op##_relaxed, ##args);	\
45 } while (0)
46 
47 #define TEST_RETURN(bit, op, c_op, val)				\
48 do {								\
49 	atomic##bit##_set(&v, v0);				\
50 	r = v0;							\
51 	r c_op val;						\
52 	BUG_ON(atomic##bit##_##op(val, &v) != r);		\
53 	BUG_ON(atomic##bit##_read(&v) != r);			\
54 } while (0)
55 
56 #define RETURN_FAMILY_TEST(bit, op, c_op, val)			\
57 do {								\
58 	FAMILY_TEST(TEST_RETURN, bit, op, c_op, val);		\
59 } while (0)
60 
61 #define TEST_ARGS(bit, op, init, ret, expect, args...)		\
62 do {								\
63 	atomic##bit##_set(&v, init);				\
64 	BUG_ON(atomic##bit##_##op(&v, ##args) != ret);		\
65 	BUG_ON(atomic##bit##_read(&v) != expect);		\
66 } while (0)
67 
68 #define XCHG_FAMILY_TEST(bit, init, new)				\
69 do {									\
70 	FAMILY_TEST(TEST_ARGS, bit, xchg, init, init, new, new);	\
71 } while (0)
72 
73 #define CMPXCHG_FAMILY_TEST(bit, init, new, wrong)			\
74 do {									\
75 	FAMILY_TEST(TEST_ARGS, bit, cmpxchg, 				\
76 			init, init, new, init, new);			\
77 	FAMILY_TEST(TEST_ARGS, bit, cmpxchg,				\
78 			init, init, init, wrong, new);			\
79 } while (0)
80 
81 #define INC_RETURN_FAMILY_TEST(bit, i)			\
82 do {							\
83 	FAMILY_TEST(TEST_ARGS, bit, inc_return,		\
84 			i, (i) + one, (i) + one);	\
85 } while (0)
86 
87 #define DEC_RETURN_FAMILY_TEST(bit, i)			\
88 do {							\
89 	FAMILY_TEST(TEST_ARGS, bit, dec_return,		\
90 			i, (i) - one, (i) - one);	\
91 } while (0)
92 
93 static __init void test_atomic(void)
94 {
95 	int v0 = 0xaaa31337;
96 	int v1 = 0xdeadbeef;
97 	int onestwos = 0x11112222;
98 	int one = 1;
99 
100 	atomic_t v;
101 	int r;
102 
103 	TEST(, add, +=, onestwos);
104 	TEST(, add, +=, -one);
105 	TEST(, sub, -=, onestwos);
106 	TEST(, sub, -=, -one);
107 	TEST(, or, |=, v1);
108 	TEST(, and, &=, v1);
109 	TEST(, xor, ^=, v1);
110 	TEST(, andnot, &= ~, v1);
111 
112 	RETURN_FAMILY_TEST(, add_return, +=, onestwos);
113 	RETURN_FAMILY_TEST(, add_return, +=, -one);
114 	RETURN_FAMILY_TEST(, sub_return, -=, onestwos);
115 	RETURN_FAMILY_TEST(, sub_return, -=, -one);
116 
117 	INC_RETURN_FAMILY_TEST(, v0);
118 	DEC_RETURN_FAMILY_TEST(, v0);
119 
120 	XCHG_FAMILY_TEST(, v0, v1);
121 	CMPXCHG_FAMILY_TEST(, v0, v1, onestwos);
122 
123 }
124 
125 #define INIT(c) do { atomic64_set(&v, c); r = c; } while (0)
126 static __init void test_atomic64(void)
127 {
128 	long long v0 = 0xaaa31337c001d00dLL;
129 	long long v1 = 0xdeadbeefdeafcafeLL;
130 	long long v2 = 0xfaceabadf00df001LL;
131 	long long onestwos = 0x1111111122222222LL;
132 	long long one = 1LL;
133 
134 	atomic64_t v = ATOMIC64_INIT(v0);
135 	long long r = v0;
136 	BUG_ON(v.counter != r);
137 
138 	atomic64_set(&v, v1);
139 	r = v1;
140 	BUG_ON(v.counter != r);
141 	BUG_ON(atomic64_read(&v) != r);
142 
143 	TEST(64, add, +=, onestwos);
144 	TEST(64, add, +=, -one);
145 	TEST(64, sub, -=, onestwos);
146 	TEST(64, sub, -=, -one);
147 	TEST(64, or, |=, v1);
148 	TEST(64, and, &=, v1);
149 	TEST(64, xor, ^=, v1);
150 	TEST(64, andnot, &= ~, v1);
151 
152 	RETURN_FAMILY_TEST(64, add_return, +=, onestwos);
153 	RETURN_FAMILY_TEST(64, add_return, +=, -one);
154 	RETURN_FAMILY_TEST(64, sub_return, -=, onestwos);
155 	RETURN_FAMILY_TEST(64, sub_return, -=, -one);
156 
157 	INIT(v0);
158 	atomic64_inc(&v);
159 	r += one;
160 	BUG_ON(v.counter != r);
161 
162 	INIT(v0);
163 	atomic64_dec(&v);
164 	r -= one;
165 	BUG_ON(v.counter != r);
166 
167 	INC_RETURN_FAMILY_TEST(64, v0);
168 	DEC_RETURN_FAMILY_TEST(64, v0);
169 
170 	XCHG_FAMILY_TEST(64, v0, v1);
171 	CMPXCHG_FAMILY_TEST(64, v0, v1, v2);
172 
173 	INIT(v0);
174 	BUG_ON(atomic64_add_unless(&v, one, v0));
175 	BUG_ON(v.counter != r);
176 
177 	INIT(v0);
178 	BUG_ON(!atomic64_add_unless(&v, one, v1));
179 	r += one;
180 	BUG_ON(v.counter != r);
181 
182 #ifdef CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE
183 	INIT(onestwos);
184 	BUG_ON(atomic64_dec_if_positive(&v) != (onestwos - 1));
185 	r -= one;
186 	BUG_ON(v.counter != r);
187 
188 	INIT(0);
189 	BUG_ON(atomic64_dec_if_positive(&v) != -one);
190 	BUG_ON(v.counter != r);
191 
192 	INIT(-one);
193 	BUG_ON(atomic64_dec_if_positive(&v) != (-one - one));
194 	BUG_ON(v.counter != r);
195 #else
196 #warning Please implement atomic64_dec_if_positive for your architecture and select the above Kconfig symbol
197 #endif
198 
199 	INIT(onestwos);
200 	BUG_ON(!atomic64_inc_not_zero(&v));
201 	r += one;
202 	BUG_ON(v.counter != r);
203 
204 	INIT(0);
205 	BUG_ON(atomic64_inc_not_zero(&v));
206 	BUG_ON(v.counter != r);
207 
208 	INIT(-one);
209 	BUG_ON(!atomic64_inc_not_zero(&v));
210 	r += one;
211 	BUG_ON(v.counter != r);
212 }
213 
214 static __init int test_atomics(void)
215 {
216 	test_atomic();
217 	test_atomic64();
218 
219 #ifdef CONFIG_X86
220 	pr_info("passed for %s platform %s CX8 and %s SSE\n",
221 #ifdef CONFIG_X86_64
222 		"x86-64",
223 #elif defined(CONFIG_X86_CMPXCHG64)
224 		"i586+",
225 #else
226 		"i386+",
227 #endif
228 	       boot_cpu_has(X86_FEATURE_CX8) ? "with" : "without",
229 	       boot_cpu_has(X86_FEATURE_XMM) ? "with" : "without");
230 #else
231 	pr_info("passed\n");
232 #endif
233 
234 	return 0;
235 }
236 
237 core_initcall(test_atomics);
238