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)
* 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
......@@ -1047,6 +1050,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_SLB)
b 11f
#else
beq fast_exception_return /* Return from exception on success */
ble- 12f /* Failure return from hash_page */
/* fall through */
#endif
......@@ -1066,6 +1071,15 @@ _GLOBAL(handle_page_fault)
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 */
_GLOBAL(do_ste_alloc)
bl .ste_allocate /* try to insert stab entry */
......
......@@ -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,
lhpte.dw1.dword1, &slot, &dummy0, &dummy1);
if (lpar_rc == H_PTEG_Full)
if (unlikely(lpar_rc == H_PTEG_Full))
return -1;
/*
......@@ -385,7 +385,7 @@ long pSeries_lpar_hpte_insert(unsigned long hpte_group,
* 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))
return -2;
/* Because of iSeries, we have to pass down the secondary
......@@ -415,9 +415,7 @@ static long pSeries_lpar_hpte_remove(unsigned long hpte_group)
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 +445,7 @@ static long pSeries_lpar_hpte_updatepp(unsigned long slot, unsigned long newpp,
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 +464,7 @@ static unsigned long pSeries_lpar_hpte_getword0(unsigned long slot)
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 +515,12 @@ static void pSeries_lpar_hpte_updateboltedpp(unsigned long newpp,
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 +539,7 @@ static void pSeries_lpar_hpte_invalidate(unsigned long slot, unsigned long va,
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);
}
/*
......
......@@ -278,6 +278,10 @@ htab_wrong_access:
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
......@@ -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 @@ unsigned int hash_page_do_lazy_icache(unsigned int pp, pte_t pte, int trap)
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;
......@@ -371,6 +369,25 @@ static inline void make_bl(unsigned int *insn_addr, void *func)
(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)
{
extern unsigned int *htab_call_hpte_insert1;
......
......@@ -105,6 +105,7 @@ extern int fix_alignment(struct pt_regs *regs);
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);
......
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