rcu-tasks: Add detailed debugging facility to RCU Tasks Trace CPU stall warning

This is a debug-only commit, for the moment, anyway.

Signed-off-by: Paul E. McKenney <paulmck@kernel.org>
diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt
index ca9db80..0e384df 100644
--- a/Documentation/admin-guide/kernel-parameters.txt
+++ b/Documentation/admin-guide/kernel-parameters.txt
@@ -5030,6 +5030,10 @@
 			A change in value does not take effect until
 			the beginning of the next grace period.
 
+	rcupdate.rcu_task_trc_detail_stall= [KNL]
+			Print detailed per-task information prior to a
+			RCU Tasks Trace CPU stall warning.
+
 	rcupdate.rcu_self_test= [KNL]
 			Run the RCU early boot self tests
 
diff --git a/include/linux/sched.h b/include/linux/sched.h
index 16a7981..912851c 100644
--- a/include/linux/sched.h
+++ b/include/linux/sched.h
@@ -846,6 +846,7 @@ struct task_struct {
 	int				trc_ipi_to_cpu;
 	union rcu_special		trc_reader_special;
 	struct list_head		trc_holdout_list;
+	bool				trc_needreport;
 #endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
 
 	struct sched_info		sched_info;
diff --git a/init/init_task.c b/init/init_task.c
index 73cc8f0..6d510da 100644
--- a/init/init_task.c
+++ b/init/init_task.c
@@ -157,6 +157,7 @@ struct task_struct init_task
 	.trc_reader_nesting = 0,
 	.trc_reader_special.s = 0,
 	.trc_holdout_list = LIST_HEAD_INIT(init_task.trc_holdout_list),
+	.trc_needreport = false,
 #endif
 #ifdef CONFIG_CPUSETS
 	.mems_allowed_seq = SEQCNT_SPINLOCK_ZERO(init_task.mems_allowed_seq,
diff --git a/kernel/fork.c b/kernel/fork.c
index 9796897..f563cef 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -1811,6 +1811,7 @@ static inline void rcu_copy_process(struct task_struct *p)
 	p->trc_reader_nesting = 0;
 	p->trc_reader_special.s = 0;
 	INIT_LIST_HEAD(&p->trc_holdout_list);
+	p->trc_needreport = false;
 #endif /* #ifdef CONFIG_TASKS_TRACE_RCU */
 }
 
diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h
index 09417fe..3f7601e 100644
--- a/kernel/rcu/tasks.h
+++ b/kernel/rcu/tasks.h
@@ -161,6 +161,9 @@ module_param(rcu_task_contend_lim, int, 0444);
 static int rcu_task_collapse_lim __read_mostly = 10;
 module_param(rcu_task_collapse_lim, int, 0444);
 
+static int rcu_task_trc_detail_stall;
+module_param(rcu_task_trc_detail_stall, int, 0644);
+
 /* RCU tasks grace-period state for debugging. */
 #define RTGS_INIT		 0
 #define RTGS_WAIT_WAIT_CBS	 1
@@ -1377,11 +1380,16 @@ static void trc_wait_for_one_reader(struct task_struct *t,
 	int cpu;
 
 	// If a previous IPI is still in flight, let it complete.
-	if (smp_load_acquire(&t->trc_ipi_to_cpu) != -1) // Order IPI
+	if (smp_load_acquire(&t->trc_ipi_to_cpu) != -1) { // Order IPI
+		if (READ_ONCE(t->trc_needreport))
+			pr_info("%s(P%d/%d) IPI to task still in flight.\n", __func__, t->pid, task_cpu(t));
 		return;
+	}
 
 	// The current task had better be in a quiescent state.
 	if (t == current) {
+		if (READ_ONCE(t->trc_needreport))
+			pr_info("%s(P%d/%d) is currently running task.\n", __func__, t->pid, task_cpu(t));
 		rcu_trc_cmpxchg_need_qs(t, 0, TRC_NEED_QS_CHECKED);
 		WARN_ON_ONCE(READ_ONCE(t->trc_reader_nesting));
 		return;
@@ -1390,6 +1398,8 @@ static void trc_wait_for_one_reader(struct task_struct *t,
 	// Attempt to nail down the task for inspection.
 	get_task_struct(t);
 	if (!task_call_func(t, trc_inspect_reader, NULL)) {
+		if (READ_ONCE(t->trc_needreport))
+			pr_info("%s(P%d/%d) task_call_func() succeeded.\n", __func__, t->pid, task_cpu(t));
 		put_task_struct(t);
 		return;
 	}
@@ -1550,6 +1560,8 @@ static void check_all_holdout_tasks_trace(struct list_head *hop,
 
 	list_for_each_entry_safe(t, g, hop, trc_holdout_list) {
 		// If safe and needed, try to check the current task.
+		if (READ_ONCE(rcu_task_trc_detail_stall))
+			t->trc_needreport = needreport;
 		if (READ_ONCE(t->trc_ipi_to_cpu) == -1 &&
 		    !(rcu_ld_need_qs(t) & TRC_NEED_QS_CHECKED))
 			trc_wait_for_one_reader(t, hop);
@@ -1560,6 +1572,7 @@ static void check_all_holdout_tasks_trace(struct list_head *hop,
 			trc_del_holdout(t);
 		else if (needreport)
 			show_stalled_task_trace(t, firstreport);
+		t->trc_needreport = false;
 	}
 
 	// Re-enable CPU hotplug now that the holdout list scan has completed.