From ee34709f4d26e65758b851985f0a030bf2fed904 Mon Sep 17 00:00:00 2001
From: "he, bo" <bo.he@intel.com>
Date: Sun, 9 Dec 2018 18:11:33 +0800
Subject: [PATCH] rcu: detect the preempt_rcu hang

Change-Id: I2c059ffe7d8b3ef8ab5f2cb246dff24a729555f1
Tracked-On:
Signed-off-by: he, bo <bo.he@intel.com>
---
 kernel/rcu/tree.c   | 34 ++++++++++++++++++++++++++++------
 kernel/rcu/update.c |  4 +++-
 2 files changed, 31 insertions(+), 7 deletions(-)

diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c
index 0b760c1..df59507 100644
--- a/kernel/rcu/tree.c
+++ b/kernel/rcu/tree.c
@@ -61,6 +61,7 @@
 #include <linux/trace_events.h>
 #include <linux/suspend.h>
 #include <linux/ftrace.h>
+#include <linux/sched/clock.h>
 
 #include "tree.h"
 #include "rcu.h"
@@ -637,11 +638,13 @@ void show_rcu_gp_kthreads(void)
 	struct rcu_state *rsp;
 
 	for_each_rcu_flavor(rsp) {
-		pr_info("%s: wait state: %d ->state: %#lx\n",
-			rsp->name, rsp->gp_state, rsp->gp_kthread->state);
+		pr_info("%s: wait state: %d ->state: %#lx gp_flags: %d rsp: gp_seq = %lu\n",
+			rsp->name, rsp->gp_state, rsp->gp_kthread->state, rsp->gp_flags, rsp->gp_seq);
 		rcu_for_each_node_breadth_first(rsp, rnp) {
-			if (ULONG_CMP_GE(rsp->gp_seq, rnp->gp_seq_needed))
+			if (ULONG_CMP_GE(rsp->gp_seq, rnp->gp_seq_needed)) {
+				pr_info("\trsp->gp_seq %lu is ge rnp->gp_seq_needed %lu\n", rsp->gp_seq, rnp->gp_seq_needed);
 				continue;
+			}
 			pr_info("\trcu_node %d:%d ->gp_seq %lu ->gp_seq_needed %lu\n",
 				rnp->grplo, rnp->grphi, rnp->gp_seq,
 				rnp->gp_seq_needed);
@@ -651,8 +654,11 @@ void show_rcu_gp_kthreads(void)
 				rdp = per_cpu_ptr(rsp->rda, cpu);
 				if (rdp->gpwrap ||
 				    ULONG_CMP_GE(rsp->gp_seq,
-						 rdp->gp_seq_needed))
+						 rdp->gp_seq_needed)) {
+					pr_info("\tcpu %d rdp->gpwrap = %d rsp->gp_seq %lu is ge rdp->gp_seq_needed %lu\n",
+							cpu, rdp->gpwrap, rsp->gp_seq, rdp->gp_seq_needed);
 					continue;
+				}
 				pr_info("\tcpu %d ->gp_seq_needed %lu\n",
 					cpu, rdp->gp_seq_needed);
 			}
@@ -2153,8 +2159,13 @@ static int __noreturn rcu_gp_kthread(void *arg)
 	int ret;
 	struct rcu_state *rsp = arg;
 	struct rcu_node *rnp = rcu_get_root(rsp);
+	pid_t rcu_preempt_pid;
 
 	rcu_bind_gp_kthread();
+	if(!strcmp(rsp->name, "rcu_preempt")) {
+		rcu_preempt_pid = rsp->gp_kthread->pid;
+	}
+
 	for (;;) {
 
 		/* Handle grace-period start. */
@@ -2163,8 +2174,19 @@ static int __noreturn rcu_gp_kthread(void *arg)
 					       READ_ONCE(rsp->gp_seq),
 					       TPS("reqwait"));
 			rsp->gp_state = RCU_GP_WAIT_GPS;
-			swait_event_idle_exclusive(rsp->gp_wq, READ_ONCE(rsp->gp_flags) &
-						     RCU_GP_FLAG_INIT);
+			if (current->pid != rcu_preempt_pid) {
+				swait_event_idle_exclusive(rsp->gp_wq, READ_ONCE(rsp->gp_flags) &
+						RCU_GP_FLAG_INIT);
+			} else {
+				ret = swait_event_idle_timeout_exclusive(rsp->gp_wq, READ_ONCE(rsp->gp_flags) &
+						RCU_GP_FLAG_INIT, 2*HZ);
+
+				if(!ret) {
+					show_rcu_gp_kthreads();
+					panic("hung_task: blocked in rcu_gp_kthread init");
+				}
+			}
+
 			rsp->gp_state = RCU_GP_DONE_GPS;
 			/* Locking provides needed memory barrier. */
 			if (rcu_gp_init(rsp))
diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c
index 81db882..63f761a 100644
--- a/kernel/rcu/update.c
+++ b/kernel/rcu/update.c
@@ -364,8 +364,10 @@ void __wait_rcu_gp(bool checktiny, int n, call_rcu_func_t *crcu_array,
 				break;
 		if (j == i) {
 			trace_printk("bobo: start wait for rcu gp\n");
-			if(!wait_for_completion_timeout(&rs_array[i].completion, 3*HZ))
+			if(!wait_for_completion_timeout(&rs_array[i].completion, 2*HZ)) {
+				show_rcu_gp_kthreads();
 				panic("hung_task: blocked tasks in rcu");
+			}
 			trace_printk("bobo: finish wait for rcu gp\n");
 
 		}
-- 
2.7.4

