patch-2.4.0-test2 linux/arch/arm/mm/fault-common.c
Next file: linux/arch/arm/mm/init.c
Previous file: linux/arch/arm/mm/fault-armv.c
Back to the patch index
Back to the overall index
- Lines: 261
- Date:
Mon Jun 19 17:59:34 2000
- Orig file:
v2.4.0-test1/linux/arch/arm/mm/fault-common.c
- Orig date:
Wed Apr 26 16:34:06 2000
diff -u --recursive --new-file v2.4.0-test1/linux/arch/arm/mm/fault-common.c linux/arch/arm/mm/fault-common.c
@@ -16,6 +16,10 @@
{
pgd_t *pgd;
+ if (!mm)
+ mm = &init_mm;
+
+ printk(KERN_ALERT "pgd = %p\n", mm->pgd);
pgd = pgd_offset(mm, addr);
printk(KERN_ALERT "*pgd = %08lx", pgd_val(*pgd));
@@ -52,39 +56,78 @@
printk("\n");
}
-/*
- * Oops. The kernel tried to access some bad page. We'll have to
- * terminate things with extreme prejudice.
- */
-static void
-kernel_page_fault(unsigned long addr, int write_access, struct pt_regs *regs,
- struct task_struct *tsk, struct mm_struct *mm)
+static int __do_page_fault(struct mm_struct *mm, unsigned long addr, int mode, struct task_struct *tsk)
{
- char *reason;
+ struct vm_area_struct *vma;
+ int fault, mask;
+
+ vma = find_vma(mm, addr);
+ fault = -2; /* bad map area */
+ if (!vma)
+ goto out;
+ if (vma->vm_start > addr)
+ goto check_stack;
- if (addr < PAGE_SIZE)
- reason = "NULL pointer dereference";
+ /*
+ * Ok, we have a good vm_area for this
+ * memory access, so we can handle it.
+ */
+good_area:
+ if (READ_FAULT(mode)) /* read? */
+ mask = VM_READ|VM_EXEC;
else
- reason = "paging request";
+ mask = VM_WRITE;
- printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
- reason, addr);
- if (!mm)
- mm = &init_mm;
+ fault = -1; /* bad access type */
+ if (!(vma->vm_flags & mask))
+ goto out;
- printk(KERN_ALERT "pgd = %p\n", mm->pgd);
- show_pte(mm, addr);
- die("Oops", regs, write_access);
+ /*
+ * If for any reason at all we couldn't handle
+ * the fault, make sure we exit gracefully rather
+ * than endlessly redo the fault.
+ */
+survive:
+ fault = handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(mode));
- do_exit(SIGKILL);
+ /*
+ * Handle the "normal" cases first - successful and sigbus
+ */
+ switch (fault) {
+ case 2:
+ tsk->maj_flt++;
+ return fault;
+ case 1:
+ tsk->min_flt++;
+ case 0:
+ return fault;
+ }
+
+ fault = -3; /* out of memory */
+ if (tsk->pid != 1)
+ goto out;
+
+ /*
+ * If we are out of memory for pid1,
+ * sleep for a while and retry
+ */
+ tsk->policy |= SCHED_YIELD;
+ schedule();
+ goto survive;
+
+check_stack:
+ if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
+ goto good_area;
+out:
+ return fault;
}
static int do_page_fault(unsigned long addr, int mode, struct pt_regs *regs)
{
struct task_struct *tsk;
struct mm_struct *mm;
- struct vm_area_struct *vma;
unsigned long fixup;
+ int fault;
tsk = current;
mm = tsk->mm;
@@ -97,57 +140,77 @@
goto no_context;
down(&mm->mmap_sem);
- vma = find_vma(mm, addr);
- if (!vma)
- goto bad_area;
- if (vma->vm_start <= addr)
- goto good_area;
- if (!(vma->vm_flags & VM_GROWSDOWN) || expand_stack(vma, addr))
- goto bad_area;
+ fault = __do_page_fault(mm, addr, mode, tsk);
+ up(&mm->mmap_sem);
/*
- * Ok, we have a good vm_area for this memory access, so
- * we can handle it..
+ * Handle the "normal" case first
*/
-good_area:
- if (READ_FAULT(mode)) { /* read? */
- if (!(vma->vm_flags & (VM_READ|VM_EXEC)))
- goto bad_area;
- } else {
- if (!(vma->vm_flags & VM_WRITE))
- goto bad_area;
- }
+ if (fault > 0)
+ return 0;
/*
- * If for any reason at all we couldn't handle the fault,
- * make sure we exit gracefully rather than endlessly redo
- * the fault.
+ * We had some memory, but were unable to
+ * successfully fix up this page fault.
*/
- if (!handle_mm_fault(mm, vma, addr & PAGE_MASK, DO_COW(mode)))
+ if (fault == 0)
goto do_sigbus;
- up(&mm->mmap_sem);
- return 0;
-
/*
- * Something tried to access memory that isn't in our memory map..
- * Fix it, but check if it's kernel or user first..
+ * If we are in kernel mode at this point, we
+ * have no context to handle this fault with.
*/
-bad_area:
- up(&mm->mmap_sem);
+ if (!user_mode(regs))
+ goto no_context;
+
+ if (fault == -3) {
+ /*
+ * 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 {
+ /*
+ * Something tried to access memory that isn't in our memory map..
+ * User mode accesses just cause a SIGSEGV
+ */
+ struct siginfo si;
+
+#ifdef CONFIG_DEBUG_USER
+ printk(KERN_DEBUG "%s: unhandled page fault at pc=0x%08lx, "
+ "lr=0x%08lx (bad address=0x%08lx, code %d)\n",
+ tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode);
+#endif
- /* User mode accesses just cause a SIGSEGV */
- if (user_mode(regs)) {
tsk->thread.address = addr;
tsk->thread.error_code = mode;
tsk->thread.trap_no = 14;
-#ifdef CONFIG_DEBUG_USER
- printk("%s: memory violation at pc=0x%08lx, lr=0x%08lx (bad address=0x%08lx, code %d)\n",
- tsk->comm, regs->ARM_pc, regs->ARM_lr, addr, mode);
-#endif
- force_sig(SIGSEGV, tsk);
- return 0;
+ si.si_signo = SIGSEGV;
+ si.si_code = fault == -1 ? SEGV_ACCERR : SEGV_MAPERR;
+ si.si_addr = (void *)addr;
+ force_sig_info(SIGSEGV, &si, tsk);
}
+ return 0;
+
+
+/*
+ * 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 = mode;
+ tsk->thread.trap_no = 14;
+ force_sig(SIGBUS, tsk);
+
+ /* Kernel mode? Handle exceptions or die */
+ if (user_mode(regs))
+ return 0;
no_context:
/* Are we prepared to handle this kernel fault? */
@@ -160,29 +223,16 @@
return 0;
}
- kernel_page_fault(addr, mode, regs, tsk, mm);
- return 0;
-
-do_sigbus:
/*
- * We ran out of memory, or some other thing happened to us that made
- * us unable to handle the page fault gracefully.
+ * Oops. The kernel tried to access some bad page. We'll have to
+ * terminate things with extreme prejudice.
*/
- up(&mm->mmap_sem);
+ printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n",
+ (addr < PAGE_SIZE) ? "NULL pointer dereference" : "paging request", addr);
- /*
- * Send a sigbus, regardless of whether we were in kernel
- * or user mode.
- */
- tsk->thread.address = addr;
- tsk->thread.error_code = mode;
- tsk->thread.trap_no = 14;
- force_sig(SIGBUS, tsk);
+ show_pte(mm, addr);
+ die("Oops", regs, mode);
+ do_exit(SIGKILL);
- /* Kernel mode? Handle exceptions or die */
- if (!user_mode(regs))
- goto no_context;
return 0;
}
-
-
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)