rcu: Reduce rcutorture tracing

Currently, rcutorture traces every read-side access.  This can be
problematic because even a two-minute rcutorture run on a two-CPU system
can generate 28,853,363 reads.  Normally, only a failing read is of
interest, so this commit traces adjusts rcutorture's tracing to only
trace failing reads.  The resulting event tracing records the time
and the ->completed value captured at the beginning of the RCU read-side
critical section, allowing correlation with other event-tracing messages.

Signed-off-by: Paul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: Josh Triplett <josh@joshtriplett.org>
[ paulmck: Add fix to build problem located by Randy Dunlap based on
  diagnosis by Steven Rostedt. ]
This commit is contained in:
Paul E. McKenney 2012-11-14 16:26:40 -08:00 committed by Paul E. McKenney
parent dc975e94f3
commit 5249453510
5 changed files with 55 additions and 18 deletions

View File

@ -53,7 +53,10 @@ extern int rcutorture_runnable; /* for sysctl */
extern void rcutorture_record_test_transition(void); extern void rcutorture_record_test_transition(void);
extern void rcutorture_record_progress(unsigned long vernum); extern void rcutorture_record_progress(unsigned long vernum);
extern void do_trace_rcu_torture_read(char *rcutorturename, extern void do_trace_rcu_torture_read(char *rcutorturename,
struct rcu_head *rhp); struct rcu_head *rhp,
unsigned long secs,
unsigned long c_old,
unsigned long c);
#else #else
static inline void rcutorture_record_test_transition(void) static inline void rcutorture_record_test_transition(void)
{ {
@ -63,9 +66,13 @@ static inline void rcutorture_record_progress(unsigned long vernum)
} }
#ifdef CONFIG_RCU_TRACE #ifdef CONFIG_RCU_TRACE
extern void do_trace_rcu_torture_read(char *rcutorturename, extern void do_trace_rcu_torture_read(char *rcutorturename,
struct rcu_head *rhp); struct rcu_head *rhp,
unsigned long secs,
unsigned long c_old,
unsigned long c);
#else #else
#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
do { } while (0)
#endif #endif
#endif #endif

View File

@ -523,22 +523,30 @@ TRACE_EVENT(rcu_batch_end,
*/ */
TRACE_EVENT(rcu_torture_read, TRACE_EVENT(rcu_torture_read,
TP_PROTO(char *rcutorturename, struct rcu_head *rhp), TP_PROTO(char *rcutorturename, struct rcu_head *rhp,
unsigned long secs, unsigned long c_old, unsigned long c),
TP_ARGS(rcutorturename, rhp), TP_ARGS(rcutorturename, rhp, secs, c_old, c),
TP_STRUCT__entry( TP_STRUCT__entry(
__field(char *, rcutorturename) __field(char *, rcutorturename)
__field(struct rcu_head *, rhp) __field(struct rcu_head *, rhp)
__field(unsigned long, secs)
__field(unsigned long, c_old)
__field(unsigned long, c)
), ),
TP_fast_assign( TP_fast_assign(
__entry->rcutorturename = rcutorturename; __entry->rcutorturename = rcutorturename;
__entry->rhp = rhp; __entry->rhp = rhp;
__entry->secs = secs;
__entry->c_old = c_old;
__entry->c = c;
), ),
TP_printk("%s torture read %p", TP_printk("%s torture read %p %luus c: %lu %lu",
__entry->rcutorturename, __entry->rhp) __entry->rcutorturename, __entry->rhp,
__entry->secs, __entry->c_old, __entry->c)
); );
/* /*
@ -608,7 +616,8 @@ TRACE_EVENT(rcu_barrier,
#define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0) #define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0)
#define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \ #define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \
do { } while (0) do { } while (0)
#define trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) #define trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
do { } while (0)
#define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0) #define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0)
#endif /* #else #ifdef CONFIG_RCU_TRACE */ #endif /* #else #ifdef CONFIG_RCU_TRACE */

View File

@ -404,11 +404,14 @@ EXPORT_SYMBOL_GPL(rcuhead_debug_descr);
#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ #endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE)
void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp) void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp,
unsigned long secs,
unsigned long c_old, unsigned long c)
{ {
trace_rcu_torture_read(rcutorturename, rhp); trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c);
} }
EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
#else #else
#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \
do { } while (0)
#endif #endif

View File

@ -46,6 +46,7 @@
#include <linux/stat.h> #include <linux/stat.h>
#include <linux/srcu.h> #include <linux/srcu.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/trace_clock.h>
#include <asm/byteorder.h> #include <asm/byteorder.h>
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
@ -1028,7 +1029,6 @@ void rcutorture_trace_dump(void)
return; return;
if (atomic_xchg(&beenhere, 1) != 0) if (atomic_xchg(&beenhere, 1) != 0)
return; return;
do_trace_rcu_torture_read(cur_ops->name, (struct rcu_head *)~0UL);
ftrace_dump(DUMP_ALL); ftrace_dump(DUMP_ALL);
} }
@ -1042,13 +1042,16 @@ static void rcu_torture_timer(unsigned long unused)
{ {
int idx; int idx;
int completed; int completed;
int completed_end;
static DEFINE_RCU_RANDOM(rand); static DEFINE_RCU_RANDOM(rand);
static DEFINE_SPINLOCK(rand_lock); static DEFINE_SPINLOCK(rand_lock);
struct rcu_torture *p; struct rcu_torture *p;
int pipe_count; int pipe_count;
unsigned long long ts;
idx = cur_ops->readlock(); idx = cur_ops->readlock();
completed = cur_ops->completed(); completed = cur_ops->completed();
ts = trace_clock_local();
p = rcu_dereference_check(rcu_torture_current, p = rcu_dereference_check(rcu_torture_current,
rcu_read_lock_bh_held() || rcu_read_lock_bh_held() ||
rcu_read_lock_sched_held() || rcu_read_lock_sched_held() ||
@ -1058,7 +1061,6 @@ static void rcu_torture_timer(unsigned long unused)
cur_ops->readunlock(idx); cur_ops->readunlock(idx);
return; return;
} }
do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
if (p->rtort_mbtest == 0) if (p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror); atomic_inc(&n_rcu_torture_mberror);
spin_lock(&rand_lock); spin_lock(&rand_lock);
@ -1071,10 +1073,16 @@ static void rcu_torture_timer(unsigned long unused)
/* Should not happen, but... */ /* Should not happen, but... */
pipe_count = RCU_TORTURE_PIPE_LEN; pipe_count = RCU_TORTURE_PIPE_LEN;
} }
if (pipe_count > 1) completed_end = cur_ops->completed();
if (pipe_count > 1) {
unsigned long __maybe_unused ts_rem = do_div(ts, NSEC_PER_USEC);
do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, ts,
completed, completed_end);
rcutorture_trace_dump(); rcutorture_trace_dump();
}
__this_cpu_inc(rcu_torture_count[pipe_count]); __this_cpu_inc(rcu_torture_count[pipe_count]);
completed = cur_ops->completed() - completed; completed = completed_end - completed;
if (completed > RCU_TORTURE_PIPE_LEN) { if (completed > RCU_TORTURE_PIPE_LEN) {
/* Should not happen, but... */ /* Should not happen, but... */
completed = RCU_TORTURE_PIPE_LEN; completed = RCU_TORTURE_PIPE_LEN;
@ -1094,11 +1102,13 @@ static int
rcu_torture_reader(void *arg) rcu_torture_reader(void *arg)
{ {
int completed; int completed;
int completed_end;
int idx; int idx;
DEFINE_RCU_RANDOM(rand); DEFINE_RCU_RANDOM(rand);
struct rcu_torture *p; struct rcu_torture *p;
int pipe_count; int pipe_count;
struct timer_list t; struct timer_list t;
unsigned long long ts;
VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); VERBOSE_PRINTK_STRING("rcu_torture_reader task started");
set_user_nice(current, 19); set_user_nice(current, 19);
@ -1112,6 +1122,7 @@ rcu_torture_reader(void *arg)
} }
idx = cur_ops->readlock(); idx = cur_ops->readlock();
completed = cur_ops->completed(); completed = cur_ops->completed();
ts = trace_clock_local();
p = rcu_dereference_check(rcu_torture_current, p = rcu_dereference_check(rcu_torture_current,
rcu_read_lock_bh_held() || rcu_read_lock_bh_held() ||
rcu_read_lock_sched_held() || rcu_read_lock_sched_held() ||
@ -1122,7 +1133,6 @@ rcu_torture_reader(void *arg)
schedule_timeout_interruptible(HZ); schedule_timeout_interruptible(HZ);
continue; continue;
} }
do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
if (p->rtort_mbtest == 0) if (p->rtort_mbtest == 0)
atomic_inc(&n_rcu_torture_mberror); atomic_inc(&n_rcu_torture_mberror);
cur_ops->read_delay(&rand); cur_ops->read_delay(&rand);
@ -1132,10 +1142,17 @@ rcu_torture_reader(void *arg)
/* Should not happen, but... */ /* Should not happen, but... */
pipe_count = RCU_TORTURE_PIPE_LEN; pipe_count = RCU_TORTURE_PIPE_LEN;
} }
if (pipe_count > 1) completed_end = cur_ops->completed();
if (pipe_count > 1) {
unsigned long __maybe_unused ts_rem =
do_div(ts, NSEC_PER_USEC);
do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu,
ts, completed, completed_end);
rcutorture_trace_dump(); rcutorture_trace_dump();
}
__this_cpu_inc(rcu_torture_count[pipe_count]); __this_cpu_inc(rcu_torture_count[pipe_count]);
completed = cur_ops->completed() - completed; completed = completed_end - completed;
if (completed > RCU_TORTURE_PIPE_LEN) { if (completed > RCU_TORTURE_PIPE_LEN) {
/* Should not happen, but... */ /* Should not happen, but... */
completed = RCU_TORTURE_PIPE_LEN; completed = RCU_TORTURE_PIPE_LEN;

View File

@ -1008,6 +1008,7 @@ config RCU_CPU_STALL_INFO
config RCU_TRACE config RCU_TRACE
bool "Enable tracing for RCU" bool "Enable tracing for RCU"
depends on DEBUG_KERNEL depends on DEBUG_KERNEL
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.