18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Testsuite for atomic64_t functions 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright © 2010 Luca Barbieri 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/bug.h> 128c2ecf20Sopenharmony_ci#include <linux/kernel.h> 138c2ecf20Sopenharmony_ci#include <linux/atomic.h> 148c2ecf20Sopenharmony_ci#include <linux/module.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 178c2ecf20Sopenharmony_ci#include <asm/cpufeature.h> /* for boot_cpu_has below */ 188c2ecf20Sopenharmony_ci#endif 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define TEST(bit, op, c_op, val) \ 218c2ecf20Sopenharmony_cido { \ 228c2ecf20Sopenharmony_ci atomic##bit##_set(&v, v0); \ 238c2ecf20Sopenharmony_ci r = v0; \ 248c2ecf20Sopenharmony_ci atomic##bit##_##op(val, &v); \ 258c2ecf20Sopenharmony_ci r c_op val; \ 268c2ecf20Sopenharmony_ci WARN(atomic##bit##_read(&v) != r, "%Lx != %Lx\n", \ 278c2ecf20Sopenharmony_ci (unsigned long long)atomic##bit##_read(&v), \ 288c2ecf20Sopenharmony_ci (unsigned long long)r); \ 298c2ecf20Sopenharmony_ci} while (0) 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* 328c2ecf20Sopenharmony_ci * Test for a atomic operation family, 338c2ecf20Sopenharmony_ci * @test should be a macro accepting parameters (bit, op, ...) 348c2ecf20Sopenharmony_ci */ 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#define FAMILY_TEST(test, bit, op, args...) \ 378c2ecf20Sopenharmony_cido { \ 388c2ecf20Sopenharmony_ci test(bit, op, ##args); \ 398c2ecf20Sopenharmony_ci test(bit, op##_acquire, ##args); \ 408c2ecf20Sopenharmony_ci test(bit, op##_release, ##args); \ 418c2ecf20Sopenharmony_ci test(bit, op##_relaxed, ##args); \ 428c2ecf20Sopenharmony_ci} while (0) 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#define TEST_RETURN(bit, op, c_op, val) \ 458c2ecf20Sopenharmony_cido { \ 468c2ecf20Sopenharmony_ci atomic##bit##_set(&v, v0); \ 478c2ecf20Sopenharmony_ci r = v0; \ 488c2ecf20Sopenharmony_ci r c_op val; \ 498c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_##op(val, &v) != r); \ 508c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_read(&v) != r); \ 518c2ecf20Sopenharmony_ci} while (0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define TEST_FETCH(bit, op, c_op, val) \ 548c2ecf20Sopenharmony_cido { \ 558c2ecf20Sopenharmony_ci atomic##bit##_set(&v, v0); \ 568c2ecf20Sopenharmony_ci r = v0; \ 578c2ecf20Sopenharmony_ci r c_op val; \ 588c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_##op(val, &v) != v0); \ 598c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_read(&v) != r); \ 608c2ecf20Sopenharmony_ci} while (0) 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#define RETURN_FAMILY_TEST(bit, op, c_op, val) \ 638c2ecf20Sopenharmony_cido { \ 648c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_RETURN, bit, op, c_op, val); \ 658c2ecf20Sopenharmony_ci} while (0) 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci#define FETCH_FAMILY_TEST(bit, op, c_op, val) \ 688c2ecf20Sopenharmony_cido { \ 698c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_FETCH, bit, op, c_op, val); \ 708c2ecf20Sopenharmony_ci} while (0) 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci#define TEST_ARGS(bit, op, init, ret, expect, args...) \ 738c2ecf20Sopenharmony_cido { \ 748c2ecf20Sopenharmony_ci atomic##bit##_set(&v, init); \ 758c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_##op(&v, ##args) != ret); \ 768c2ecf20Sopenharmony_ci BUG_ON(atomic##bit##_read(&v) != expect); \ 778c2ecf20Sopenharmony_ci} while (0) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define XCHG_FAMILY_TEST(bit, init, new) \ 808c2ecf20Sopenharmony_cido { \ 818c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_ARGS, bit, xchg, init, init, new, new); \ 828c2ecf20Sopenharmony_ci} while (0) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci#define CMPXCHG_FAMILY_TEST(bit, init, new, wrong) \ 858c2ecf20Sopenharmony_cido { \ 868c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_ARGS, bit, cmpxchg, \ 878c2ecf20Sopenharmony_ci init, init, new, init, new); \ 888c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_ARGS, bit, cmpxchg, \ 898c2ecf20Sopenharmony_ci init, init, init, wrong, new); \ 908c2ecf20Sopenharmony_ci} while (0) 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci#define INC_RETURN_FAMILY_TEST(bit, i) \ 938c2ecf20Sopenharmony_cido { \ 948c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_ARGS, bit, inc_return, \ 958c2ecf20Sopenharmony_ci i, (i) + one, (i) + one); \ 968c2ecf20Sopenharmony_ci} while (0) 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci#define DEC_RETURN_FAMILY_TEST(bit, i) \ 998c2ecf20Sopenharmony_cido { \ 1008c2ecf20Sopenharmony_ci FAMILY_TEST(TEST_ARGS, bit, dec_return, \ 1018c2ecf20Sopenharmony_ci i, (i) - one, (i) - one); \ 1028c2ecf20Sopenharmony_ci} while (0) 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic __init void test_atomic(void) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int v0 = 0xaaa31337; 1078c2ecf20Sopenharmony_ci int v1 = 0xdeadbeef; 1088c2ecf20Sopenharmony_ci int onestwos = 0x11112222; 1098c2ecf20Sopenharmony_ci int one = 1; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci atomic_t v; 1128c2ecf20Sopenharmony_ci int r; 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci TEST(, add, +=, onestwos); 1158c2ecf20Sopenharmony_ci TEST(, add, +=, -one); 1168c2ecf20Sopenharmony_ci TEST(, sub, -=, onestwos); 1178c2ecf20Sopenharmony_ci TEST(, sub, -=, -one); 1188c2ecf20Sopenharmony_ci TEST(, or, |=, v1); 1198c2ecf20Sopenharmony_ci TEST(, and, &=, v1); 1208c2ecf20Sopenharmony_ci TEST(, xor, ^=, v1); 1218c2ecf20Sopenharmony_ci TEST(, andnot, &= ~, v1); 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(, add_return, +=, onestwos); 1248c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(, add_return, +=, -one); 1258c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(, sub_return, -=, onestwos); 1268c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(, sub_return, -=, -one); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_add, +=, onestwos); 1298c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_add, +=, -one); 1308c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_sub, -=, onestwos); 1318c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_sub, -=, -one); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_or, |=, v1); 1348c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_and, &=, v1); 1358c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_andnot, &= ~, v1); 1368c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(, fetch_xor, ^=, v1); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci INC_RETURN_FAMILY_TEST(, v0); 1398c2ecf20Sopenharmony_ci DEC_RETURN_FAMILY_TEST(, v0); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci XCHG_FAMILY_TEST(, v0, v1); 1428c2ecf20Sopenharmony_ci CMPXCHG_FAMILY_TEST(, v0, v1, onestwos); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci} 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci#define INIT(c) do { atomic64_set(&v, c); r = c; } while (0) 1478c2ecf20Sopenharmony_cistatic __init void test_atomic64(void) 1488c2ecf20Sopenharmony_ci{ 1498c2ecf20Sopenharmony_ci long long v0 = 0xaaa31337c001d00dLL; 1508c2ecf20Sopenharmony_ci long long v1 = 0xdeadbeefdeafcafeLL; 1518c2ecf20Sopenharmony_ci long long v2 = 0xfaceabadf00df001LL; 1528c2ecf20Sopenharmony_ci long long v3 = 0x8000000000000000LL; 1538c2ecf20Sopenharmony_ci long long onestwos = 0x1111111122222222LL; 1548c2ecf20Sopenharmony_ci long long one = 1LL; 1558c2ecf20Sopenharmony_ci int r_int; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci atomic64_t v = ATOMIC64_INIT(v0); 1588c2ecf20Sopenharmony_ci long long r = v0; 1598c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci atomic64_set(&v, v1); 1628c2ecf20Sopenharmony_ci r = v1; 1638c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 1648c2ecf20Sopenharmony_ci BUG_ON(atomic64_read(&v) != r); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci TEST(64, add, +=, onestwos); 1678c2ecf20Sopenharmony_ci TEST(64, add, +=, -one); 1688c2ecf20Sopenharmony_ci TEST(64, sub, -=, onestwos); 1698c2ecf20Sopenharmony_ci TEST(64, sub, -=, -one); 1708c2ecf20Sopenharmony_ci TEST(64, or, |=, v1); 1718c2ecf20Sopenharmony_ci TEST(64, and, &=, v1); 1728c2ecf20Sopenharmony_ci TEST(64, xor, ^=, v1); 1738c2ecf20Sopenharmony_ci TEST(64, andnot, &= ~, v1); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(64, add_return, +=, onestwos); 1768c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(64, add_return, +=, -one); 1778c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(64, sub_return, -=, onestwos); 1788c2ecf20Sopenharmony_ci RETURN_FAMILY_TEST(64, sub_return, -=, -one); 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_add, +=, onestwos); 1818c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_add, +=, -one); 1828c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_sub, -=, onestwos); 1838c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_sub, -=, -one); 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_or, |=, v1); 1868c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_and, &=, v1); 1878c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_andnot, &= ~, v1); 1888c2ecf20Sopenharmony_ci FETCH_FAMILY_TEST(64, fetch_xor, ^=, v1); 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_ci INIT(v0); 1918c2ecf20Sopenharmony_ci atomic64_inc(&v); 1928c2ecf20Sopenharmony_ci r += one; 1938c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci INIT(v0); 1968c2ecf20Sopenharmony_ci atomic64_dec(&v); 1978c2ecf20Sopenharmony_ci r -= one; 1988c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci INC_RETURN_FAMILY_TEST(64, v0); 2018c2ecf20Sopenharmony_ci DEC_RETURN_FAMILY_TEST(64, v0); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci XCHG_FAMILY_TEST(64, v0, v1); 2048c2ecf20Sopenharmony_ci CMPXCHG_FAMILY_TEST(64, v0, v1, v2); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci INIT(v0); 2078c2ecf20Sopenharmony_ci BUG_ON(atomic64_add_unless(&v, one, v0)); 2088c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci INIT(v0); 2118c2ecf20Sopenharmony_ci BUG_ON(!atomic64_add_unless(&v, one, v1)); 2128c2ecf20Sopenharmony_ci r += one; 2138c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci INIT(onestwos); 2168c2ecf20Sopenharmony_ci BUG_ON(atomic64_dec_if_positive(&v) != (onestwos - 1)); 2178c2ecf20Sopenharmony_ci r -= one; 2188c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci INIT(0); 2218c2ecf20Sopenharmony_ci BUG_ON(atomic64_dec_if_positive(&v) != -one); 2228c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci INIT(-one); 2258c2ecf20Sopenharmony_ci BUG_ON(atomic64_dec_if_positive(&v) != (-one - one)); 2268c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci INIT(onestwos); 2298c2ecf20Sopenharmony_ci BUG_ON(!atomic64_inc_not_zero(&v)); 2308c2ecf20Sopenharmony_ci r += one; 2318c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_ci INIT(0); 2348c2ecf20Sopenharmony_ci BUG_ON(atomic64_inc_not_zero(&v)); 2358c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci INIT(-one); 2388c2ecf20Sopenharmony_ci BUG_ON(!atomic64_inc_not_zero(&v)); 2398c2ecf20Sopenharmony_ci r += one; 2408c2ecf20Sopenharmony_ci BUG_ON(v.counter != r); 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci /* Confirm the return value fits in an int, even if the value doesn't */ 2438c2ecf20Sopenharmony_ci INIT(v3); 2448c2ecf20Sopenharmony_ci r_int = atomic64_inc_not_zero(&v); 2458c2ecf20Sopenharmony_ci BUG_ON(!r_int); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic __init int test_atomics_init(void) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci test_atomic(); 2518c2ecf20Sopenharmony_ci test_atomic64(); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci#ifdef CONFIG_X86 2548c2ecf20Sopenharmony_ci pr_info("passed for %s platform %s CX8 and %s SSE\n", 2558c2ecf20Sopenharmony_ci#ifdef CONFIG_X86_64 2568c2ecf20Sopenharmony_ci "x86-64", 2578c2ecf20Sopenharmony_ci#elif defined(CONFIG_X86_CMPXCHG64) 2588c2ecf20Sopenharmony_ci "i586+", 2598c2ecf20Sopenharmony_ci#else 2608c2ecf20Sopenharmony_ci "i386+", 2618c2ecf20Sopenharmony_ci#endif 2628c2ecf20Sopenharmony_ci boot_cpu_has(X86_FEATURE_CX8) ? "with" : "without", 2638c2ecf20Sopenharmony_ci boot_cpu_has(X86_FEATURE_XMM) ? "with" : "without"); 2648c2ecf20Sopenharmony_ci#else 2658c2ecf20Sopenharmony_ci pr_info("passed\n"); 2668c2ecf20Sopenharmony_ci#endif 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci return 0; 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic __exit void test_atomics_exit(void) {} 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cimodule_init(test_atomics_init); 2748c2ecf20Sopenharmony_cimodule_exit(test_atomics_exit); 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 277