Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 91afaf30 authored by Paul E. McKenney's avatar Paul E. McKenney Committed by Paul E. McKenney
Browse files

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: default avatarPaul E. McKenney <paul.mckenney@linaro.org>
Signed-off-by: default avatarPaul E. McKenney <paulmck@linux.vnet.ibm.com>
Reviewed-by: default avatarJosh Triplett <josh@joshtriplett.org>
parent a8eecf22
Loading
Loading
Loading
Loading
+8 −0
Original line number Original line Diff line number Diff line
@@ -51,6 +51,8 @@ extern int rcutorture_runnable; /* for sysctl */
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
#if defined(CONFIG_TREE_RCU) || defined(CONFIG_TREE_PREEMPT_RCU)
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,
				      struct rcu_head *rhp);
#else
#else
static inline void rcutorture_record_test_transition(void)
static inline void rcutorture_record_test_transition(void)
{
{
@@ -58,6 +60,12 @@ static inline void rcutorture_record_test_transition(void)
static inline void rcutorture_record_progress(unsigned long vernum)
static inline void rcutorture_record_progress(unsigned long vernum)
{
{
}
}
#ifdef CONFIG_RCU_TRACE
extern void do_trace_rcu_torture_read(char *rcutorturename,
				      struct rcu_head *rhp);
#else
#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)
#endif
#endif
#endif


#define UINT_CMP_GE(a, b)	(UINT_MAX / 2 >= (a) - (b))
#define UINT_CMP_GE(a, b)	(UINT_MAX / 2 >= (a) - (b))
+26 −0
Original line number Original line Diff line number Diff line
@@ -437,6 +437,31 @@ TRACE_EVENT(rcu_batch_end,
		  __entry->rcuname, __entry->callbacks_invoked)
		  __entry->rcuname, __entry->callbacks_invoked)
);
);


/*
 * Tracepoint for rcutorture readers.  The first argument is the name
 * of the RCU flavor from rcutorture's viewpoint and the second argument
 * is the callback address.
 */
TRACE_EVENT(rcu_torture_read,

	TP_PROTO(char *rcutorturename, struct rcu_head *rhp),

	TP_ARGS(rcutorturename, rhp),

	TP_STRUCT__entry(
		__field(char *, rcutorturename)
		__field(struct rcu_head *, rhp)
	),

	TP_fast_assign(
		__entry->rcutorturename = rcutorturename;
		__entry->rhp = rhp;
	),

	TP_printk("%s torture read %p",
		  __entry->rcutorturename, __entry->rhp)
);

#else /* #ifdef CONFIG_RCU_TRACE */
#else /* #ifdef CONFIG_RCU_TRACE */


#define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0)
#define trace_rcu_grace_period(rcuname, gpnum, gpevent) do { } while (0)
@@ -452,6 +477,7 @@ TRACE_EVENT(rcu_batch_end,
#define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0)
#define trace_rcu_invoke_callback(rcuname, rhp) do { } while (0)
#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) do { } while (0)
#define trace_rcu_batch_end(rcuname, callbacks_invoked) do { } while (0)
#define trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)


#endif /* #else #ifdef CONFIG_RCU_TRACE */
#endif /* #else #ifdef CONFIG_RCU_TRACE */


+10 −0
Original line number Original line Diff line number Diff line
@@ -316,3 +316,13 @@ struct debug_obj_descr rcuhead_debug_descr = {
};
};
EXPORT_SYMBOL_GPL(rcuhead_debug_descr);
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)
void do_trace_rcu_torture_read(char *rcutorturename, struct rcu_head *rhp)
{
	trace_rcu_torture_read(rcutorturename, rhp);
}
EXPORT_SYMBOL_GPL(do_trace_rcu_torture_read);
#else
#define do_trace_rcu_torture_read(rcutorturename, rhp) do { } while (0)
#endif
+18 −0
Original line number Original line Diff line number Diff line
@@ -913,6 +913,18 @@ rcu_torture_fakewriter(void *arg)
	return 0;
	return 0;
}
}


void rcutorture_trace_dump(void)
{
	static atomic_t beenhere = ATOMIC_INIT(0);

	if (atomic_read(&beenhere))
		return;
	if (atomic_xchg(&beenhere, 1) != 0)
		return;
	do_trace_rcu_torture_read(cur_ops->name, (struct rcu_head *)~0UL);
	ftrace_dump(DUMP_ALL);
}

/*
/*
 * RCU torture reader from timer handler.  Dereferences rcu_torture_current,
 * RCU torture reader from timer handler.  Dereferences rcu_torture_current,
 * incrementing the corresponding element of the pipeline array.  The
 * incrementing the corresponding element of the pipeline array.  The
@@ -934,6 +946,7 @@ static void rcu_torture_timer(unsigned long unused)
				  rcu_read_lock_bh_held() ||
				  rcu_read_lock_bh_held() ||
				  rcu_read_lock_sched_held() ||
				  rcu_read_lock_sched_held() ||
				  srcu_read_lock_held(&srcu_ctl));
				  srcu_read_lock_held(&srcu_ctl));
	do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
	if (p == NULL) {
	if (p == NULL) {
		/* Leave because rcu_torture_writer is not yet underway */
		/* Leave because rcu_torture_writer is not yet underway */
		cur_ops->readunlock(idx);
		cur_ops->readunlock(idx);
@@ -951,6 +964,8 @@ 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)
		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 = cur_ops->completed() - completed;
	if (completed > RCU_TORTURE_PIPE_LEN) {
	if (completed > RCU_TORTURE_PIPE_LEN) {
@@ -994,6 +1009,7 @@ rcu_torture_reader(void *arg)
					  rcu_read_lock_bh_held() ||
					  rcu_read_lock_bh_held() ||
					  rcu_read_lock_sched_held() ||
					  rcu_read_lock_sched_held() ||
					  srcu_read_lock_held(&srcu_ctl));
					  srcu_read_lock_held(&srcu_ctl));
		do_trace_rcu_torture_read(cur_ops->name, &p->rtort_rcu);
		if (p == NULL) {
		if (p == NULL) {
			/* Wait for rcu_torture_writer to get underway */
			/* Wait for rcu_torture_writer to get underway */
			cur_ops->readunlock(idx);
			cur_ops->readunlock(idx);
@@ -1009,6 +1025,8 @@ 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)
			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 = cur_ops->completed() - completed;
		if (completed > RCU_TORTURE_PIPE_LEN) {
		if (completed > RCU_TORTURE_PIPE_LEN) {