Blame SOURCES/0006-ppc64-handle-backtrace-when-CPU-is-in-an-emergency-s.patch

56ae9b
From f256095c61355d8db11502709ab3a084343f2bec Mon Sep 17 00:00:00 2001
56ae9b
From: Hari Bathini <hbathini@linux.ibm.com>
56ae9b
Date: Mon, 4 Jul 2022 10:55:44 +0530
56ae9b
Subject: [PATCH 06/28] ppc64: handle backtrace when CPU is in an emergency
56ae9b
 stack
56ae9b
56ae9b
A CPU could be in an emergency stack when it is running in real mode
56ae9b
or any special scenario like TM bad thing. Also, there are dedicated
56ae9b
emergency stacks for machine check and system reset interrupt. Right
56ae9b
now, no backtrace is provided if a CPU is in any of these stacks.
56ae9b
This change ensures backtrace is processed appropriately even when
56ae9b
a CPU is in any one of these emergency stacks. Also, if stack info
56ae9b
cannot be found, print that message always instead of only when
56ae9b
verbose logs are enabled.
56ae9b
56ae9b
Related kernel commits:
56ae9b
729b0f715371 ("powerpc/book3s: Introduce exclusive emergency stack for machine check exception.")
56ae9b
b1ee8a3de579 ("powerpc/64s: Dedicated system reset interrupt stack")
56ae9b
56ae9b
Signed-off-by: Hari Bathini <hbathini@linux.ibm.com>
56ae9b
Signed-off-by: Lianbo Jiang <lijiang@redhat.com>
56ae9b
---
56ae9b
 defs.h  |  12 ++++
56ae9b
 ppc64.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++----
56ae9b
 2 files changed, 203 insertions(+), 12 deletions(-)
56ae9b
56ae9b
diff --git a/defs.h b/defs.h
56ae9b
index d8fbeb89e335..6a1b6f8a16a8 100644
56ae9b
--- a/defs.h
56ae9b
+++ b/defs.h
56ae9b
@@ -6296,6 +6296,13 @@ struct ppc64_elf_prstatus {
56ae9b
 
56ae9b
 #ifdef PPC64
56ae9b
 
56ae9b
+enum emergency_stack_type {
56ae9b
+	NONE_STACK		= 0,
56ae9b
+	EMERGENCY_STACK,
56ae9b
+	NMI_EMERGENCY_STACK,
56ae9b
+	MC_EMERGENCY_STACK
56ae9b
+};
56ae9b
+
56ae9b
 struct ppc64_opal {
56ae9b
 	uint64_t base;
56ae9b
 	uint64_t entry;
56ae9b
@@ -6315,6 +6322,11 @@ struct machine_specific {
56ae9b
         char *hwstackbuf;
56ae9b
         uint hwstacksize;
56ae9b
 
56ae9b
+	/* Emergency stacks */
56ae9b
+	ulong *emergency_sp;
56ae9b
+	ulong *nmi_emergency_sp;
56ae9b
+	ulong *mc_emergency_sp;
56ae9b
+
56ae9b
 	uint l4_index_size;
56ae9b
 	uint l3_index_size;
56ae9b
 	uint l2_index_size;
56ae9b
diff --git a/ppc64.c b/ppc64.c
56ae9b
index 0a3aa5f7af91..03047a85955d 100644
56ae9b
--- a/ppc64.c
56ae9b
+++ b/ppc64.c
56ae9b
@@ -48,6 +48,10 @@ static ulong ppc64_get_stackbase(ulong);
56ae9b
 static ulong ppc64_get_stacktop(ulong);
56ae9b
 void ppc64_compiler_warning_stub(void);
56ae9b
 static ulong ppc64_in_irqstack(ulong);
56ae9b
+static enum emergency_stack_type ppc64_in_emergency_stack(int cpu, ulong addr,
56ae9b
+							  bool verbose);
56ae9b
+static void ppc64_set_bt_emergency_stack(enum emergency_stack_type type,
56ae9b
+					 struct bt_info *bt);
56ae9b
 static char * ppc64_check_eframe(struct ppc64_pt_regs *);
56ae9b
 static void ppc64_print_eframe(char *, struct ppc64_pt_regs *, 
56ae9b
 		struct bt_info *);
56ae9b
@@ -56,6 +60,7 @@ static int ppc64_paca_percpu_offset_init(int);
56ae9b
 static void ppc64_init_cpu_info(void);
56ae9b
 static int ppc64_get_cpu_map(void);
56ae9b
 static void ppc64_clear_machdep_cache(void);
56ae9b
+static void ppc64_init_paca_info(void);
56ae9b
 static void ppc64_vmemmap_init(void);
56ae9b
 static int ppc64_get_kvaddr_ranges(struct vaddr_range *);
56ae9b
 static uint get_ptetype(ulong pte);
56ae9b
@@ -692,6 +697,8 @@ ppc64_init(int when)
56ae9b
 				error(FATAL, "cannot malloc hwirqstack buffer space.");
56ae9b
 		}
56ae9b
 
56ae9b
+		ppc64_init_paca_info();
56ae9b
+
56ae9b
 		if (!machdep->hz) {
56ae9b
 			machdep->hz = HZ;
56ae9b
 			if (THIS_KERNEL_VERSION >= LINUX(2,6,0))
56ae9b
@@ -1204,6 +1211,70 @@ ppc64_kvtop(struct task_context *tc, ulong kvaddr,
56ae9b
 		return ppc64_vtop(kvaddr, (ulong *)vt->kernel_pgd[0], paddr, verbose);
56ae9b
 }
56ae9b
 
56ae9b
+static void
56ae9b
+ppc64_init_paca_info(void)
56ae9b
+{
56ae9b
+	struct machine_specific *ms = machdep->machspec;
56ae9b
+	ulong *paca_ptr;
56ae9b
+	int i;
56ae9b
+
56ae9b
+	if (!(paca_ptr = (ulong *)calloc(kt->cpus, sizeof(ulong))))
56ae9b
+		error(FATAL, "cannot malloc paca pointers space.\n");
56ae9b
+
56ae9b
+	/* Get paca pointers for all CPUs. */
56ae9b
+	if (symbol_exists("paca_ptrs")) {
56ae9b
+		ulong paca_loc;
56ae9b
+
56ae9b
+		readmem(symbol_value("paca_ptrs"), KVADDR, &paca_loc, sizeof(void *),
56ae9b
+			"paca double pointer", FAULT_ON_ERROR);
56ae9b
+		readmem(paca_loc, KVADDR, paca_ptr, sizeof(void *) * kt->cpus,
56ae9b
+			"paca pointers", FAULT_ON_ERROR);
56ae9b
+	} else if (symbol_exists("paca") &&
56ae9b
+		   (get_symbol_type("paca", NULL, NULL) == TYPE_CODE_PTR)) {
56ae9b
+		readmem(symbol_value("paca"), KVADDR, paca_ptr, sizeof(void *) * kt->cpus,
56ae9b
+			"paca pointers", FAULT_ON_ERROR);
56ae9b
+	} else {
56ae9b
+		free(paca_ptr);
56ae9b
+		return;
56ae9b
+	}
56ae9b
+
56ae9b
+	/* Initialize emergency stacks info. */
56ae9b
+	if (MEMBER_EXISTS("paca_struct", "emergency_sp")) {
56ae9b
+		ulong offset = MEMBER_OFFSET("paca_struct", "emergency_sp");
56ae9b
+
56ae9b
+		if (!(ms->emergency_sp = (ulong *)calloc(kt->cpus, sizeof(ulong))))
56ae9b
+			error(FATAL, "cannot malloc emergency stack space.\n");
56ae9b
+		for (i = 0; i < kt->cpus; i++)
56ae9b
+			readmem(paca_ptr[i] + offset, KVADDR, &ms->emergency_sp[i],
56ae9b
+				sizeof(void *), "paca->emergency_sp",
56ae9b
+				FAULT_ON_ERROR);
56ae9b
+	}
56ae9b
+
56ae9b
+	if (MEMBER_EXISTS("paca_struct", "nmi_emergency_sp")) {
56ae9b
+		ulong offset = MEMBER_OFFSET("paca_struct", "nmi_emergency_sp");
56ae9b
+
56ae9b
+		if (!(ms->nmi_emergency_sp = (ulong *)calloc(kt->cpus, sizeof(ulong))))
56ae9b
+			error(FATAL, "cannot malloc NMI emergency stack space.\n");
56ae9b
+		for (i = 0; i < kt->cpus; i++)
56ae9b
+			readmem(paca_ptr[i] + offset, KVADDR, &ms->nmi_emergency_sp[i],
56ae9b
+				sizeof(void *), "paca->nmi_emergency_sp",
56ae9b
+				FAULT_ON_ERROR);
56ae9b
+	}
56ae9b
+
56ae9b
+	if (MEMBER_EXISTS("paca_struct", "mc_emergency_sp")) {
56ae9b
+		ulong offset = MEMBER_OFFSET("paca_struct", "mc_emergency_sp");
56ae9b
+
56ae9b
+		if (!(ms->mc_emergency_sp = (ulong *)calloc(kt->cpus, sizeof(ulong))))
56ae9b
+			error(FATAL, "cannot malloc machine check emergency stack space.\n");
56ae9b
+		for (i = 0; i < kt->cpus; i++)
56ae9b
+			readmem(paca_ptr[i] + offset, KVADDR, &ms->mc_emergency_sp[i],
56ae9b
+				sizeof(void *), "paca->mc_emergency_sp",
56ae9b
+				FAULT_ON_ERROR);
56ae9b
+	}
56ae9b
+
56ae9b
+	free(paca_ptr);
56ae9b
+}
56ae9b
+
56ae9b
 /*
56ae9b
  *  Verify that the kernel has made the vmemmap list available,
56ae9b
  *  and if so, stash the relevant data required to make vtop
56ae9b
@@ -1755,6 +1826,11 @@ ppc64_eframe_search(struct bt_info *bt_in)
56ae9b
 			addr = bt->stackbase +
56ae9b
 				roundup(SIZE(thread_info), sizeof(ulong));
56ae9b
 		} else if (!INSTACK(addr, bt)) {
56ae9b
+			enum emergency_stack_type estype;
56ae9b
+
56ae9b
+			if ((estype = ppc64_in_emergency_stack(bt->tc->processor, addr, false)))
56ae9b
+				ppc64_set_bt_emergency_stack(estype, bt);
56ae9b
+
56ae9b
 			/*
56ae9b
 			 * If the user specified SP is in HW interrupt stack
56ae9b
 			 * (only for tasks running on other CPUs and in 2.4
56ae9b
@@ -1856,6 +1932,84 @@ ppc64_in_irqstack(ulong addr)
56ae9b
 	return 0;
56ae9b
 }
56ae9b
 
56ae9b
+/*
56ae9b
+ * Check if the CPU is running in any of its emergency stacks.
56ae9b
+ * Returns
56ae9b
+ *	NONE_STACK          : if input is invalid or addr is not within any emergency stack.
56ae9b
+ *	EMERGENCY_STACK     : if the addr is within emergency stack.
56ae9b
+ *	NMI_EMERGENCY_STACK : if the addr is within NMI emergency stack.
56ae9b
+ *	MC_EMERGENCY_STACK  : if the addr is within machine check emergency stack.
56ae9b
+ */
56ae9b
+static enum emergency_stack_type
56ae9b
+ppc64_in_emergency_stack(int cpu, ulong addr, bool verbose)
56ae9b
+{
56ae9b
+	struct machine_specific *ms = machdep->machspec;
56ae9b
+	ulong base, top;
56ae9b
+
56ae9b
+	if (cpu < 0  || cpu >= kt->cpus)
56ae9b
+		return NONE_STACK;
56ae9b
+
56ae9b
+	if (ms->emergency_sp) {
56ae9b
+		top = ms->emergency_sp[cpu];
56ae9b
+		base =  top - STACKSIZE();
56ae9b
+		if (addr >= base && addr < top) {
56ae9b
+			if (verbose)
56ae9b
+				fprintf(fp, "---<Emergency Stack>---\n");
56ae9b
+			return EMERGENCY_STACK;
56ae9b
+		}
56ae9b
+	}
56ae9b
+
56ae9b
+	if (ms->nmi_emergency_sp) {
56ae9b
+		top = ms->nmi_emergency_sp[cpu];
56ae9b
+		base = top - STACKSIZE();
56ae9b
+		if (addr >= base && addr < top) {
56ae9b
+			if (verbose)
56ae9b
+				fprintf(fp, "---<NMI Emergency Stack>---\n");
56ae9b
+			return NMI_EMERGENCY_STACK;
56ae9b
+		}
56ae9b
+	}
56ae9b
+
56ae9b
+	if (ms->mc_emergency_sp) {
56ae9b
+		top = ms->mc_emergency_sp[cpu];
56ae9b
+		base =  top - STACKSIZE();
56ae9b
+		if (addr >= base && addr < top) {
56ae9b
+			if (verbose)
56ae9b
+				fprintf(fp, "---<Machine Check Emergency Stack>---\n");
56ae9b
+			return MC_EMERGENCY_STACK;
56ae9b
+		}
56ae9b
+	}
56ae9b
+
56ae9b
+	return NONE_STACK;
56ae9b
+}
56ae9b
+
56ae9b
+static void
56ae9b
+ppc64_set_bt_emergency_stack(enum emergency_stack_type type, struct bt_info *bt)
56ae9b
+{
56ae9b
+	struct machine_specific *ms = machdep->machspec;
56ae9b
+	ulong top;
56ae9b
+
56ae9b
+	switch (type) {
56ae9b
+	case EMERGENCY_STACK:
56ae9b
+		top = ms->emergency_sp[bt->tc->processor];
56ae9b
+		break;
56ae9b
+	case NMI_EMERGENCY_STACK:
56ae9b
+		top = ms->nmi_emergency_sp[bt->tc->processor];
56ae9b
+		break;
56ae9b
+	case MC_EMERGENCY_STACK:
56ae9b
+		top = ms->mc_emergency_sp[bt->tc->processor];
56ae9b
+		break;
56ae9b
+	default:
56ae9b
+		top = 0;
56ae9b
+		break;
56ae9b
+	}
56ae9b
+
56ae9b
+	if (top) {
56ae9b
+		bt->stackbase = top - STACKSIZE();
56ae9b
+		bt->stacktop = top;
56ae9b
+		alter_stackbuf(bt);
56ae9b
+	}
56ae9b
+}
56ae9b
+
56ae9b
 /*
56ae9b
  *  Unroll a kernel stack.
56ae9b
  */
56ae9b
@@ -1936,10 +2090,13 @@ ppc64_back_trace_cmd(struct bt_info *bt)
56ae9b
 static void
56ae9b
 ppc64_back_trace(struct gnu_request *req, struct bt_info *bt)
56ae9b
 {
56ae9b
-	int frame = 0;
56ae9b
-	ulong lr = 0; /* hack...need to pass in initial lr reg */
56ae9b
+	enum emergency_stack_type estype;
56ae9b
 	ulong newpc = 0, newsp, marker;
56ae9b
+	int c = bt->tc->processor;
56ae9b
+	ulong nmi_sp = 0;
56ae9b
 	int eframe_found;
56ae9b
+	int frame = 0;
56ae9b
+	ulong lr = 0; /* hack...need to pass in initial lr reg */
56ae9b
 
56ae9b
 	if (!INSTACK(req->sp, bt)) {
56ae9b
 		ulong irqstack;
56ae9b
@@ -1949,6 +2106,10 @@ ppc64_back_trace(struct gnu_request *req, struct bt_info *bt)
56ae9b
 			bt->stackbase = irqstack;
56ae9b
 			bt->stacktop = bt->stackbase + STACKSIZE();
56ae9b
 			alter_stackbuf(bt);
56ae9b
+		} else if ((estype = ppc64_in_emergency_stack(c, req->sp, true))) {
56ae9b
+			if (estype == NMI_EMERGENCY_STACK)
56ae9b
+				nmi_sp = req->sp;
56ae9b
+			ppc64_set_bt_emergency_stack(estype, bt);
56ae9b
 		} else if (ms->hwintrstack) {
56ae9b
 			bt->stacktop = ms->hwintrstack[bt->tc->processor] +
56ae9b
 				sizeof(ulong);
56ae9b
@@ -1957,9 +2118,7 @@ ppc64_back_trace(struct gnu_request *req, struct bt_info *bt)
56ae9b
 			bt->stackbuf = ms->hwstackbuf;
56ae9b
 			alter_stackbuf(bt);
56ae9b
 		} else {
56ae9b
-			if (CRASHDEBUG(1)) {
56ae9b
-				fprintf(fp, "cannot find the stack info.\n");
56ae9b
-			}
56ae9b
+			fprintf(fp, "cannot find the stack info.\n");
56ae9b
 			return;
56ae9b
 		}
56ae9b
 	}
56ae9b
@@ -1989,13 +2148,20 @@ ppc64_back_trace(struct gnu_request *req, struct bt_info *bt)
56ae9b
 				newsp = 
56ae9b
 				*(ulong *)&bt->stackbuf[newsp - bt->stackbase];
56ae9b
 			if (!INSTACK(newsp, bt)) {
56ae9b
-                        	/*
56ae9b
-                         	* Switch HW interrupt stack to process's stack.
56ae9b
-                         	*/
56ae9b
-                        	bt->stackbase = GET_STACKBASE(bt->task);
56ae9b
-                        	bt->stacktop = GET_STACKTOP(bt->task);
56ae9b
-                        	alter_stackbuf(bt);
56ae9b
-                	}  
56ae9b
+				if ((estype = ppc64_in_emergency_stack(c, newsp, true))) {
56ae9b
+					if (!nmi_sp && estype == NMI_EMERGENCY_STACK)
56ae9b
+						nmi_sp = newsp;
56ae9b
+					ppc64_set_bt_emergency_stack(estype, bt);
56ae9b
+				} else {
56ae9b
+					/*
56ae9b
+					 * Switch HW interrupt stack or emergency stack
56ae9b
+					 * to process's stack.
56ae9b
+					 */
56ae9b
+					bt->stackbase = GET_STACKBASE(bt->task);
56ae9b
+					bt->stacktop = GET_STACKTOP(bt->task);
56ae9b
+					alter_stackbuf(bt);
56ae9b
+				}
56ae9b
+			}
56ae9b
 			if (IS_KVADDR(newsp) && INSTACK(newsp, bt))
56ae9b
 				newpc = *(ulong *)&bt->stackbuf[newsp + 16 -
56ae9b
 						bt->stackbase];
56ae9b
@@ -2039,6 +2205,16 @@ ppc64_back_trace(struct gnu_request *req, struct bt_info *bt)
56ae9b
 			} 
56ae9b
 		}
56ae9b
 
56ae9b
+		/*
56ae9b
+		 * NMI stack may not be re-entrant. In so, an SP in the NMI stack
56ae9b
+		 * is likely to point back to an SP within the NMI stack, in case
56ae9b
+		 * of a nested NMI.
56ae9b
+		 */
56ae9b
+		if (nmi_sp && nmi_sp == newsp) {
56ae9b
+			fprintf(fp, "---<Nested NMI>---\n");
56ae9b
+			break;
56ae9b
+		}
56ae9b
+
56ae9b
 		/*
56ae9b
 		 * Some Linux 3.7 kernel threads have been seen to have
56ae9b
 		 * their end-of-trace stack linkage pointer pointing
56ae9b
@@ -2416,6 +2592,9 @@ ppc64_get_dumpfile_stack_frame(struct bt_info *bt_in, ulong *nip, ulong *ksp)
56ae9b
 		pt_regs = (struct ppc64_pt_regs *)bt->machdep;
56ae9b
 		ur_nip = pt_regs->nip;
56ae9b
 		ur_ksp = pt_regs->gpr[1];
56ae9b
+		/* Print the collected regs for panic task. */
56ae9b
+		ppc64_print_regs(pt_regs);
56ae9b
+		ppc64_print_nip_lr(pt_regs, 1);
56ae9b
 	} else if ((pc->flags & KDUMP) ||
56ae9b
 		   ((pc->flags & DISKDUMP) &&
56ae9b
 		    (*diskdump_flags & KDUMP_CMPRS_LOCAL))) {
56ae9b
-- 
56ae9b
2.37.1
56ae9b