/*
* Copyright (c) 2018 Richard Braun.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*
* This test makes sure that atomic operations behave at least as their
* non-atomic counterpart. It doesn't actually test the atomicity of the
* operations, but rather helps check that the generated code matches
* expectations for the target configuration.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef CONFIG_TEST_MODULE_ATOMIC_64
typedef uint64_t test_int_t;
#else
typedef unsigned long test_int_t;
#endif
/*
* Don't make this variable private, so that the compiler may not assume
* that all accesses are known, which forces it to generate access
* instructions for it. This only works when building without LTO.
*/
test_int_t test_n;
/*
* Prevent these functions from being inlined in order to make the generated
* code easy to find and review.
*/
static __noinline test_int_t
test_atomic_load(test_int_t *n)
{
return atomic_load(n, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_store(test_int_t *n, test_int_t val)
{
atomic_store(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_cas(test_int_t *n, test_int_t oval, test_int_t nval)
{
return atomic_cas(n, oval, nval, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_swap(test_int_t *n, test_int_t val)
{
return atomic_swap(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_fetch_add(test_int_t *n, test_int_t val)
{
return atomic_fetch_add(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_fetch_sub(test_int_t *n, test_int_t val)
{
return atomic_fetch_sub(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_fetch_and(test_int_t *n, test_int_t val)
{
return atomic_fetch_and(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_fetch_or(test_int_t *n, test_int_t val)
{
return atomic_fetch_or(n, val, ATOMIC_RELAXED);
}
static __noinline test_int_t
test_atomic_fetch_xor(test_int_t *n, test_int_t val)
{
return atomic_fetch_xor(n, val, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_add(test_int_t *n, test_int_t val)
{
atomic_add(n, val, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_sub(test_int_t *n, test_int_t val)
{
atomic_sub(n, val, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_and(test_int_t *n, test_int_t val)
{
atomic_and(n, val, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_or(test_int_t *n, test_int_t val)
{
atomic_or(n, val, ATOMIC_RELAXED);
}
static __noinline void
test_atomic_xor(test_int_t *n, test_int_t val)
{
atomic_xor(n, val, ATOMIC_RELAXED);
}
static void
test_check_n(test_int_t val, const char *fn)
{
volatile test_int_t *ptr;
int error;
ptr = &test_n;
error = (val == *ptr) ? 0 : EINVAL;
error_check(error, fn);
}
static void
test_load(void)
{
test_int_t n;
n = test_atomic_load(&test_n);
test_check_n(n, __func__);
}
static void
test_store(void)
{
test_int_t val;
val = 0x123;
test_atomic_store(&test_n, val);
test_check_n(val, __func__);
}
static void
test_cas_match(void)
{
test_int_t prev, oval, nval;
int error;
oval = 0;
nval = 0x123;
test_n = oval;
prev = test_atomic_cas(&test_n, oval, nval);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(nval, __func__);
}
static void
test_cas_nomatch(void)
{
test_int_t prev, oval, nval;
int error;
oval = 0;
nval = 0x123;
test_n = oval;
prev = test_atomic_cas(&test_n, oval + 1, nval);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval, __func__);
}
static void
test_swap(void)
{
test_int_t prev, oval, nval;
int error;
oval = 0;
nval = 0x123;
test_n = oval;
prev = test_atomic_swap(&test_n, nval);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(nval, __func__);
}
static void
test_fetch_add(void)
{
test_int_t prev, oval, delta;
int error;
oval = 0x123;
delta = 0x456;
test_n = oval;
prev = test_atomic_fetch_add(&test_n, delta);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval + delta, __func__);
}
static void
test_fetch_sub(void)
{
test_int_t prev, oval, delta;
int error;
oval = 0x123;
delta = 0x456;
test_n = oval;
prev = test_atomic_fetch_sub(&test_n, delta);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval - delta, __func__);
}
static void
test_fetch_and(void)
{
test_int_t prev, oval, delta;
int error;
oval = 0x123;
delta = 0x456;
test_n = oval;
prev = test_atomic_fetch_and(&test_n, delta);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval & delta, __func__);
}
static void
test_fetch_or(void)
{
test_int_t prev, oval, delta;
int error;
oval = 0x123;
delta = 0x456;
test_n = oval;
prev = test_atomic_fetch_or(&test_n, delta);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval | delta, __func__);
}
static void
test_fetch_xor(void)
{
test_int_t prev, oval, delta;
int error;
oval = 0x123;
delta = 0x456;
test_n = oval;
prev = test_atomic_fetch_xor(&test_n, delta);
error = (prev == oval) ? 0 : EINVAL;
error_check(error, __func__);
test_check_n(oval ^ delta, __func__);
}
static void
test_add(void)
{
test_int_t oval, delta;
oval = 0x123;
delta = 0x456;
test_n = oval;
test_atomic_add(&test_n, delta);
test_check_n(oval + delta, __func__);
}
static void
test_sub(void)
{
test_int_t oval, delta;
oval = 0x123;
delta = 0x456;
test_n = oval;
test_atomic_sub(&test_n, delta);
test_check_n(oval - delta, __func__);
}
static void
test_and(void)
{
test_int_t oval, delta;
oval = 0x123;
delta = 0x456;
test_n = oval;
test_atomic_and(&test_n, delta);
test_check_n(oval & delta, __func__);
}
static void
test_or(void)
{
test_int_t oval, delta;
oval = 0x123;
delta = 0x456;
test_n = oval;
test_atomic_or(&test_n, delta);
test_check_n(oval | delta, __func__);
}
static void
test_xor(void)
{
test_int_t oval, delta;
oval = 0x123;
delta = 0x456;
test_n = oval;
test_atomic_xor(&test_n, delta);
test_check_n(oval ^ delta, __func__);
}
void __init
test_setup(void)
{
test_load();
test_store();
test_cas_match();
test_cas_nomatch();
test_swap();
test_fetch_add();
test_fetch_sub();
test_fetch_and();
test_fetch_or();
test_fetch_xor();
test_add();
test_sub();
test_and();
test_or();
test_xor();
log_info("test: done");
}