[RFC][PATCH] ppc64: better handling of H_ENTER failures

Benjamin Herrenschmidt benh at kernel.crashing.org
Fri Jul 30 15:22:32 EST 2004


Hi !

This patch changes the hash insertion routines to return an error
instead of calling panic() when HV refuses to insert a HPTE.

The error is now propagated upstream, and either bad_page_fault() is
called (kernel mode) or a SIGBUS signal is forced (user mode). Some
other panic() cases are also turned into BUG_ON.

Overall, this should provide us with better debugging data if the
problem happens, and avoids errors from userland mapping /dev/mem and
trying to use forbidden IOs (XFree ?) to bring the whole kernel down.

Any comment ?

Ben.

===== arch/ppc64/kernel/head.S 1.61 vs edited =====
--- 1.61/arch/ppc64/kernel/head.S	2004-06-17 00:46:06 -05:00
+++ edited/arch/ppc64/kernel/head.S	2004-06-23 15:03:55 -05:00
@@ -1015,6 +1015,9 @@
 	 * interrupts if necessary.
 	 */
 	beq	.ret_from_except_lite
+	/* For a hash failure, we don't bother re-enabling interrupts */
+	ble-	12f
+
 	/*
 	 * hash_page couldn't handle it, set soft interrupt enable back
 	 * to what it was before the trap.  Note that .local_irq_restore
@@ -1025,6 +1028,8 @@
 	b	11f
 #else
 	beq+	fast_exception_return   /* Return from exception on success */
+	ble-	12f			/* Failure return from hash_page */
+
 	/* fall through */
 #endif

@@ -1042,6 +1047,15 @@
 	addi	r3,r1,STACK_FRAME_OVERHEAD
 	lwz	r4,_DAR(r1)
 	bl	.bad_page_fault
+	b	.ret_from_except
+
+/* We have a page fault that hash_page could handle but HV refused
+ * the PTE insertion
+ */
+12:	bl	.save_nvgprs
+	addi	r3,r1,STACK_FRAME_OVERHEAD
+	lwz	r4,_DAR(r1)
+	bl	.low_hash_fault
 	b	.ret_from_except

 	/* here we have a segment miss */
===== arch/ppc64/kernel/pSeries_lpar.c 1.36 vs edited =====
--- 1.36/arch/ppc64/kernel/pSeries_lpar.c	2004-04-12 12:54:09 -05:00
+++ edited/arch/ppc64/kernel/pSeries_lpar.c	2004-06-23 15:00:53 -05:00
@@ -377,7 +377,7 @@
 	lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0,
 			      lhpte.dw1.dword1, &slot, &dummy0, &dummy1);

-	if (lpar_rc == H_PTEG_Full)
+	if (unlikely(lpar_rc == H_PTEG_Full))
 		return -1;

 	/*
@@ -385,8 +385,13 @@
 	 * will fail. However we must catch the failure in hash_page
 	 * or we will loop forever, so return -2 in this case.
 	 */
-	if (lpar_rc != H_Success)
+	if (unlikely(lpar_rc != H_Success)) {
+		printk(KERN_WARNING
+		       "MMU fault ! H_ENTER failed, rc: %lx, va: %016lx, prpn: %lx,"
+		       " hpteflags: %lx, secondary: %d, bolted: %d, large: %d\n",
+		       lpar_rc, va, prpn, hpteflags, secondary, bolted, large);
 		return -2;
+	}

 	/* Because of iSeries, we have to pass down the secondary
 	 * bucket bit here as well
@@ -415,9 +420,7 @@
 		if (lpar_rc == H_Success)
 			return i;

-		if (lpar_rc != H_Not_Found)
-			panic("Bad return code from pte remove rc = %lx\n",
-			      lpar_rc);
+		BUG_ON(lpar_rc != H_Not_Found);

 		slot_offset++;
 		slot_offset &= 0x7;
@@ -447,8 +450,7 @@
 	if (lpar_rc == H_Not_Found)
 		return -1;

-	if (lpar_rc != H_Success)
-		panic("bad return code from pte protect rc = %lx\n", lpar_rc);
+	BUG_ON(lpar_rc != H_Success);

 	return 0;
 }
@@ -467,8 +469,7 @@

 	lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1);

-	if (lpar_rc != H_Success)
-		panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc);
+	BUG_ON(lpar_rc != H_Success);

 	return dword0;
 }
@@ -519,15 +520,12 @@
 	vpn = va >> PAGE_SHIFT;

 	slot = pSeries_lpar_hpte_find(vpn);
-	if (slot == -1)
-		panic("updateboltedpp: Could not find page to bolt\n");
+	BUG_ON(slot == -1);

 	flags = newpp & 3;
 	lpar_rc = plpar_pte_protect(flags, slot, 0);

-	if (lpar_rc != H_Success)
-		panic("Bad return code from pte bolted protect rc = %lx\n",
-		      lpar_rc);
+	BUG_ON(lpar_rc != H_Success);
 }

 static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
@@ -546,8 +544,7 @@
 	if (lpar_rc == H_Not_Found)
 		return;

-	if (lpar_rc != H_Success)
-		panic("Bad return code from invalidate rc = %lx\n", lpar_rc);
+	BUG_ON(lpar_rc != H_Success);
 }

 /*
===== arch/ppc64/mm/hash_low.S 1.5 vs edited =====
--- 1.5/arch/ppc64/mm/hash_low.S	2004-04-12 12:54:08 -05:00
+++ edited/arch/ppc64/mm/hash_low.S	2004-06-22 16:14:22 -05:00
@@ -278,6 +278,10 @@
 	b	bail

 htab_pte_insert_failure:
-	b	.htab_insert_failure
+	/* Bail out restoring old PTE */
+	ld	r6,STK_PARM(r6)(r1)
+	std	r31,0(r6)
+	li	r3,-1
+	b	bail


===== arch/ppc64/mm/hash_utils.c 1.47 vs edited =====
--- 1.47/arch/ppc64/mm/hash_utils.c	2004-06-10 01:21:41 -05:00
+++ edited/arch/ppc64/mm/hash_utils.c	2004-06-23 15:08:29 -05:00
@@ -28,6 +28,7 @@
 #include <linux/ctype.h>
 #include <linux/cache.h>
 #include <linux/init.h>
+#include <linux/signal.h>

 #include <asm/ppcdebug.h>
 #include <asm/processor.h>
@@ -236,14 +237,11 @@
 	return pp;
 }

-/*
- * Called by asm hashtable.S in case of critical insert failure
+/* Result code is:
+ *  0 - handled
+ *  1 - normal page fault
+ * -1 - critical hash insertion error
  */
-void htab_insert_failure(void)
-{
-	panic("hash_page: pte_insert failed\n");
-}
-
 int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
 {
 	void *pgdir;
@@ -374,6 +372,25 @@
 	*insn_addr = (unsigned int)(0x48000001 | (offset & 0x03fffffc));
 	flush_icache_range((unsigned long)insn_addr, 4+
 			   (unsigned long)insn_addr);
+}
+
+/*
+ * low_hash_fault is called when we the low level hash code failed
+ * to instert a PTE due to an hypervisor error
+ */
+void low_hash_fault(struct pt_regs *regs, unsigned long address)
+{
+	if (user_mode(regs)) {
+		siginfo_t info;
+
+		info.si_signo = SIGBUS;
+		info.si_errno = 0;
+		info.si_code = BUS_ADRERR;
+		info.si_addr = (void *)address;
+		force_sig_info(SIGBUS, &info, current);
+		return;
+	}
+	bad_page_fault(regs, address, SIGBUS);
 }

 void __init htab_finish_init(void)
===== include/asm-ppc64/system.h 1.32 vs edited =====
--- 1.32/include/asm-ppc64/system.h	2004-06-20 20:15:35 -05:00
+++ edited/include/asm-ppc64/system.h	2004-06-23 15:08:01 -05:00
@@ -105,6 +105,7 @@
 extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
 			   int sig);
 extern void show_regs(struct pt_regs * regs);
+extern void low_hash_fault(struct pt_regs *regs, unsigned long address);
 extern int die(const char *str, struct pt_regs *regs, long err);

 extern void flush_instruction_cache(void);


** Sent via the linuxppc64-dev mail list. See http://lists.linuxppc.org/





More information about the Linuxppc64-dev mailing list