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

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)