summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRichard Braun <rbraun@sceen.net>2017-08-29 00:48:11 +0200
committerRichard Braun <rbraun@sceen.net>2017-08-31 00:43:27 +0200
commita9719450e83c2c64eecd097d82beb624948e9de9 (patch)
tree7096534cc5d5402be2027895b917fa2a94c29cd8
parent08a8ebf87c58e7a247bae1ec1ece187103473f72 (diff)
test/test_mutex: new module
-rw-r--r--Makefrag.am4
-rw-r--r--configure.ac3
-rw-r--r--test/test_mutex.c182
3 files changed, 189 insertions, 0 deletions
diff --git a/Makefrag.am b/Makefrag.am
index f29e5926..d799bb86 100644
--- a/Makefrag.am
+++ b/Makefrag.am
@@ -158,6 +158,10 @@ if TEST_LLSYNC_DEFER
x15_SOURCES += test/test_llsync_defer.c
endif TEST_LLSYNC_DEFER
+if TEST_MUTEX
+x15_SOURCES += test/test_mutex.c
+endif TEST_MUTEX
+
if TEST_MUTEX_PI
x15_SOURCES += test/test_mutex_pi.c
endif TEST_MUTEX_PI
diff --git a/configure.ac b/configure.ac
index 26939597..947786c5 100644
--- a/configure.ac
+++ b/configure.ac
@@ -77,6 +77,7 @@ AC_DEFINE_UNQUOTED([X15_ARCH], [$arch], [arch])
m4_define([ENABLE_TEST_MODULE],
[AS_CASE(["$enable_test_module"],
[llsync_defer], [test_llsync_defer=yes],
+ [mutex], [test_mutex=yes],
[mutex_pi], [test_mutex_pi=yes],
[pmap_update_mp], [test_pmap_update_mp=yes],
[sref_dirty_zeroes], [test_sref_dirty_zeroes=yes],
@@ -92,6 +93,8 @@ m4_define([ENABLE_TEST_MODULE],
AS_IF([test x"$enable_test_module" != xno], [ENABLE_TEST_MODULE])
AM_CONDITIONAL([TEST_LLSYNC_DEFER],
[test x"$test_llsync_defer" = xyes])
+AM_CONDITIONAL([TEST_MUTEX],
+ [test x"$test_mutex" = xyes])
AM_CONDITIONAL([TEST_MUTEX_PI],
[test x"$test_mutex_pi" = xyes])
AM_CONDITIONAL([TEST_PMAP_UPDATE_MP],
diff --git a/test/test_mutex.c b/test/test_mutex.c
new file mode 100644
index 00000000..f24cc9e1
--- /dev/null
+++ b/test/test_mutex.c
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2017 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 module is a stress test, expected to never terminate, of the
+ * timed lock functionality provided by the mutex implementations. The
+ * two conditions for success are :
+ * - no assertion triggered
+ * - all debugging system counters of the selected mutex implementation
+ * must be non-zero after some time.
+ *
+ * The system counters are meant to perform simple code coverage, asserting
+ * all the tricky code paths are taken at least once.
+ */
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdio.h>
+
+#include <kern/atomic.h>
+#include <kern/clock.h>
+#include <kern/error.h>
+#include <kern/kmem.h>
+#include <kern/log.h>
+#include <kern/mutex.h>
+#include <kern/panic.h>
+#include <kern/thread.h>
+#include <kern/timer.h>
+#include <test/test.h>
+
+#define TEST_REPORT_INTERVAL 10000
+
+struct test {
+ struct mutex mutex;
+ unsigned int counter;
+};
+
+static struct timer test_timer;
+
+static void
+test_run(void *arg)
+{
+ unsigned int prev, counter;
+ struct test *test;
+ int error;
+
+ test = arg;
+
+ for (counter = 1; /* no condition */; counter++) {
+ if ((counter % 1024) == 0) {
+ printf("%s ", thread_self()->name);
+ }
+
+ error = mutex_timedlock(&test->mutex, clock_get_time() + 1);
+
+ if (error) {
+ thread_delay(1, false);
+ continue;
+ }
+
+ prev = atomic_fetch_add(&test->counter, 1, ATOMIC_SEQ_CST);
+
+ if (prev != 0) {
+ break;
+ }
+
+ if ((counter % 2) == 0) {
+ cpu_delay(clock_ticks_to_ms(1) * 1000);
+ } else {
+ thread_delay(1, false);
+ }
+
+ prev = atomic_fetch_sub(&test->counter, 1, ATOMIC_SEQ_CST);
+
+ if (prev != 1) {
+ break;
+ }
+
+ mutex_unlock(&test->mutex);
+
+ if ((counter % 2) == 0) {
+ thread_delay(1, false);
+ }
+ }
+
+ panic("test: invalid counter value (%u)", test->counter);
+}
+
+static struct test *
+test_create(unsigned int nr_threads)
+{
+ char name[THREAD_NAME_SIZE];
+ struct thread_attr attr;
+ struct thread *thread;
+ struct cpumap *cpumap;
+ struct test *test;
+ int error;
+
+ assert(nr_threads);
+
+ test = kmem_alloc(sizeof(*test));
+
+ if (!test) {
+ panic("test: unable to allocate memory");
+ }
+
+ mutex_init(&test->mutex);
+ test->counter = 0;
+
+ error = cpumap_create(&cpumap);
+ error_check(error, "cpumap_create");
+
+ for (size_t i = 0; i < nr_threads; i++) {
+ cpumap_zero(cpumap);
+ cpumap_set(cpumap, i % 3);
+ snprintf(name, sizeof(name), THREAD_KERNEL_PREFIX "test_run:%u/%zu",
+ nr_threads, i);
+ thread_attr_init(&attr, name);
+ thread_attr_set_detached(&attr);
+ thread_attr_set_cpumap(&attr, cpumap);
+
+ if (i < 2) {
+ thread_attr_set_policy(&attr, THREAD_SCHED_POLICY_RR);
+ thread_attr_set_priority(&attr, THREAD_SCHED_RT_PRIO_MIN + i);
+ }
+
+ error = thread_create(&thread, &attr, test_run, test);
+ error_check(error, "thread_create");
+ }
+
+ return test;
+}
+
+static void
+test_report_syscnt(struct timer *timer)
+{
+ uint64_t time;
+
+#ifdef X15_MUTEX_PI
+ syscnt_info("rtmutex");
+#else /* X15_MUTEX_PI */
+ syscnt_info("mutex");
+#endif /* X15_MUTEX_PI */
+
+ time = timer_get_time(timer) + clock_ticks_from_ms(TEST_REPORT_INTERVAL);
+ timer_schedule(timer, time);
+}
+
+void
+test_setup(void)
+{
+ uint64_t time;
+
+ if (cpu_count() < 3) {
+ panic("test: at least 3 processors are required");
+ }
+
+ test_create(1);
+ test_create(2);
+ test_create(3);
+ test_create(10);
+
+ timer_init(&test_timer, test_report_syscnt, TIMER_DETACHED);
+ time = clock_get_time() + clock_ticks_from_ms(TEST_REPORT_INTERVAL);
+ timer_schedule(&test_timer, time);
+
+ log_info("test: enable mutex debugging for the selected implementation");
+ log_info("test: and check the relevant system counters");
+}