Merge branch 'for-mingo-kcsan' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu into locking/core

Pull KCSAN changes from Paul E. McKenney: misc updates.

Signed-off-by: Ingo Molnar <mingo@kernel.org>
This commit is contained in:
Ingo Molnar 2021-04-11 14:35:02 +02:00
commit eedd634134
13 changed files with 112 additions and 77 deletions

View File

@ -1,3 +1,6 @@
.. SPDX-License-Identifier: GPL-2.0
.. Copyright (C) 2019, Google LLC.
The Kernel Concurrency Sanitizer (KCSAN)
========================================

View File

@ -1,4 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* KCSAN access checks and modifiers. These can be used to explicitly check
* uninstrumented accesses, or change KCSAN checking behaviour of accesses.
*
* Copyright (C) 2019, Google LLC.
*/
#ifndef _LINUX_KCSAN_CHECKS_H
#define _LINUX_KCSAN_CHECKS_H

View File

@ -1,4 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The Kernel Concurrency Sanitizer (KCSAN) infrastructure. Public interface and
* data structures to set up runtime. See kcsan-checks.h for explicit checks and
* modifiers. For more info please see Documentation/dev-tools/kcsan.rst.
*
* Copyright (C) 2019, Google LLC.
*/
#ifndef _LINUX_KCSAN_H
#define _LINUX_KCSAN_H

View File

@ -13,5 +13,5 @@ CFLAGS_core.o := $(call cc-option,-fno-conserve-stack) \
obj-y := core.o debugfs.o report.o
obj-$(CONFIG_KCSAN_SELFTEST) += selftest.o
CFLAGS_kcsan-test.o := $(CFLAGS_KCSAN) -g -fno-omit-frame-pointer
obj-$(CONFIG_KCSAN_TEST) += kcsan-test.o
CFLAGS_kcsan_test.o := $(CFLAGS_KCSAN) -g -fno-omit-frame-pointer
obj-$(CONFIG_KCSAN_KUNIT_TEST) += kcsan_test.o

View File

@ -1,4 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Rules for implicitly atomic memory accesses.
*
* Copyright (C) 2019, Google LLC.
*/
#ifndef _KERNEL_KCSAN_ATOMIC_H
#define _KERNEL_KCSAN_ATOMIC_H

View File

@ -1,4 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KCSAN core runtime.
*
* Copyright (C) 2019, Google LLC.
*/
#define pr_fmt(fmt) "kcsan: " fmt
@ -639,8 +644,6 @@ void __init kcsan_init(void)
BUG_ON(!in_task());
kcsan_debugfs_init();
for_each_possible_cpu(cpu)
per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles();

View File

@ -1,4 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KCSAN debugfs interface.
*
* Copyright (C) 2019, Google LLC.
*/
#define pr_fmt(fmt) "kcsan: " fmt
@ -261,7 +266,9 @@ static const struct file_operations debugfs_ops =
.release = single_release
};
void __init kcsan_debugfs_init(void)
static void __init kcsan_debugfs_init(void)
{
debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops);
}
late_initcall(kcsan_debugfs_init);

View File

@ -1,4 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* KCSAN watchpoint encoding.
*
* Copyright (C) 2019, Google LLC.
*/
#ifndef _KERNEL_KCSAN_ENCODING_H
#define _KERNEL_KCSAN_ENCODING_H

View File

@ -1,8 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* The Kernel Concurrency Sanitizer (KCSAN) infrastructure. For more info please
* see Documentation/dev-tools/kcsan.rst.
*
* Copyright (C) 2019, Google LLC.
*/
#ifndef _KERNEL_KCSAN_KCSAN_H
@ -30,11 +31,6 @@ extern bool kcsan_enabled;
void kcsan_save_irqtrace(struct task_struct *task);
void kcsan_restore_irqtrace(struct task_struct *task);
/*
* Initialize debugfs file.
*/
void kcsan_debugfs_init(void);
/*
* Statistics counters displayed via debugfs; should only be modified in
* slow-paths.

View File

@ -13,6 +13,8 @@
* 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>
@ -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
* 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),
@ -996,24 +1029,6 @@ static struct kunit_case kcsan_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. */
__no_kcsan
static void access_thread_timer(struct timer_list *timer)
@ -1076,9 +1091,6 @@ static int test_init(struct kunit *test)
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;
@ -1087,38 +1099,18 @@ static int test_init(struct kunit *test)
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);
threads = kcalloc(nthreads + 1, sizeof(struct task_struct *), GFP_KERNEL);
if (WARN_ON(!threads))
goto err;
threads[nthreads] = NULL;
for (i = 0; i < nthreads; ++i) {
if (torture_create_kthread(access_thread, NULL, threads[i]))
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);
if (WARN_ON(!threads))
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();
@ -1156,7 +1148,7 @@ static void test_exit(struct kunit *test)
}
static struct kunit_suite kcsan_test_suite = {
.name = "kcsan-test",
.name = "kcsan",
.test_cases = kcsan_test_cases,
.init = test_init,
.exit = test_exit,

View File

@ -1,4 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KCSAN reporting.
*
* Copyright (C) 2019, Google LLC.
*/
#include <linux/debug_locks.h>
#include <linux/delay.h>

View File

@ -1,4 +1,9 @@
// SPDX-License-Identifier: GPL-2.0
/*
* KCSAN short boot-time selftests.
*
* Copyright (C) 2019, Google LLC.
*/
#define pr_fmt(fmt) "kcsan: " fmt

View File

@ -69,8 +69,9 @@ config KCSAN_SELFTEST
panic. Recommended to be enabled, ensuring critical functionality
works as intended.
config KCSAN_TEST
tristate "KCSAN test for integrated runtime behaviour"
config KCSAN_KUNIT_TEST
tristate "KCSAN test for integrated runtime behaviour" if !KUNIT_ALL_TESTS
default KUNIT_ALL_TESTS
depends on TRACEPOINTS && KUNIT
select TORTURE_TEST
help