Merge branches 'doc.2016.11.14a', 'fixes.2016.11.14a', 'list.2016.10.31a' and 'torture.2016.11.14a' into HEAD

doc.2016.11.14a:  Documentation changes
fixes.2016.11.14aneous fixes
list.2016.10.31a:  List updates
torture.2016.11.14a:  Torture-test updates
This commit is contained in:
Paul E. McKenney 2016-11-14 10:50:50 -08:00
commit 6190aaafd0
15 changed files with 197 additions and 106 deletions

View File

@ -21,6 +21,8 @@ void lkdtm_SPINLOCKUP(void);
void lkdtm_HUNG_TASK(void); void lkdtm_HUNG_TASK(void);
void lkdtm_ATOMIC_UNDERFLOW(void); void lkdtm_ATOMIC_UNDERFLOW(void);
void lkdtm_ATOMIC_OVERFLOW(void); void lkdtm_ATOMIC_OVERFLOW(void);
void lkdtm_CORRUPT_LIST_ADD(void);
void lkdtm_CORRUPT_LIST_DEL(void);
/* lkdtm_heap.c */ /* lkdtm_heap.c */
void lkdtm_OVERWRITE_ALLOCATION(void); void lkdtm_OVERWRITE_ALLOCATION(void);

View File

@ -5,8 +5,13 @@
* test source files. * test source files.
*/ */
#include "lkdtm.h" #include "lkdtm.h"
#include <linux/list.h>
#include <linux/sched.h> #include <linux/sched.h>
struct lkdtm_list {
struct list_head node;
};
/* /*
* Make sure our attempts to over run the kernel stack doesn't trigger * Make sure our attempts to over run the kernel stack doesn't trigger
* a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we * a compiler warning when CONFIG_FRAME_WARN is set. Then make sure we
@ -146,3 +151,66 @@ void lkdtm_ATOMIC_OVERFLOW(void)
pr_info("attempting bad atomic overflow\n"); pr_info("attempting bad atomic overflow\n");
atomic_inc(&over); atomic_inc(&over);
} }
void lkdtm_CORRUPT_LIST_ADD(void)
{
/*
* Initially, an empty list via LIST_HEAD:
* test_head.next = &test_head
* test_head.prev = &test_head
*/
LIST_HEAD(test_head);
struct lkdtm_list good, bad;
void *target[2] = { };
void *redirection = &target;
pr_info("attempting good list addition\n");
/*
* Adding to the list performs these actions:
* test_head.next->prev = &good.node
* good.node.next = test_head.next
* good.node.prev = test_head
* test_head.next = good.node
*/
list_add(&good.node, &test_head);
pr_info("attempting corrupted list addition\n");
/*
* In simulating this "write what where" primitive, the "what" is
* the address of &bad.node, and the "where" is the address held
* by "redirection".
*/
test_head.next = redirection;
list_add(&bad.node, &test_head);
if (target[0] == NULL && target[1] == NULL)
pr_err("Overwrite did not happen, but no BUG?!\n");
else
pr_err("list_add() corruption not detected!\n");
}
void lkdtm_CORRUPT_LIST_DEL(void)
{
LIST_HEAD(test_head);
struct lkdtm_list item;
void *target[2] = { };
void *redirection = &target;
list_add(&item.node, &test_head);
pr_info("attempting good list removal\n");
list_del(&item.node);
pr_info("attempting corrupted list removal\n");
list_add(&item.node, &test_head);
/* As with the list_add() test above, this corrupts "next". */
item.node.next = redirection;
list_del(&item.node);
if (target[0] == NULL && target[1] == NULL)
pr_err("Overwrite did not happen, but no BUG?!\n");
else
pr_err("list_del() corruption not detected!\n");
}

View File

@ -197,6 +197,8 @@ struct crashtype crashtypes[] = {
CRASHTYPE(EXCEPTION), CRASHTYPE(EXCEPTION),
CRASHTYPE(LOOP), CRASHTYPE(LOOP),
CRASHTYPE(OVERFLOW), CRASHTYPE(OVERFLOW),
CRASHTYPE(CORRUPT_LIST_ADD),
CRASHTYPE(CORRUPT_LIST_DEL),
CRASHTYPE(CORRUPT_STACK), CRASHTYPE(CORRUPT_STACK),
CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE), CRASHTYPE(UNALIGNED_LOAD_STORE_WRITE),
CRASHTYPE(OVERWRITE_ALLOCATION), CRASHTYPE(OVERWRITE_ALLOCATION),

View File

@ -121,4 +121,21 @@ static inline enum bug_trap_type report_bug(unsigned long bug_addr,
} }
#endif /* CONFIG_GENERIC_BUG */ #endif /* CONFIG_GENERIC_BUG */
/*
* Since detected data corruption should stop operation on the affected
* structures, this returns false if the corruption condition is found.
*/
#define CHECK_DATA_CORRUPTION(condition, fmt, ...) \
do { \
if (unlikely(condition)) { \
if (IS_ENABLED(CONFIG_BUG_ON_DATA_CORRUPTION)) { \
pr_err(fmt, ##__VA_ARGS__); \
BUG(); \
} else \
WARN(1, fmt, ##__VA_ARGS__); \
return false; \
} \
} while (0)
#endif /* _LINUX_BUG_H */ #endif /* _LINUX_BUG_H */

View File

@ -28,27 +28,42 @@ static inline void INIT_LIST_HEAD(struct list_head *list)
list->prev = list; list->prev = list;
} }
#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next);
extern bool __list_del_entry_valid(struct list_head *entry);
#else
static inline bool __list_add_valid(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
return true;
}
static inline bool __list_del_entry_valid(struct list_head *entry)
{
return true;
}
#endif
/* /*
* Insert a new entry between two known consecutive entries. * Insert a new entry between two known consecutive entries.
* *
* This is only for internal list manipulation where we know * This is only for internal list manipulation where we know
* the prev/next entries already! * the prev/next entries already!
*/ */
#ifndef CONFIG_DEBUG_LIST
static inline void __list_add(struct list_head *new, static inline void __list_add(struct list_head *new,
struct list_head *prev, struct list_head *prev,
struct list_head *next) struct list_head *next)
{ {
if (!__list_add_valid(new, prev, next))
return;
next->prev = new; next->prev = new;
new->next = next; new->next = next;
new->prev = prev; new->prev = prev;
WRITE_ONCE(prev->next, new); WRITE_ONCE(prev->next, new);
} }
#else
extern void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next);
#endif
/** /**
* list_add - add a new entry * list_add - add a new entry
@ -96,22 +111,20 @@ static inline void __list_del(struct list_head * prev, struct list_head * next)
* Note: list_empty() on entry does not return true after this, the entry is * Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state. * in an undefined state.
*/ */
#ifndef CONFIG_DEBUG_LIST
static inline void __list_del_entry(struct list_head *entry) static inline void __list_del_entry(struct list_head *entry)
{ {
if (!__list_del_entry_valid(entry))
return;
__list_del(entry->prev, entry->next); __list_del(entry->prev, entry->next);
} }
static inline void list_del(struct list_head *entry) static inline void list_del(struct list_head *entry)
{ {
__list_del(entry->prev, entry->next); __list_del_entry(entry);
entry->next = LIST_POISON1; entry->next = LIST_POISON1;
entry->prev = LIST_POISON2; entry->prev = LIST_POISON2;
} }
#else
extern void __list_del_entry(struct list_head *entry);
extern void list_del(struct list_head *entry);
#endif
/** /**
* list_replace - replace old entry by new one * list_replace - replace old entry by new one

View File

@ -45,19 +45,17 @@ static inline void INIT_LIST_HEAD_RCU(struct list_head *list)
* This is only for internal list manipulation where we know * This is only for internal list manipulation where we know
* the prev/next entries already! * the prev/next entries already!
*/ */
#ifndef CONFIG_DEBUG_LIST
static inline void __list_add_rcu(struct list_head *new, static inline void __list_add_rcu(struct list_head *new,
struct list_head *prev, struct list_head *next) struct list_head *prev, struct list_head *next)
{ {
if (!__list_add_valid(new, prev, next))
return;
new->next = next; new->next = next;
new->prev = prev; new->prev = prev;
rcu_assign_pointer(list_next_rcu(prev), new); rcu_assign_pointer(list_next_rcu(prev), new);
next->prev = new; next->prev = new;
} }
#else
void __list_add_rcu(struct list_head *new,
struct list_head *prev, struct list_head *next);
#endif
/** /**
* list_add_rcu - add a new entry to rcu-protected list * list_add_rcu - add a new entry to rcu-protected list

View File

@ -698,7 +698,10 @@ TRACE_EVENT(rcu_batch_end,
/* /*
* Tracepoint for rcutorture readers. The first argument is the name * Tracepoint for rcutorture readers. The first argument is the name
* of the RCU flavor from rcutorture's viewpoint and the second argument * of the RCU flavor from rcutorture's viewpoint and the second argument
* is the callback address. * is the callback address. The third argument is the start time in
* seconds, and the last two arguments are the grace period numbers
* at the beginning and end of the read, respectively. Note that the
* callback address can be NULL.
*/ */
TRACE_EVENT(rcu_torture_read, TRACE_EVENT(rcu_torture_read,

View File

@ -289,15 +289,24 @@ static int rcu_torture_read_lock(void) __acquires(RCU)
static void rcu_read_delay(struct torture_random_state *rrsp) static void rcu_read_delay(struct torture_random_state *rrsp)
{ {
unsigned long started;
unsigned long completed;
const unsigned long shortdelay_us = 200; const unsigned long shortdelay_us = 200;
const unsigned long longdelay_ms = 50; const unsigned long longdelay_ms = 50;
unsigned long long ts;
/* We want a short delay sometimes to make a reader delay the grace /* We want a short delay sometimes to make a reader delay the grace
* period, and we want a long delay occasionally to trigger * period, and we want a long delay occasionally to trigger
* force_quiescent_state. */ * force_quiescent_state. */
if (!(torture_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) if (!(torture_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) {
started = cur_ops->completed();
ts = rcu_trace_clock_local();
mdelay(longdelay_ms); mdelay(longdelay_ms);
completed = cur_ops->completed();
do_trace_rcu_torture_read(cur_ops->name, NULL, ts,
started, completed);
}
if (!(torture_random(rrsp) % (nrealreaders * 2 * shortdelay_us))) if (!(torture_random(rrsp) % (nrealreaders * 2 * shortdelay_us)))
udelay(shortdelay_us); udelay(shortdelay_us);
#ifdef CONFIG_PREEMPT #ifdef CONFIG_PREEMPT

View File

@ -1304,7 +1304,8 @@ static void rcu_stall_kick_kthreads(struct rcu_state *rsp)
if (!rcu_kick_kthreads) if (!rcu_kick_kthreads)
return; return;
j = READ_ONCE(rsp->jiffies_kick_kthreads); j = READ_ONCE(rsp->jiffies_kick_kthreads);
if (time_after(jiffies, j) && rsp->gp_kthread) { if (time_after(jiffies, j) && rsp->gp_kthread &&
(rcu_gp_in_progress(rsp) || READ_ONCE(rsp->gp_flags))) {
WARN_ONCE(1, "Kicking %s grace-period kthread\n", rsp->name); WARN_ONCE(1, "Kicking %s grace-period kthread\n", rsp->name);
rcu_ftrace_dump(DUMP_ALL); rcu_ftrace_dump(DUMP_ALL);
wake_up_process(rsp->gp_kthread); wake_up_process(rsp->gp_kthread);
@ -2828,8 +2829,7 @@ static void rcu_do_batch(struct rcu_state *rsp, struct rcu_data *rdp)
* Also schedule RCU core processing. * Also schedule RCU core processing.
* *
* This function must be called from hardirq context. It is normally * This function must be called from hardirq context. It is normally
* invoked from the scheduling-clock interrupt. If rcu_pending returns * invoked from the scheduling-clock interrupt.
* false, there is no point in invoking rcu_check_callbacks().
*/ */
void rcu_check_callbacks(int user) void rcu_check_callbacks(int user)
{ {
@ -3121,7 +3121,9 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func,
unsigned long flags; unsigned long flags;
struct rcu_data *rdp; struct rcu_data *rdp;
WARN_ON_ONCE((unsigned long)head & 0x1); /* Misaligned rcu_head! */ /* Misaligned rcu_head! */
WARN_ON_ONCE((unsigned long)head & (sizeof(void *) - 1));
if (debug_rcu_head_queue(head)) { if (debug_rcu_head_queue(head)) {
/* Probable double call_rcu(), so leak the callback. */ /* Probable double call_rcu(), so leak the callback. */
WRITE_ONCE(head->func, rcu_leak_callback); WRITE_ONCE(head->func, rcu_leak_callback);
@ -3130,13 +3132,6 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func,
} }
head->func = func; head->func = func;
head->next = NULL; head->next = NULL;
/*
* Opportunistically note grace-period endings and beginnings.
* Note that we might see a beginning right after we see an
* end, but never vice versa, since this CPU has to pass through
* a quiescent state betweentimes.
*/
local_irq_save(flags); local_irq_save(flags);
rdp = this_cpu_ptr(rsp->rda); rdp = this_cpu_ptr(rsp->rda);

View File

@ -404,6 +404,7 @@ struct rcu_data {
atomic_long_t exp_workdone1; /* # done by others #1. */ atomic_long_t exp_workdone1; /* # done by others #1. */
atomic_long_t exp_workdone2; /* # done by others #2. */ atomic_long_t exp_workdone2; /* # done by others #2. */
atomic_long_t exp_workdone3; /* # done by others #3. */ atomic_long_t exp_workdone3; /* # done by others #3. */
int exp_dynticks_snap; /* Double-check need for IPI. */
/* 7) Callback offloading. */ /* 7) Callback offloading. */
#ifdef CONFIG_RCU_NOCB_CPU #ifdef CONFIG_RCU_NOCB_CPU

View File

@ -358,8 +358,10 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu); struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu); struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
rdp->exp_dynticks_snap =
atomic_add_return(0, &rdtp->dynticks);
if (raw_smp_processor_id() == cpu || if (raw_smp_processor_id() == cpu ||
!(atomic_add_return(0, &rdtp->dynticks) & 0x1) || !(rdp->exp_dynticks_snap & 0x1) ||
!(rnp->qsmaskinitnext & rdp->grpmask)) !(rnp->qsmaskinitnext & rdp->grpmask))
mask_ofl_test |= rdp->grpmask; mask_ofl_test |= rdp->grpmask;
} }
@ -377,9 +379,17 @@ static void sync_rcu_exp_select_cpus(struct rcu_state *rsp,
/* IPI the remaining CPUs for expedited quiescent state. */ /* IPI the remaining CPUs for expedited quiescent state. */
for_each_leaf_node_possible_cpu(rnp, cpu) { for_each_leaf_node_possible_cpu(rnp, cpu) {
unsigned long mask = leaf_node_cpu_bit(rnp, cpu); unsigned long mask = leaf_node_cpu_bit(rnp, cpu);
struct rcu_data *rdp = per_cpu_ptr(rsp->rda, cpu);
struct rcu_dynticks *rdtp = &per_cpu(rcu_dynticks, cpu);
if (!(mask_ofl_ipi & mask)) if (!(mask_ofl_ipi & mask))
continue; continue;
retry_ipi: retry_ipi:
if (atomic_add_return(0, &rdtp->dynticks) !=
rdp->exp_dynticks_snap) {
mask_ofl_test |= mask;
continue;
}
ret = smp_call_function_single(cpu, func, rsp, 0); ret = smp_call_function_single(cpu, func, rsp, 0);
if (!ret) { if (!ret) {
mask_ofl_ipi &= ~mask; mask_ofl_ipi &= ~mask;

View File

@ -1214,7 +1214,7 @@ config DEBUG_BUGVERBOSE
config DEBUG_LIST config DEBUG_LIST
bool "Debug linked list manipulation" bool "Debug linked list manipulation"
depends on DEBUG_KERNEL depends on DEBUG_KERNEL || BUG_ON_DATA_CORRUPTION
help help
Enable this to turn on extended checks in the linked-list Enable this to turn on extended checks in the linked-list
walking routines. walking routines.
@ -1430,7 +1430,8 @@ config RCU_TRACE
select TRACE_CLOCK select TRACE_CLOCK
help help
This option provides tracing in RCU which presents stats This option provides tracing in RCU which presents stats
in debugfs for debugging RCU implementation. in debugfs for debugging RCU implementation. It also enables
additional tracepoints for ftrace-style event tracing.
Say Y here if you want to enable RCU tracing Say Y here if you want to enable RCU tracing
Say N if you are unsure. Say N if you are unsure.
@ -1960,6 +1961,16 @@ config TEST_STATIC_KEYS
If unsure, say N. If unsure, say N.
config BUG_ON_DATA_CORRUPTION
bool "Trigger a BUG when data corruption is detected"
select DEBUG_LIST
help
Select this option if the kernel should BUG when it encounters
data corruption in kernel memory structures when they get checked
for validity.
If unsure, say N.
source "samples/Kconfig" source "samples/Kconfig"
source "lib/Kconfig.kgdb" source "lib/Kconfig.kgdb"

View File

@ -2,8 +2,7 @@
* Copyright 2006, Red Hat, Inc., Dave Jones * Copyright 2006, Red Hat, Inc., Dave Jones
* Released under the General Public License (GPL). * Released under the General Public License (GPL).
* *
* This file contains the linked list implementations for * This file contains the linked list validation for DEBUG_LIST.
* DEBUG_LIST.
*/ */
#include <linux/export.h> #include <linux/export.h>
@ -13,88 +12,48 @@
#include <linux/rculist.h> #include <linux/rculist.h>
/* /*
* Insert a new entry between two known consecutive entries. * Check that the data structures for the list manipulations are reasonably
* * valid. Failures here indicate memory corruption (and possibly an exploit
* This is only for internal list manipulation where we know * attempt).
* the prev/next entries already!
*/ */
void __list_add(struct list_head *new, bool __list_add_valid(struct list_head *new, struct list_head *prev,
struct list_head *prev, struct list_head *next)
struct list_head *next)
{ {
WARN(next->prev != prev, CHECK_DATA_CORRUPTION(next->prev != prev,
"list_add corruption. next->prev should be " "list_add corruption. next->prev should be prev (%p), but was %p. (next=%p).\n",
"prev (%p), but was %p. (next=%p).\n",
prev, next->prev, next); prev, next->prev, next);
WARN(prev->next != next, CHECK_DATA_CORRUPTION(prev->next != next,
"list_add corruption. prev->next should be " "list_add corruption. prev->next should be next (%p), but was %p. (prev=%p).\n",
"next (%p), but was %p. (prev=%p).\n",
next, prev->next, prev); next, prev->next, prev);
WARN(new == prev || new == next, CHECK_DATA_CORRUPTION(new == prev || new == next,
"list_add double add: new=%p, prev=%p, next=%p.\n", "list_add double add: new=%p, prev=%p, next=%p.\n",
new, prev, next); new, prev, next);
next->prev = new;
new->next = next;
new->prev = prev;
WRITE_ONCE(prev->next, new);
}
EXPORT_SYMBOL(__list_add);
void __list_del_entry(struct list_head *entry) return true;
}
EXPORT_SYMBOL(__list_add_valid);
bool __list_del_entry_valid(struct list_head *entry)
{ {
struct list_head *prev, *next; struct list_head *prev, *next;
prev = entry->prev; prev = entry->prev;
next = entry->next; next = entry->next;
if (WARN(next == LIST_POISON1, CHECK_DATA_CORRUPTION(next == LIST_POISON1,
"list_del corruption, %p->next is LIST_POISON1 (%p)\n", "list_del corruption, %p->next is LIST_POISON1 (%p)\n",
entry, LIST_POISON1) || entry, LIST_POISON1);
WARN(prev == LIST_POISON2, CHECK_DATA_CORRUPTION(prev == LIST_POISON2,
"list_del corruption, %p->prev is LIST_POISON2 (%p)\n", "list_del corruption, %p->prev is LIST_POISON2 (%p)\n",
entry, LIST_POISON2) || entry, LIST_POISON2);
WARN(prev->next != entry, CHECK_DATA_CORRUPTION(prev->next != entry,
"list_del corruption. prev->next should be %p, " "list_del corruption. prev->next should be %p, but was %p\n",
"but was %p\n", entry, prev->next) || entry, prev->next);
WARN(next->prev != entry, CHECK_DATA_CORRUPTION(next->prev != entry,
"list_del corruption. next->prev should be %p, " "list_del corruption. next->prev should be %p, but was %p\n",
"but was %p\n", entry, next->prev)) entry, next->prev);
return; return true;
__list_del(prev, next);
} }
EXPORT_SYMBOL(__list_del_entry); EXPORT_SYMBOL(__list_del_entry_valid);
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty on entry does not return true after this, the entry is
* in an undefined state.
*/
void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
EXPORT_SYMBOL(list_del);
/*
* RCU variants.
*/
void __list_add_rcu(struct list_head *new,
struct list_head *prev, struct list_head *next)
{
WARN(next->prev != prev,
"list_add_rcu corruption. next->prev should be prev (%p), but was %p. (next=%p).\n",
prev, next->prev, next);
WARN(prev->next != next,
"list_add_rcu corruption. prev->next should be next (%p), but was %p. (prev=%p).\n",
next, prev->next, prev);
new->next = next;
new->prev = prev;
rcu_assign_pointer(list_next_rcu(prev), new);
next->prev = new;
}
EXPORT_SYMBOL(__list_add_rcu);

View File

@ -1,6 +1,4 @@
initrd initrd
linux-2.6
b[0-9]* b[0-9]*
rcu-test-image
res res
*.swp *.swp

View File

@ -303,6 +303,7 @@ then
fi fi
___EOF___ ___EOF___
awk < $T/cfgcpu.pack \ awk < $T/cfgcpu.pack \
-v TORTURE_BUILDONLY="$TORTURE_BUILDONLY" \
-v CONFIGDIR="$CONFIGFRAG/" \ -v CONFIGDIR="$CONFIGFRAG/" \
-v KVM="$KVM" \ -v KVM="$KVM" \
-v ncpus=$cpus \ -v ncpus=$cpus \
@ -375,6 +376,10 @@ function dump(first, pastlast, batchnum)
njitter = ncpus; njitter = ncpus;
else else
njitter = ja[1]; njitter = ja[1];
if (TORTURE_BUILDONLY && njitter != 0) {
njitter = 0;
print "echo Build-only run, so suppressing jitter >> " rd "/log"
}
for (j = 0; j < njitter; j++) for (j = 0; j < njitter; j++)
print "jitter.sh " j " " dur " " ja[2] " " ja[3] "&" print "jitter.sh " j " " dur " " ja[2] " " ja[3] "&"
print "wait" print "wait"