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:
commit
eedd634134
13 changed files with 112 additions and 77 deletions
|
@ -1,3 +1,6 @@
|
||||||
|
.. SPDX-License-Identifier: GPL-2.0
|
||||||
|
.. Copyright (C) 2019, Google LLC.
|
||||||
|
|
||||||
The Kernel Concurrency Sanitizer (KCSAN)
|
The Kernel Concurrency Sanitizer (KCSAN)
|
||||||
========================================
|
========================================
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* 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
|
#ifndef _LINUX_KCSAN_CHECKS_H
|
||||||
#define _LINUX_KCSAN_CHECKS_H
|
#define _LINUX_KCSAN_CHECKS_H
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* 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
|
#ifndef _LINUX_KCSAN_H
|
||||||
#define _LINUX_KCSAN_H
|
#define _LINUX_KCSAN_H
|
||||||
|
|
|
@ -13,5 +13,5 @@ CFLAGS_core.o := $(call cc-option,-fno-conserve-stack) \
|
||||||
obj-y := core.o debugfs.o report.o
|
obj-y := core.o debugfs.o report.o
|
||||||
obj-$(CONFIG_KCSAN_SELFTEST) += selftest.o
|
obj-$(CONFIG_KCSAN_SELFTEST) += selftest.o
|
||||||
|
|
||||||
CFLAGS_kcsan-test.o := $(CFLAGS_KCSAN) -g -fno-omit-frame-pointer
|
CFLAGS_kcsan_test.o := $(CFLAGS_KCSAN) -g -fno-omit-frame-pointer
|
||||||
obj-$(CONFIG_KCSAN_TEST) += kcsan-test.o
|
obj-$(CONFIG_KCSAN_KUNIT_TEST) += kcsan_test.o
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Rules for implicitly atomic memory accesses.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _KERNEL_KCSAN_ATOMIC_H
|
#ifndef _KERNEL_KCSAN_ATOMIC_H
|
||||||
#define _KERNEL_KCSAN_ATOMIC_H
|
#define _KERNEL_KCSAN_ATOMIC_H
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* KCSAN core runtime.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) "kcsan: " fmt
|
#define pr_fmt(fmt) "kcsan: " fmt
|
||||||
|
|
||||||
|
@ -639,8 +644,6 @@ void __init kcsan_init(void)
|
||||||
|
|
||||||
BUG_ON(!in_task());
|
BUG_ON(!in_task());
|
||||||
|
|
||||||
kcsan_debugfs_init();
|
|
||||||
|
|
||||||
for_each_possible_cpu(cpu)
|
for_each_possible_cpu(cpu)
|
||||||
per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles();
|
per_cpu(kcsan_rand_state, cpu) = (u32)get_cycles();
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* KCSAN debugfs interface.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) "kcsan: " fmt
|
#define pr_fmt(fmt) "kcsan: " fmt
|
||||||
|
|
||||||
|
@ -261,7 +266,9 @@ static const struct file_operations debugfs_ops =
|
||||||
.release = single_release
|
.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);
|
debugfs_create_file("kcsan", 0644, NULL, NULL, &debugfs_ops);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
late_initcall(kcsan_debugfs_init);
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* KCSAN watchpoint encoding.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#ifndef _KERNEL_KCSAN_ENCODING_H
|
#ifndef _KERNEL_KCSAN_ENCODING_H
|
||||||
#define _KERNEL_KCSAN_ENCODING_H
|
#define _KERNEL_KCSAN_ENCODING_H
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The Kernel Concurrency Sanitizer (KCSAN) infrastructure. For more info please
|
* The Kernel Concurrency Sanitizer (KCSAN) infrastructure. For more info please
|
||||||
* see Documentation/dev-tools/kcsan.rst.
|
* see Documentation/dev-tools/kcsan.rst.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef _KERNEL_KCSAN_KCSAN_H
|
#ifndef _KERNEL_KCSAN_KCSAN_H
|
||||||
|
@ -30,11 +31,6 @@ extern bool kcsan_enabled;
|
||||||
void kcsan_save_irqtrace(struct task_struct *task);
|
void kcsan_save_irqtrace(struct task_struct *task);
|
||||||
void kcsan_restore_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
|
* Statistics counters displayed via debugfs; should only be modified in
|
||||||
* slow-paths.
|
* slow-paths.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 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.
|
||||||
*/
|
*/
|
||||||
#define KCSAN_KUNIT_CASE(test_name) \
|
static const void *nthreads_gen_params(const void *prev, char *desc)
|
||||||
{ .run_case = test_name, .name = #test_name "-02" }, \
|
{
|
||||||
{ .run_case = test_name, .name = #test_name "-03" }, \
|
long nthreads = (long)prev;
|
||||||
{ .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 (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[] = {
|
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[] = {
|
||||||
|
|
||||||
/* ===== 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)
|
||||||
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,38 +1099,18 @@ 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
|
goto err;
|
||||||
* 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) {
|
threads = kcalloc(nthreads + 1, sizeof(struct task_struct *), GFP_KERNEL);
|
||||||
pr_err("%s: too few online CPUs (%u < %d) for test",
|
if (WARN_ON(!threads))
|
||||||
test->name, num_online_cpus(), min_required_cpus);
|
goto err;
|
||||||
|
|
||||||
|
threads[nthreads] = NULL;
|
||||||
|
for (i = 0; i < nthreads; ++i) {
|
||||||
|
if (torture_create_kthread(access_thread, NULL, threads[i]))
|
||||||
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);
|
|
||||||
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();
|
torture_init_end();
|
||||||
|
@ -1156,7 +1148,7 @@ static void test_exit(struct kunit *test)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct kunit_suite kcsan_test_suite = {
|
static struct kunit_suite kcsan_test_suite = {
|
||||||
.name = "kcsan-test",
|
.name = "kcsan",
|
||||||
.test_cases = kcsan_test_cases,
|
.test_cases = kcsan_test_cases,
|
||||||
.init = test_init,
|
.init = test_init,
|
||||||
.exit = test_exit,
|
.exit = test_exit,
|
|
@ -1,4 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* KCSAN reporting.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#include <linux/debug_locks.h>
|
#include <linux/debug_locks.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* KCSAN short boot-time selftests.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019, Google LLC.
|
||||||
|
*/
|
||||||
|
|
||||||
#define pr_fmt(fmt) "kcsan: " fmt
|
#define pr_fmt(fmt) "kcsan: " fmt
|
||||||
|
|
||||||
|
|
|
@ -69,8 +69,9 @@ config KCSAN_SELFTEST
|
||||||
panic. Recommended to be enabled, ensuring critical functionality
|
panic. Recommended to be enabled, ensuring critical functionality
|
||||||
works as intended.
|
works as intended.
|
||||||
|
|
||||||
config KCSAN_TEST
|
config KCSAN_KUNIT_TEST
|
||||||
tristate "KCSAN test for integrated runtime behaviour"
|
tristate "KCSAN test for integrated runtime behaviour" if !KUNIT_ALL_TESTS
|
||||||
|
default KUNIT_ALL_TESTS
|
||||||
depends on TRACEPOINTS && KUNIT
|
depends on TRACEPOINTS && KUNIT
|
||||||
select TORTURE_TEST
|
select TORTURE_TEST
|
||||||
help
|
help
|
||||||
|
|
Loading…
Add table
Reference in a new issue