diff options
author | Paul E. McKenney <paul.mckenney@linaro.org> | 2011-10-02 10:44:32 -0400 |
---|---|---|
committer | Paul E. McKenney <paulmck@linux.vnet.ibm.com> | 2011-12-11 13:31:26 -0500 |
commit | 91afaf300269aa99a4d646969b3258b74294ac4d (patch) | |
tree | b4d7dd5f5c9933be7873b206624f7b55eb25d906 | |
parent | a8eecf2248a45bf69f0625b23c003ad2ccd765ee (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.h | 8 | ||||
-rw-r--r-- | include/trace/events/rcu.h | 26 | ||||
-rw-r--r-- | kernel/rcupdate.c | 10 | ||||
-rw-r--r-- | kernel/rcutorture.c | 18 |
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) |
52 | extern void rcutorture_record_test_transition(void); | 52 | extern void rcutorture_record_test_transition(void); |
53 | extern void rcutorture_record_progress(unsigned long vernum); | 53 | extern void rcutorture_record_progress(unsigned long vernum); |
54 | extern void do_trace_rcu_torture_read(char *rcutorturename, | ||
55 | struct rcu_head *rhp); | ||
54 | #else | 56 | #else |
55 | static inline void rcutorture_record_test_transition(void) | 57 | static inline void rcutorture_record_test_transition(void) |
56 | { | 58 | { |
@@ -58,6 +60,12 @@ static inline void rcutorture_record_test_transition(void) | |||
58 | static inline void rcutorture_record_progress(unsigned long vernum) | 60 | static inline void rcutorture_record_progress(unsigned long vernum) |
59 | { | 61 | { |
60 | } | 62 | } |
63 | #ifdef CONFIG_RCU_TRACE | ||
64 | extern 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 | */ | ||
445 | TRACE_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 | }; |
317 | EXPORT_SYMBOL_GPL(rcuhead_debug_descr); | 317 | EXPORT_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) | ||
321 | void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp) | ||
322 | { | ||
323 | trace_rcu_torture_read(rcutorturename, rhp); | ||
324 | } | ||
325 | EXPORT_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 | ||
916 | void 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) { |