summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/mm/fault.c80
1 files changed, 36 insertions, 44 deletions
diff --git a/arch/arm/mm/fault.c b/arch/arm/mm/fault.c
index 29be1c018949..e25b4fd8412c 100644
--- a/arch/arm/mm/fault.c
+++ b/arch/arm/mm/fault.c
@@ -108,14 +108,15 @@ __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
*/
static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
- unsigned int fsr, int code, struct pt_regs *regs)
+ unsigned int fsr, unsigned int sig, int code,
+ struct pt_regs *regs)
{
struct siginfo si;
#ifdef CONFIG_DEBUG_USER
if (user_debug & UDBG_SEGV) {
- printk(KERN_DEBUG "%s: unhandled page fault at 0x%08lx, code 0x%03x\n",
- tsk->comm, addr, fsr);
+ printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
+ tsk->comm, sig, addr, fsr);
show_pte(tsk->mm, addr);
show_regs(regs);
}
@@ -124,11 +125,11 @@ __do_user_fault(struct task_struct *tsk, unsigned long addr,
tsk->thread.address = addr;
tsk->thread.error_code = fsr;
tsk->thread.trap_no = 14;
- si.si_signo = SIGSEGV;
+ si.si_signo = sig;
si.si_errno = 0;
si.si_code = code;
si.si_addr = (void __user *)addr;
- force_sig_info(SIGSEGV, &si, tsk);
+ force_sig_info(sig, &si, tsk);
}
void
@@ -140,7 +141,7 @@ do_bad_area(struct task_struct *tsk, struct mm_struct *mm, unsigned long addr,
* have no context to handle this fault with.
*/
if (user_mode(regs))
- __do_user_fault(tsk, addr, fsr, SEGV_MAPERR, regs);
+ __do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
else
__do_kernel_fault(mm, addr, fsr, regs);
}
@@ -201,10 +202,11 @@ survive:
goto out;
/*
- * If we are out of memory for pid1,
- * sleep for a while and retry
+ * If we are out of memory for pid1, sleep for a while and retry
*/
+ up_read(&mm->mmap_sem);
yield();
+ down_read(&mm->mmap_sem);
goto survive;
check_stack:
@@ -219,7 +221,7 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
struct task_struct *tsk;
struct mm_struct *mm;
- int fault;
+ int fault, sig, code;
tsk = current;
mm = tsk->mm;
@@ -242,55 +244,45 @@ do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
return 0;
/*
- * We had some memory, but were unable to
- * successfully fix up this page fault.
- */
- if (fault == 0)
- goto do_sigbus;
-
- /*
* If we are in kernel mode at this point, we
* have no context to handle this fault with.
*/
if (!user_mode(regs))
goto no_context;
- if (fault == VM_FAULT_OOM) {
+ switch (fault) {
+ case VM_FAULT_OOM:
/*
- * We ran out of memory, or some other thing happened to
- * us that made us unable to handle the page fault gracefully.
+ * We ran out of memory, or some other thing
+ * happened to us that made us unable to handle
+ * the page fault gracefully.
*/
printk("VM: killing process %s\n", tsk->comm);
do_exit(SIGKILL);
- } else
- __do_user_fault(tsk, addr, fsr, fault == VM_FAULT_BADACCESS ?
- SEGV_ACCERR : SEGV_MAPERR, regs);
- return 0;
+ return 0;
+ case 0:
+ /*
+ * We had some memory, but were unable to
+ * successfully fix up this page fault.
+ */
+ sig = SIGBUS;
+ code = BUS_ADRERR;
+ break;
-/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
- */
-do_sigbus:
- /*
- * Send a sigbus, regardless of whether we were in kernel
- * or user mode.
- */
- tsk->thread.address = addr;
- tsk->thread.error_code = fsr;
- tsk->thread.trap_no = 14;
- force_sig(SIGBUS, tsk);
-#ifdef CONFIG_DEBUG_USER
- if (user_debug & UDBG_BUS) {
- printk(KERN_DEBUG "%s: sigbus at 0x%08lx, pc=0x%08lx\n",
- current->comm, addr, instruction_pointer(regs));
+ default:
+ /*
+ * Something tried to access memory that
+ * isn't in our memory map..
+ */
+ sig = SIGSEGV;
+ code = fault == VM_FAULT_BADACCESS ?
+ SEGV_ACCERR : SEGV_MAPERR;
+ break;
}
-#endif
- /* Kernel mode? Handle exceptions or die */
- if (user_mode(regs))
- return 0;
+ __do_user_fault(tsk, addr, fsr, sig, code, regs);
+ return 0;
no_context:
__do_kernel_fault(mm, addr, fsr, regs);