Commit 19dfdc05 authored by Mark Rutland's avatar Mark Rutland Committed by Paul E. McKenney

kcsan: Remove reporting indirection

Now that we have separate kcsan_report_*() functions, we can factor the
distinct logic for each of the report cases out of kcsan_report(). While
this means each case has to handle mutual exclusion independently, this
minimizes the conditionality of code and makes it easier to read, and
will permit passing distinct bits of information to print_report() in
future.

There should be no functional change as a result of this patch.
Signed-off-by: default avatarMark Rutland <mark.rutland@arm.com>
[ elver@google.com: retain comment about lockdep_off() ]
Signed-off-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarPaul E. McKenney <paulmck@kernel.org>
parent 39b2e763
...@@ -434,13 +434,11 @@ static void print_report(enum kcsan_value_change value_change, ...@@ -434,13 +434,11 @@ static void print_report(enum kcsan_value_change value_change,
static void release_report(unsigned long *flags, struct other_info *other_info) static void release_report(unsigned long *flags, struct other_info *other_info)
{ {
if (other_info) /*
/* * Use size to denote valid/invalid, since KCSAN entirely ignores
* Use size to denote valid/invalid, since KCSAN entirely * 0-sized accesses.
* ignores 0-sized accesses. */
*/ other_info->ai.size = 0;
other_info->ai.size = 0;
raw_spin_unlock_irqrestore(&report_lock, *flags); raw_spin_unlock_irqrestore(&report_lock, *flags);
} }
...@@ -573,61 +571,6 @@ static bool prepare_report_consumer(unsigned long *flags, ...@@ -573,61 +571,6 @@ static bool prepare_report_consumer(unsigned long *flags,
return false; return false;
} }
/*
* Depending on the report type either sets @other_info and returns false, or
* awaits @other_info and returns true. If @other_info is not required for the
* report type, simply acquires @report_lock and returns true.
*/
static noinline bool prepare_report(unsigned long *flags,
enum kcsan_report_type type,
const struct access_info *ai,
struct other_info *other_info)
{
switch (type) {
case KCSAN_REPORT_CONSUMED_WATCHPOINT:
prepare_report_producer(flags, ai, other_info);
return false;
case KCSAN_REPORT_RACE_SIGNAL:
return prepare_report_consumer(flags, ai, other_info);
default:
/* @other_info not required; just acquire @report_lock. */
raw_spin_lock_irqsave(&report_lock, *flags);
return true;
}
}
static void kcsan_report(const struct access_info *ai, enum kcsan_value_change value_change,
enum kcsan_report_type type, struct other_info *other_info)
{
unsigned long flags = 0;
kcsan_disable_current();
/*
* Because we may generate reports when we're in scheduler code, the use
* of printk() could deadlock. Until such time that all printing code
* called in print_report() is scheduler-safe, accept the risk, and just
* get our message out. As such, also disable lockdep to hide the
* warning, and avoid disabling lockdep for the rest of the kernel.
*/
lockdep_off();
if (prepare_report(&flags, type, ai, other_info)) {
/*
* Never report if value_change is FALSE, only if we it is
* either TRUE or MAYBE. In case of MAYBE, further filtering may
* be done once we know the full stack trace in print_report().
*/
if (value_change != KCSAN_VALUE_CHANGE_FALSE)
print_report(value_change, type, ai, other_info);
release_report(&flags, other_info);
}
lockdep_on();
kcsan_enable_current();
}
static struct access_info prepare_access_info(const volatile void *ptr, size_t size, static struct access_info prepare_access_info(const volatile void *ptr, size_t size,
int access_type) int access_type)
{ {
...@@ -644,22 +587,62 @@ void kcsan_report_set_info(const volatile void *ptr, size_t size, int access_typ ...@@ -644,22 +587,62 @@ void kcsan_report_set_info(const volatile void *ptr, size_t size, int access_typ
int watchpoint_idx) int watchpoint_idx)
{ {
const struct access_info ai = prepare_access_info(ptr, size, access_type); const struct access_info ai = prepare_access_info(ptr, size, access_type);
unsigned long flags;
kcsan_disable_current();
lockdep_off(); /* See kcsan_report_known_origin(). */
kcsan_report(&ai, KCSAN_VALUE_CHANGE_MAYBE, KCSAN_REPORT_CONSUMED_WATCHPOINT, prepare_report_producer(&flags, &ai, &other_infos[watchpoint_idx]);
&other_infos[watchpoint_idx]);
lockdep_on();
kcsan_enable_current();
} }
void kcsan_report_known_origin(const volatile void *ptr, size_t size, int access_type, void kcsan_report_known_origin(const volatile void *ptr, size_t size, int access_type,
enum kcsan_value_change value_change, int watchpoint_idx) enum kcsan_value_change value_change, int watchpoint_idx)
{ {
const struct access_info ai = prepare_access_info(ptr, size, access_type); const struct access_info ai = prepare_access_info(ptr, size, access_type);
struct other_info *other_info = &other_infos[watchpoint_idx];
unsigned long flags = 0;
kcsan_report(&ai, value_change, KCSAN_REPORT_RACE_SIGNAL, &other_infos[watchpoint_idx]); kcsan_disable_current();
/*
* Because we may generate reports when we're in scheduler code, the use
* of printk() could deadlock. Until such time that all printing code
* called in print_report() is scheduler-safe, accept the risk, and just
* get our message out. As such, also disable lockdep to hide the
* warning, and avoid disabling lockdep for the rest of the kernel.
*/
lockdep_off();
if (!prepare_report_consumer(&flags, &ai, other_info))
goto out;
/*
* Never report if value_change is FALSE, only when it is
* either TRUE or MAYBE. In case of MAYBE, further filtering may
* be done once we know the full stack trace in print_report().
*/
if (value_change != KCSAN_VALUE_CHANGE_FALSE)
print_report(value_change, KCSAN_REPORT_RACE_SIGNAL, &ai, other_info);
release_report(&flags, other_info);
out:
lockdep_on();
kcsan_enable_current();
} }
void kcsan_report_unknown_origin(const volatile void *ptr, size_t size, int access_type) void kcsan_report_unknown_origin(const volatile void *ptr, size_t size, int access_type)
{ {
const struct access_info ai = prepare_access_info(ptr, size, access_type); const struct access_info ai = prepare_access_info(ptr, size, access_type);
unsigned long flags;
kcsan_disable_current();
lockdep_off(); /* See kcsan_report_known_origin(). */
raw_spin_lock_irqsave(&report_lock, flags);
print_report(KCSAN_VALUE_CHANGE_TRUE, KCSAN_REPORT_RACE_UNKNOWN_ORIGIN, &ai, NULL);
raw_spin_unlock_irqrestore(&report_lock, flags);
kcsan_report(&ai, KCSAN_VALUE_CHANGE_TRUE, KCSAN_REPORT_RACE_UNKNOWN_ORIGIN, NULL); lockdep_on();
kcsan_enable_current();
} }
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