aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul E. McKenney <paul.mckenney@linaro.org>2011-10-02 10:44:32 -0400
committerPaul E. McKenney <paulmck@linux.vnet.ibm.com>2011-12-11 13:31:26 -0500
commit91afaf300269aa99a4d646969b3258b74294ac4d (patch)
treeb4d7dd5f5c9933be7873b206624f7b55eb25d906
parenta8eecf2248a45bf69f0625b23c003ad2ccd765ee (diff)
rcu: Add failure tracing to rcutorture
Trace the rcutorture RCU accesses and dump the trace buffer when the first failure is detected. 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>
-rw-r--r--include/linux/rcupdate.h8
-rw-r--r--include/trace/events/rcu.h26
-rw-r--r--kernel/rcupdate.c10
-rw-r--r--kernel/rcutorture.c18
4 files changed, 62 insertions, 0 deletions
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index cd1ad4b04c6d..8d315b013e37 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -51,6 +51,8 @@ extern int rcutorture_runnable; /* for sysctl */
51#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) 51#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
52extern void rcutorture_record_test_transition(void); 52extern void rcutorture_record_test_transition(void);
53extern void rcutorture_record_progress(unsigned long vernum); 53extern void rcutorture_record_progress(unsigned long vernum);
54extern void do_trace_rcu_torture_read(char *rcutorturename,
55 struct rcu_head *rhp);
54#else 56#else
55static inline void rcutorture_record_test_transition(void) 57static inline void rcutorture_record_test_transition(void)
56{ 58{
@@ -58,6 +60,12 @@ static inline void rcutorture_record_test_transition(void)
58static inline void rcutorture_record_progress(unsigned long vernum) 60static inline void rcutorture_record_progress(unsigned long vernum)
59{ 61{
60} 62}
63#ifdef CONFIG_RCU_TRACE
64extern void do_trace_rcu_torture_read(char *rcutorturename,
65 struct rcu_head *rhp);
66#else
67#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)
68#endif
61#endif 69#endif
62 70
63#define UINT_CMP_GE(a, b) (UINT_MAX / 2 >= (a) - (b)) 71#define UINT_CMP_GE(a, b) (UINT_MAX / 2 >= (a) - (b))
diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h
index e5771804c507..172620a92b1a 100644
--- a/include/trace/events/rcu.h
+++ b/include/trace/events/rcu.h
@@ -437,6 +437,31 @@ TRACE_EVENT(rcu_batch_end,
437 __entry->rcuname, __entry->callbacks_invoked) 437 __entry->rcuname, __entry->callbacks_invoked)
438); 438);
439 439
440/*
441 * Tracepoint for rcutorture readers. The first argument is the name
442 * of the RCU flavor from rcutorture's viewpoint and the second argument
443 * is the callback address.
444 */
445TRACE_EVENT(rcu_torture_read,
446
447 TP_PROTO(char *rcutorturename, struct rcu_head *rhp),
448
449 TP_ARGS(rcutorturename, rhp),
450
451 TP_STRUCT__entry(
452 __field(char *, rcutorturename)
453 __field(struct rcu_head *, rhp)
454 ),
455
456 TP_fast_assign(
457 __entry->rcutorturename = rcutorturename;
458 __entry->rhp = rhp;
459 ),
460
461 TP_printk("%s torture read %p",
462 __entry->rcutorturename, __entry->rhp)
463);
464
440#else /* #ifdef CONFIG_RCU_TRACE */ 465#else /* #ifdef CONFIG_RCU_TRACE */
441 466
442#define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0) 467#define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0)
@@ -452,6 +477,7 @@ TRACE_EVENT(rcu_batch_end,
452#define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0) 477#define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0)
453#define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0) 478#define trace_rcu_invoke_kfree_callback(rcuname, rhp, offset) do { } while (0)
454#define trace_rcu_batch_end(rcuname, callbacks_invoked) do { } while (0) 479#define trace_rcu_batch_end(rcuname, callbacks_invoked) do { } while (0)
480#define trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)
455 481
456#endif /* #else #ifdef CONFIG_RCU_TRACE */ 482#endif /* #else #ifdef CONFIG_RCU_TRACE */
457 483
diff --git a/kernel/rcupdate.c b/kernel/rcupdate.c
index c5b98e565aee..92e771d7b44b 100644
--- a/kernel/rcupdate.c
+++ b/kernel/rcupdate.c
@@ -316,3 +316,13 @@ struct debug_obj_descr rcuhead_debug_descr = {
316}; 316};
317EXPORT_SYMBOL_GPL(rcuhead_debug_descr); 317EXPORT_SYMBOL_GPL(rcuhead_debug_descr);
318#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */ 318#endif /* #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD */
319
320#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU) || defined(CONFIG_RCU_TRACE)
321void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp)
322{
323 trace_rcu_torture_read(rcutorturename, rhp);
324}
325EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
326#else
327#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)
328#endif
diff --git a/kernel/rcutorture.c b/kernel/rcutorture.c
index 764825c2685c..df35228e743b 100644
--- a/kernel/rcutorture.c
+++ b/kernel/rcutorture.c
@@ -913,6 +913,18 @@ rcu_torture_fakewriter(void *arg)
913 return 0; 913 return 0;
914} 914}
915 915
916void rcutorture_trace_dump(void)
917{
918 static atomic_t beenhere = ATOMIC_INIT(0);
919
920 if (atomic_read(&beenhere))
921 return;
922 if (atomic_xchg(&beenhere, 1) != 0)
923 return;
924 do_trace_rcu_torture_read(cur_ops->name, (struct rcu_head *)~0UL);
925 ftrace_dump(DUMP_ALL);
926}
927
916/* 928/*
917 * RCU torture reader from timer handler. Dereferences rcu_torture_current, 929 * RCU torture reader from timer handler. Dereferences rcu_torture_current,
918 * incrementing the corresponding element of the pipeline array. The 930 * incrementing the corresponding element of the pipeline array. The
@@ -934,6 +946,7 @@ static void rcu_torture_timer(unsigned long unused)
934 rcu_read_lock_bh_held() || 946 rcu_read_lock_bh_held() ||
935 rcu_read_lock_sched_held() || 947 rcu_read_lock_sched_held() ||
936 srcu_read_lock_held(&srcu_ctl)); 948 srcu_read_lock_held(&srcu_ctl));
949 do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
937 if (p == NULL) { 950 if (p == NULL) {
938 /* Leave because rcu_torture_writer is not yet underway */ 951 /* Leave because rcu_torture_writer is not yet underway */
939 cur_ops->readunlock(idx); 952 cur_ops->readunlock(idx);
@@ -951,6 +964,8 @@ static void rcu_torture_timer(unsigned long unused)
951 /* Should not happen, but... */ 964 /* Should not happen, but... */
952 pipe_count = RCU_TORTURE_PIPE_LEN; 965 pipe_count = RCU_TORTURE_PIPE_LEN;
953 } 966 }
967 if (pipe_count > 1)
968 rcutorture_trace_dump();
954 __this_cpu_inc(rcu_torture_count[pipe_count]); 969 __this_cpu_inc(rcu_torture_count[pipe_count]);
955 completed = cur_ops->completed() - completed; 970 completed = cur_ops->completed() - completed;
956 if (completed > RCU_TORTURE_PIPE_LEN) { 971 if (completed > RCU_TORTURE_PIPE_LEN) {
@@ -994,6 +1009,7 @@ rcu_torture_reader(void *arg)
994 rcu_read_lock_bh_held() || 1009 rcu_read_lock_bh_held() ||
995 rcu_read_lock_sched_held() || 1010 rcu_read_lock_sched_held() ||
996 srcu_read_lock_held(&srcu_ctl)); 1011 srcu_read_lock_held(&srcu_ctl));
1012 do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
997 if (p == NULL) { 1013 if (p == NULL) {
998 /* Wait for rcu_torture_writer to get underway */ 1014 /* Wait for rcu_torture_writer to get underway */
999 cur_ops->readunlock(idx); 1015 cur_ops->readunlock(idx);
@@ -1009,6 +1025,8 @@ rcu_torture_reader(void *arg)
1009 /* Should not happen, but... */ 1025 /* Should not happen, but... */
1010 pipe_count = RCU_TORTURE_PIPE_LEN; 1026 pipe_count = RCU_TORTURE_PIPE_LEN;
1011 } 1027 }
1028 if (pipe_count > 1)
1029 rcutorture_trace_dump();
1012 __this_cpu_inc(rcu_torture_count[pipe_count]); 1030 __this_cpu_inc(rcu_torture_count[pipe_count]);
1013 completed = cur_ops->completed() - completed; 1031 completed = cur_ops->completed() - completed;
1014 if (completed > RCU_TORTURE_PIPE_LEN) { 1032 if (completed > RCU_TORTURE_PIPE_LEN) {