summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2018-05-07 20:22:22 +0200
committerRichard Braun <rbraun@sceen.net>2018-05-07 20:47:05 +0200
commite4288bb457b8a480653df93d9d4039c2214cc15b (patch)
tree71dbc00a77515db5d4bda405db8e717302fdcdaa /test
parentcdca0795d42e2f0a3a2dd571b971062972f8af65 (diff)
test/test_atomic: new module
Diffstat (limited to 'test')
-rw-r--r--test/Kconfig15
-rw-r--r--test/Makefile1
-rw-r--r--test/test_atomic.c367
3 files changed, 383 insertions, 0 deletions
diff --git a/test/Kconfig b/test/Kconfig
index 80679ef9..3f1c3b69 100644
--- a/test/Kconfig
+++ b/test/Kconfig
@@ -9,6 +9,21 @@ if TEST_MODULE
choice
prompt "Select test module"
+config TEST_MODULE_ATOMIC
+ bool "atomic"
+
+if TEST_MODULE_ATOMIC
+
+config TEST_MODULE_ATOMIC_64
+ bool "Force 64-bit atomic operations"
+ default n
+ ---help---
+ Force atomic operations to apply to 64-bit integers, even on 32-bit
+ targets. Some targets may not support 64-bit atomic operations
+ at all.
+
+endif
+
config TEST_MODULE_BULLETIN
bool "bulletin"
diff --git a/test/Makefile b/test/Makefile
index c98d6fb4..cdce6130 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -1,3 +1,4 @@
+x15_SOURCES-$(CONFIG_TEST_MODULE_ATOMIC) += test/test_atomic.c
x15_SOURCES-$(CONFIG_TEST_MODULE_BULLETIN) += test/test_bulletin.c
x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX) += test/test_mutex.c
x15_SOURCES-$(CONFIG_TEST_MODULE_MUTEX_PI) += test/test_mutex_pi.c
diff --git a/test/test_atomic.c b/test/test_atomic.c
new file mode 100644
index 00000000..ba313753
--- /dev/null
+++ b/test/test_atomic.c
@@ -0,0 +1,367 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ *
+ * 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 <errno.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include <kern/atomic.h>
+#include <kern/error.h>
+#include <kern/init.h>
+#include <kern/log.h>
+#include <kern/macros.h>
+#include <test/test.h>
+
+#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");
+}