Commit f6a14914 authored by Marco Elver's avatar Marco Elver Committed by Paul E. McKenney

kcsan: Switch to KUNIT_CASE_PARAM for parameterized tests

Since KUnit now support parameterized tests via KUNIT_CASE_PARAM, update
KCSAN's test to switch to it for parameterized tests. This simplifies
parameterized tests and gets rid of the "parameters in case name"
workaround (hack).

At the same time, we can increase the maximum number of threads used,
because on systems with too few CPUs, KUnit allows us to now stop at the
maximum useful threads and not unnecessarily execute redundant test
cases with (the same) limited threads as had been the case before.
Reviewed-by: default avatarDavid Gow <davidgow@google.com>
Signed-off-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent a146fed5
...@@ -13,6 +13,8 @@ ...@@ -13,6 +13,8 @@
* Author: Marco Elver <elver@google.com> * Author: Marco Elver <elver@google.com>
*/ */
#define pr_fmt(fmt) "kcsan_test: " fmt
#include <kunit/test.h> #include <kunit/test.h>
#include <linux/jiffies.h> #include <linux/jiffies.h>
#include <linux/kcsan-checks.h> #include <linux/kcsan-checks.h>
...@@ -951,22 +953,53 @@ static void test_atomic_builtins(struct kunit *test) ...@@ -951,22 +953,53 @@ static void test_atomic_builtins(struct kunit *test)
} }
/* /*
* Each test case is run with different numbers of threads. Until KUnit supports * Generate thread counts for all test cases. Values generated are in interval
* passing arguments for each test case, we encode #threads in the test case * [2, 5] followed by exponentially increasing thread counts from 8 to 32.
* name (read by get_num_threads()). [The '-' was chosen as a stylistic
* preference to separate test name and #threads.]
* *
* The thread counts are chosen to cover potentially interesting boundaries and * The thread counts are chosen to cover potentially interesting boundaries and
* corner cases (range 2-5), and then stress the system with larger counts. * corner cases (2 to 5), and then stress the system with larger counts.
*/
static const void *nthreads_gen_params(const void *prev, char *desc)
{
long nthreads = (long)prev;
if (nthreads < 0 || nthreads >= 32)
nthreads = 0; /* stop */
else if (!nthreads)
nthreads = 2; /* initial value */
else if (nthreads < 5)
nthreads++;
else if (nthreads == 5)
nthreads = 8;
else
nthreads *= 2;
if (!IS_ENABLED(CONFIG_PREEMPT) || !IS_ENABLED(CONFIG_KCSAN_INTERRUPT_WATCHER)) {
/*
* Without any preemption, keep 2 CPUs free for other tasks, one
* of which is the main test case function checking for
* completion or failure.
*/ */
#define KCSAN_KUNIT_CASE(test_name) \ const long min_unused_cpus = IS_ENABLED(CONFIG_PREEMPT_NONE) ? 2 : 0;
{ .run_case = test_name, .name = #test_name "-02" }, \ const long min_required_cpus = 2 + min_unused_cpus;
{ .run_case = test_name, .name = #test_name "-03" }, \
{ .run_case = test_name, .name = #test_name "-04" }, \
{ .run_case = test_name, .name = #test_name "-05" }, \
{ .run_case = test_name, .name = #test_name "-08" }, \
{ .run_case = test_name, .name = #test_name "-16" }
if (num_online_cpus() < min_required_cpus) {
pr_err_once("Too few online CPUs (%u < %d) for test\n",
num_online_cpus(), min_required_cpus);
nthreads = 0;
} else if (nthreads >= num_online_cpus() - min_unused_cpus) {
/* Use negative value to indicate last param. */
nthreads = -(num_online_cpus() - min_unused_cpus);
pr_warn_once("Limiting number of threads to %ld (only %d online CPUs)\n",
-nthreads, num_online_cpus());
}
}
snprintf(desc, KUNIT_PARAM_DESC_SIZE, "threads=%ld", abs(nthreads));
return (void *)nthreads;
}
#define KCSAN_KUNIT_CASE(test_name) KUNIT_CASE_PARAM(test_name, nthreads_gen_params)
static struct kunit_case kcsan_test_cases[] = { static struct kunit_case kcsan_test_cases[] = {
KCSAN_KUNIT_CASE(test_basic), KCSAN_KUNIT_CASE(test_basic),
KCSAN_KUNIT_CASE(test_concurrent_races), KCSAN_KUNIT_CASE(test_concurrent_races),
...@@ -996,24 +1029,6 @@ static struct kunit_case kcsan_test_cases[] = { ...@@ -996,24 +1029,6 @@ static struct kunit_case kcsan_test_cases[] = {
/* ===== End test cases ===== */ /* ===== End test cases ===== */
/* Get number of threads encoded in test name. */
static bool __no_kcsan
get_num_threads(const char *test, int *nthreads)
{
int len = strlen(test);
if (WARN_ON(len < 3))
return false;
*nthreads = test[len - 1] - '0';
*nthreads += (test[len - 2] - '0') * 10;
if (WARN_ON(*nthreads < 0))
return false;
return true;
}
/* Concurrent accesses from interrupts. */ /* Concurrent accesses from interrupts. */
__no_kcsan __no_kcsan
static void access_thread_timer(struct timer_list *timer) static void access_thread_timer(struct timer_list *timer)
...@@ -1076,9 +1091,6 @@ static int test_init(struct kunit *test) ...@@ -1076,9 +1091,6 @@ static int test_init(struct kunit *test)
if (!torture_init_begin((char *)test->name, 1)) if (!torture_init_begin((char *)test->name, 1))
return -EBUSY; return -EBUSY;
if (!get_num_threads(test->name, &nthreads))
goto err;
if (WARN_ON(threads)) if (WARN_ON(threads))
goto err; goto err;
...@@ -1087,39 +1099,19 @@ static int test_init(struct kunit *test) ...@@ -1087,39 +1099,19 @@ static int test_init(struct kunit *test)
goto err; goto err;
} }
if (!IS_ENABLED(CONFIG_PREEMPT) || !IS_ENABLED(CONFIG_KCSAN_INTERRUPT_WATCHER)) { nthreads = abs((long)test->param_value);
/* if (WARN_ON(!nthreads))
* Without any preemption, keep 2 CPUs free for other tasks, one
* of which is the main test case function checking for
* completion or failure.
*/
const int min_unused_cpus = IS_ENABLED(CONFIG_PREEMPT_NONE) ? 2 : 0;
const int min_required_cpus = 2 + min_unused_cpus;
if (num_online_cpus() < min_required_cpus) {
pr_err("%s: too few online CPUs (%u < %d) for test",
test->name, num_online_cpus(), min_required_cpus);
goto err; goto err;
} else if (nthreads > num_online_cpus() - min_unused_cpus) {
nthreads = num_online_cpus() - min_unused_cpus;
pr_warn("%s: limiting number of threads to %d\n",
test->name, nthreads);
}
}
if (nthreads) { threads = kcalloc(nthreads + 1, sizeof(struct task_struct *), GFP_KERNEL);
threads = kcalloc(nthreads + 1, sizeof(struct task_struct *),
GFP_KERNEL);
if (WARN_ON(!threads)) if (WARN_ON(!threads))
goto err; goto err;
threads[nthreads] = NULL; threads[nthreads] = NULL;
for (i = 0; i < nthreads; ++i) { for (i = 0; i < nthreads; ++i) {
if (torture_create_kthread(access_thread, NULL, if (torture_create_kthread(access_thread, NULL, threads[i]))
threads[i]))
goto err; goto err;
} }
}
torture_init_end(); torture_init_end();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment