* Author: Marco Elver <elver@google.com>
*/
+#define pr_fmt(fmt) "kcsan_test: " fmt
+
#include <kunit/test.h>
#include <linux/jiffies.h>
#include <linux/kcsan-checks.h>
}
/*
- * Each test case is run with different numbers of threads. Until KUnit supports
- * passing arguments for each test case, we encode #threads in the test case
- * name (read by get_num_threads()). [The '-' was chosen as a stylistic
- * preference to separate test name and #threads.]
+ * Generate thread counts for all test cases. Values generated are in interval
+ * [2, 5] followed by exponentially increasing thread counts from 8 to 32.
*
* 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.
*/
-#define KCSAN_KUNIT_CASE(test_name) \
- { .run_case = test_name, .name = #test_name "-02" }, \
- { .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" }
+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.
+ */
+ const long min_unused_cpus = IS_ENABLED(CONFIG_PREEMPT_NONE) ? 2 : 0;
+ const long min_required_cpus = 2 + min_unused_cpus;
+
+ 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[] = {
KCSAN_KUNIT_CASE(test_basic),
KCSAN_KUNIT_CASE(test_concurrent_races),
/* ===== 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. */
__no_kcsan
static void access_thread_timer(struct timer_list *timer)
if (!torture_init_begin((char *)test->name, 1))
return -EBUSY;
- if (!get_num_threads(test->name, &nthreads))
- goto err;
-
if (WARN_ON(threads))
goto err;
goto err;
}
- 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.
- */
- const int min_unused_cpus = IS_ENABLED(CONFIG_PREEMPT_NONE) ? 2 : 0;
- const int min_required_cpus = 2 + min_unused_cpus;
+ nthreads = abs((long)test->param_value);
+ if (WARN_ON(!nthreads))
+ goto err;
- 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;
- } 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);
- }
- }
+ threads = kcalloc(nthreads + 1, sizeof(struct task_struct *), GFP_KERNEL);
+ if (WARN_ON(!threads))
+ goto err;
- if (nthreads) {
- threads = kcalloc(nthreads + 1, sizeof(struct task_struct *),
- GFP_KERNEL);
- if (WARN_ON(!threads))
+ threads[nthreads] = NULL;
+ for (i = 0; i < nthreads; ++i) {
+ if (torture_create_kthread(access_thread, NULL, threads[i]))
goto err;
-
- threads[nthreads] = NULL;
- for (i = 0; i < nthreads; ++i) {
- if (torture_create_kthread(access_thread, NULL,
- threads[i]))
- goto err;
- }
}
torture_init_end();