Commit 8b6c83d3 authored by Paul Mackerras's avatar Paul Mackerras Committed by Linus Torvalds

[PATCH] ppc64: better handling of H_ENTER failures

This changes the hash insertion routines to return an error instead of
calling panic() when HV refuses to insert a HPTE (the hypervisor call to
set up a hashtable PTE is H_ENTER). 

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.
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 0b06badf
...@@ -1037,6 +1037,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB) ...@@ -1037,6 +1037,9 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB)
* interrupts if necessary. * interrupts if necessary.
*/ */
beq .ret_from_except_lite 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 * hash_page couldn't handle it, set soft interrupt enable back
* to what it was before the trap. Note that .local_irq_restore * to what it was before the trap. Note that .local_irq_restore
...@@ -1047,6 +1050,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB) ...@@ -1047,6 +1050,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB)
b 11f b 11f
#else #else
beq fast_exception_return /* Return from exception on success */ beq fast_exception_return /* Return from exception on success */
ble- 12f /* Failure return from hash_page */
/* fall through */ /* fall through */
#endif #endif
...@@ -1066,6 +1071,15 @@ _GLOBAL(handle_page_fault) ...@@ -1066,6 +1071,15 @@ _GLOBAL(handle_page_fault)
bl .bad_page_fault bl .bad_page_fault
b .ret_from_except 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 */ /* here we have a segment miss */
_GLOBAL(do_ste_alloc) _GLOBAL(do_ste_alloc)
bl .ste_allocate /* try to insert stab entry */ bl .ste_allocate /* try to insert stab entry */
......
...@@ -377,7 +377,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, ...@@ -377,7 +377,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0, lpar_rc = plpar_hcall(H_ENTER, flags, hpte_group, lhpte.dw0.dword0,
lhpte.dw1.dword1, &slot, &dummy0, &dummy1); lhpte.dw1.dword1, &slot, &dummy0, &dummy1);
if (lpar_rc == H_PTEG_Full) if (unlikely(lpar_rc == H_PTEG_Full))
return -1; return -1;
/* /*
...@@ -385,7 +385,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group, ...@@ -385,7 +385,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
* will fail. However we must catch the failure in hash_page * will fail. However we must catch the failure in hash_page
* or we will loop forever, so return -2 in this case. * or we will loop forever, so return -2 in this case.
*/ */
if (lpar_rc != H_Success) if (unlikely(lpar_rc != H_Success))
return -2; return -2;
/* Because of iSeries, we have to pass down the secondary /* Because of iSeries, we have to pass down the secondary
...@@ -415,9 +415,7 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group) ...@@ -415,9 +415,7 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
if (lpar_rc == H_Success) if (lpar_rc == H_Success)
return i; return i;
if (lpar_rc != H_Not_Found) BUG_ON(lpar_rc != H_Not_Found);
panic("Bad return code from pte remove rc = %lx\n",
lpar_rc);
slot_offset++; slot_offset++;
slot_offset &= 0x7; slot_offset &= 0x7;
...@@ -447,8 +445,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, unsigned long newpp, ...@@ -447,8 +445,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, unsigned long newpp,
if (lpar_rc == H_Not_Found) if (lpar_rc == H_Not_Found)
return -1; return -1;
if (lpar_rc != H_Success) BUG_ON(lpar_rc != H_Success);
panic("bad return code from pte protect rc = %lx\n", lpar_rc);
return 0; return 0;
} }
...@@ -467,8 +464,7 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot) ...@@ -467,8 +464,7 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot)
lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1); lpar_rc = plpar_pte_read(flags, slot, &dword0, &dummy_word1);
if (lpar_rc != H_Success) BUG_ON(lpar_rc != H_Success);
panic("Error on pte read in get_hpte0 rc = %lx\n", lpar_rc);
return dword0; return dword0;
} }
...@@ -519,15 +515,12 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp, ...@@ -519,15 +515,12 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
vpn = va >> PAGE_SHIFT; vpn = va >> PAGE_SHIFT;
slot = pSeries_lpar_hpte_find(vpn); slot = pSeries_lpar_hpte_find(vpn);
if (slot == -1) BUG_ON(slot == -1);
panic("updateboltedpp: Could not find page to bolt\n");
flags = newpp & 3; flags = newpp & 3;
lpar_rc = plpar_pte_protect(flags, slot, 0); lpar_rc = plpar_pte_protect(flags, slot, 0);
if (lpar_rc != H_Success) BUG_ON(lpar_rc != H_Success);
panic("Bad return code from pte bolted protect rc = %lx\n",
lpar_rc);
} }
static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
...@@ -546,8 +539,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va, ...@@ -546,8 +539,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
if (lpar_rc == H_Not_Found) if (lpar_rc == H_Not_Found)
return; return;
if (lpar_rc != H_Success) BUG_ON(lpar_rc != H_Success);
panic("Bad return code from invalidate rc = %lx\n", lpar_rc);
} }
/* /*
......
...@@ -278,6 +278,10 @@ htab_wrong_access: ...@@ -278,6 +278,10 @@ htab_wrong_access:
b bail b bail
htab_pte_insert_failure: 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
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <linux/ctype.h> #include <linux/ctype.h>
#include <linux/cache.h> #include <linux/cache.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/signal.h>
#include <asm/ppcdebug.h> #include <asm/ppcdebug.h>
#include <asm/processor.h> #include <asm/processor.h>
...@@ -236,14 +237,11 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap) ...@@ -236,14 +237,11 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap)
return pp; return pp;
} }
/* /* Result code is:
* Called by asm hashtable.S in case of critical insert failure * 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) int hash_page(unsigned long ea, unsigned long access, unsigned long trap)
{ {
void *pgdir; void *pgdir;
...@@ -371,6 +369,25 @@ static inline void make_bl(unsigned int *insn_addr, void *func) ...@@ -371,6 +369,25 @@ static inline void make_bl(unsigned int *insn_addr, void *func)
(unsigned long)insn_addr); (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) void __init htab_finish_init(void)
{ {
extern unsigned int *htab_call_hpte_insert1; extern unsigned int *htab_call_hpte_insert1;
......
...@@ -105,6 +105,7 @@ extern int fix_alignment(struct pt_regs *regs); ...@@ -105,6 +105,7 @@ extern int fix_alignment(struct pt_regs *regs);
extern void bad_page_fault(struct pt_regs *regs, unsigned long address, extern void bad_page_fault(struct pt_regs *regs, unsigned long address,
int sig); int sig);
extern void show_regs(struct pt_regs * regs); 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 int die(const char *str, struct pt_regs *regs, long err);
extern void flush_instruction_cache(void); extern void flush_instruction_cache(void);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment