diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2012-11-14 19:26:40 -0500 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2013-01-08 17:14:55 -0500 |
commit | 52494535103986dbbf689b44d8c2c7efe2132b16 (patch) | |
tree | 88dd5a9d064608e9711c36f05b3b4aab8e255a4f | |
parent | dc975e94f322e60fa8fcc44dec1820fde4de174c (diff) |
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. ]
-rw-r--r-- | include/linux/rcupdate.h | 13 | ||||
-rw-r--r-- | include/trace/events/rcu.h | 19 | ||||
-rw-r--r-- | kernel/rcupdate.c | 9 | ||||
-rw-r--r-- | kernel/rcutorture.c | 31 | ||||
-rw-r--r-- | lib/Kconfig.debug | 1 |
5 files changed, 55 insertions, 18 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 275aa3f1062d..7f89cea596e1 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h | |||
@@ -53,7 +53,10 @@ extern int rcutorture_runnable; /* for sysctl */ | |||
53 | extern void rcutorture_record_test_transition(void); | 53 | extern void rcutorture_record_test_transition(void); |
54 | extern void rcutorture_record_progress(unsigned long vernum); | 54 | extern void rcutorture_record_progress(unsigned long vernum); |
55 | extern void do_trace_rcu_torture_read(char *rcutorturename, | 55 | extern void do_trace_rcu_torture_read(char *rcutorturename, |
56 | struct rcu_head *rhp); | 56 | struct rcu_head *rhp, |
57 | unsigned long secs, | ||
58 | unsigned long c_old, | ||
59 | unsigned long c); | ||
57 | #else | 60 | #else |
58 | static inline void rcutorture_record_test_transition(void) | 61 | static inline void rcutorture_record_test_transition(void) |
59 | { | 62 | { |
@@ -63,9 +66,13 @@ static inline void rcutorture_record_progress(unsigned long vernum) | |||
63 | } | 66 | } |
64 | #ifdef CONFIG_RCU_TRACE | 67 | #ifdef CONFIG_RCU_TRACE |
65 | extern void do_trace_rcu_torture_read(char *rcutorturename, | 68 | extern void do_trace_rcu_torture_read(char *rcutorturename, |
66 | struct rcu_head *rhp); | 69 | struct rcu_head *rhp, |
70 | unsigned long secs, | ||
71 | unsigned long c_old, | ||
72 | unsigned long c); | ||
67 | #else | 73 | #else |
68 | #define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) | 74 | #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ |
75 | do { } while (0) | ||
69 | #endif | 76 | #endif |
70 | #endif | 77 | #endif |
71 | 78 | ||
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index d4f559b1ec34..09af021c8e96 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h | |||
@@ -523,22 +523,30 @@ TRACE_EVENT(rcu_batch_end, | |||
523 | */ | 523 | */ |
524 | TRACE_EVENT(rcu_torture_read, | 524 | TRACE_EVENT(rcu_torture_read, |
525 | 525 | ||
526 | TP_PROTO(char *rcutorturename, struct rcu_head *rhp), | 526 | TP_PROTO(char *rcutorturename, struct rcu_head *rhp, |
527 | unsigned long secs, unsigned long c_old, unsigned long c), | ||
527 | 528 | ||
528 | TP_ARGS(rcutorturename, rhp), | 529 | TP_ARGS(rcutorturename, rhp, secs, c_old, c), |
529 | 530 | ||
530 | TP_STRUCT__entry( | 531 | TP_STRUCT__entry( |
531 | __field(char *, rcutorturename) | 532 | __field(char *, rcutorturename) |
532 | __field(struct rcu_head *, rhp) | 533 | __field(struct rcu_head *, rhp) |
534 | __field(unsigned long, secs) | ||
535 | __field(unsigned long, c_old) | ||
536 | __field(unsigned long, c) | ||
533 | ), | 537 | ), |
534 | 538 | ||
535 | TP_fast_assign( | 539 | TP_fast_assign( |
536 | __entry->rcutorturename = rcutorturename; | 540 | __entry->rcutorturename = rcutorturename; |
537 | __entry->rhp = rhp; | 541 | __entry->rhp = rhp; |
542 | __entry->secs = secs; | ||
543 | __entry->c_old = c_old; | ||
544 | __entry->c = c; | ||
538 | ), | 545 | ), |
539 | 546 | ||
540 | TP_printk("%s torture read %p", | 547 | TP_printk("%s torture read %p %luus c: %lu %lu", |
541 | __entry->rcutorturename, __entry->rhp) | 548 | __entry->rcutorturename, __entry->rhp, |
549 | __entry->secs, __entry->c_old, __entry->c) | ||
542 | ); | 550 | ); |
543 | 551 | ||
544 | /* | 552 | /* |
@@ -608,7 +616,8 @@ TRACE_EVENT(rcu_barrier, | |||
608 | #define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0) | 616 | #define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0) |
609 | #define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \ | 617 | #define trace_rcu_batch_end(rcuname, callbacks_invoked, cb, nr, iit, risk) \ |
610 | do { } while (0) | 618 | do { } while (0) |
611 | #define trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) | 619 | #define trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ |
620 | do { } while (0) | ||
612 | #define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0) | 621 | #define trace_rcu_barrier(name, s, cpu, cnt, done) do { } while (0) |
613 | 622 | ||
614 | #endif /* #else #ifdef CONFIG_RCU_TRACE */ | 623 | #endif /* #else #ifdef CONFIG_RCU_TRACE */ |
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c index a2cf76177b44..303359d1ca88 100644 --- a/kernel/rcupdate.c +++ b/kernel/rcupdate.c | |||
@@ -404,11 +404,14 @@ EXPORT_SYMBOL_GPL(rcuhead_debug_descr); | |||
404 | #endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ | 404 | #endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ |
405 | 405 | ||
406 | #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) | 406 | #if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE) |
407 | void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp) | 407 | void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp, |
408 | unsigned long secs, | ||
409 | unsigned long c_old, unsigned long c) | ||
408 | { | 410 | { |
409 | trace_rcu_torture_read(rcutorturename, rhp); | 411 | trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c); |
410 | } | 412 | } |
411 | EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); | 413 | EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read); |
412 | #else | 414 | #else |
413 | #define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0) | 415 | #define do_trace_rcu_torture_read(rcutorturename, rhp, secs, c_old, c) \ |
416 | do { } while (0) | ||
414 | #endif | 417 | #endif |
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c index 31dea01c85fd..a583f1ce713d 100644 --- a/kernel/rcutorture.c +++ b/kernel/rcutorture.c | |||
@@ -46,6 +46,7 @@ | |||
46 | #include <linux/stat.h> | 46 | #include <linux/stat.h> |
47 | #include <linux/srcu.h> | 47 | #include <linux/srcu.h> |
48 | #include <linux/slab.h> | 48 | #include <linux/slab.h> |
49 | #include <linux/trace_clock.h> | ||
49 | #include <asm/byteorder.h> | 50 | #include <asm/byteorder.h> |
50 | 51 | ||
51 | MODULE_LICENSE("GPL"); | 52 | MODULE_LICENSE("GPL"); |
@@ -1028,7 +1029,6 @@ void rcutorture_trace_dump(void) | |||
1028 | return; | 1029 | return; |
1029 | if (atomic_xchg(&beenhere, 1) != 0) | 1030 | if (atomic_xchg(&beenhere, 1) != 0) |
1030 | return; | 1031 | return; |
1031 | do_trace_rcu_torture_read(cur_ops->name, (struct rcu_head *)~0UL); | ||
1032 | ftrace_dump(DUMP_ALL); | 1032 | ftrace_dump(DUMP_ALL); |
1033 | } | 1033 | } |
1034 | 1034 | ||
@@ -1042,13 +1042,16 @@ static void rcu_torture_timer(unsigned long unused) | |||
1042 | { | 1042 | { |
1043 | int idx; | 1043 | int idx; |
1044 | int completed; | 1044 | int completed; |
1045 | int completed_end; | ||
1045 | static DEFINE_RCU_RANDOM(rand); | 1046 | static DEFINE_RCU_RANDOM(rand); |
1046 | static DEFINE_SPINLOCK(rand_lock); | 1047 | static DEFINE_SPINLOCK(rand_lock); |
1047 | struct rcu_torture *p; | 1048 | struct rcu_torture *p; |
1048 | int pipe_count; | 1049 | int pipe_count; |
1050 | unsigned long long ts; | ||
1049 | 1051 | ||
1050 | idx = cur_ops->readlock(); | 1052 | idx = cur_ops->readlock(); |
1051 | completed = cur_ops->completed(); | 1053 | completed = cur_ops->completed(); |
1054 | ts = trace_clock_local(); | ||
1052 | p = rcu_dereference_check(rcu_torture_current, | 1055 | p = rcu_dereference_check(rcu_torture_current, |
1053 | rcu_read_lock_bh_held() || | 1056 | rcu_read_lock_bh_held() || |
1054 | rcu_read_lock_sched_held() || | 1057 | rcu_read_lock_sched_held() || |
@@ -1058,7 +1061,6 @@ static void rcu_torture_timer(unsigned long unused) | |||
1058 | cur_ops->readunlock(idx); | 1061 | cur_ops->readunlock(idx); |
1059 | return; | 1062 | return; |
1060 | } | 1063 | } |
1061 | do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu); | ||
1062 | if (p->rtort_mbtest == 0) | 1064 | if (p->rtort_mbtest == 0) |
1063 | atomic_inc(&n_rcu_torture_mberror); | 1065 | atomic_inc(&n_rcu_torture_mberror); |
1064 | spin_lock(&rand_lock); | 1066 | spin_lock(&rand_lock); |
@@ -1071,10 +1073,16 @@ static void rcu_torture_timer(unsigned long unused) | |||
1071 | /* Should not happen, but... */ | 1073 | /* Should not happen, but... */ |
1072 | pipe_count = RCU_TORTURE_PIPE_LEN; | 1074 | pipe_count = RCU_TORTURE_PIPE_LEN; |
1073 | } | 1075 | } |
1074 | if (pipe_count > 1) | 1076 | completed_end = cur_ops->completed(); |
1077 | if (pipe_count > 1) { | ||
1078 | unsigned long __maybe_unused ts_rem = do_div(ts, NSEC_PER_USEC); | ||
1079 | |||
1080 | do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, ts, | ||
1081 | completed, completed_end); | ||
1075 | rcutorture_trace_dump(); | 1082 | rcutorture_trace_dump(); |
1083 | } | ||
1076 | __this_cpu_inc(rcu_torture_count[pipe_count]); | 1084 | __this_cpu_inc(rcu_torture_count[pipe_count]); |
1077 | completed = cur_ops->completed() - completed; | 1085 | completed = completed_end - completed; |
1078 | if (completed > RCU_TORTURE_PIPE_LEN) { | 1086 | if (completed > RCU_TORTURE_PIPE_LEN) { |
1079 | /* Should not happen, but... */ | 1087 | /* Should not happen, but... */ |
1080 | completed = RCU_TORTURE_PIPE_LEN; | 1088 | completed = RCU_TORTURE_PIPE_LEN; |
@@ -1094,11 +1102,13 @@ static int | |||
1094 | rcu_torture_reader(void *arg) | 1102 | rcu_torture_reader(void *arg) |
1095 | { | 1103 | { |
1096 | int completed; | 1104 | int completed; |
1105 | int completed_end; | ||
1097 | int idx; | 1106 | int idx; |
1098 | DEFINE_RCU_RANDOM(rand); | 1107 | DEFINE_RCU_RANDOM(rand); |
1099 | struct rcu_torture *p; | 1108 | struct rcu_torture *p; |
1100 | int pipe_count; | 1109 | int pipe_count; |
1101 | struct timer_list t; | 1110 | struct timer_list t; |
1111 | unsigned long long ts; | ||
1102 | 1112 | ||
1103 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); | 1113 | VERBOSE_PRINTK_STRING("rcu_torture_reader task started"); |
1104 | set_user_nice(current, 19); | 1114 | set_user_nice(current, 19); |
@@ -1112,6 +1122,7 @@ rcu_torture_reader(void *arg) | |||
1112 | } | 1122 | } |
1113 | idx = cur_ops->readlock(); | 1123 | idx = cur_ops->readlock(); |
1114 | completed = cur_ops->completed(); | 1124 | completed = cur_ops->completed(); |
1125 | ts = trace_clock_local(); | ||
1115 | p = rcu_dereference_check(rcu_torture_current, | 1126 | p = rcu_dereference_check(rcu_torture_current, |
1116 | rcu_read_lock_bh_held() || | 1127 | rcu_read_lock_bh_held() || |
1117 | rcu_read_lock_sched_held() || | 1128 | rcu_read_lock_sched_held() || |
@@ -1122,7 +1133,6 @@ rcu_torture_reader(void *arg) | |||
1122 | schedule_timeout_interruptible(HZ); | 1133 | schedule_timeout_interruptible(HZ); |
1123 | continue; | 1134 | continue; |
1124 | } | 1135 | } |
1125 | do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu); | ||
1126 | if (p->rtort_mbtest == 0) | 1136 | if (p->rtort_mbtest == 0) |
1127 | atomic_inc(&n_rcu_torture_mberror); | 1137 | atomic_inc(&n_rcu_torture_mberror); |
1128 | cur_ops->read_delay(&rand); | 1138 | cur_ops->read_delay(&rand); |
@@ -1132,10 +1142,17 @@ rcu_torture_reader(void *arg) | |||
1132 | /* Should not happen, but... */ | 1142 | /* Should not happen, but... */ |
1133 | pipe_count = RCU_TORTURE_PIPE_LEN; | 1143 | pipe_count = RCU_TORTURE_PIPE_LEN; |
1134 | } | 1144 | } |
1135 | if (pipe_count > 1) | 1145 | completed_end = cur_ops->completed(); |
1146 | if (pipe_count > 1) { | ||
1147 | unsigned long __maybe_unused ts_rem = | ||
1148 | do_div(ts, NSEC_PER_USEC); | ||
1149 | |||
1150 | do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu, | ||
1151 | ts, completed, completed_end); | ||
1136 | rcutorture_trace_dump(); | 1152 | rcutorture_trace_dump(); |
1153 | } | ||
1137 | __this_cpu_inc(rcu_torture_count[pipe_count]); | 1154 | __this_cpu_inc(rcu_torture_count[pipe_count]); |
1138 | completed = cur_ops->completed() - completed; | 1155 | completed = completed_end - completed; |
1139 | if (completed > RCU_TORTURE_PIPE_LEN) { | 1156 | if (completed > RCU_TORTURE_PIPE_LEN) { |
1140 | /* Should not happen, but... */ | 1157 | /* Should not happen, but... */ |
1141 | completed = RCU_TORTURE_PIPE_LEN; | 1158 | completed = RCU_TORTURE_PIPE_LEN; |
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 3a353091a903..7d83f52fbade 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug | |||
@@ -1008,6 +1008,7 @@ config RCU_CPU_STALL_INFO | |||
1008 | config RCU_TRACE | 1008 | config RCU_TRACE |
1009 | bool "Enable tracing for RCU" | 1009 | bool "Enable tracing for RCU" |
1010 | depends on DEBUG_KERNEL | 1010 | depends on DEBUG_KERNEL |
1011 | select TRACE_CLOCK | ||
1011 | help | 1012 | help |
1012 | This option provides tracing in RCU which presents stats | 1013 | This option provides tracing in RCU which presents stats |
1013 | in debugfs for debugging RCU implementation. | 1014 | in debugfs for debugging RCU implementation. |