diff options
Diffstat (limited to 'arch/alpha/mm/fault.c')
| -rw-r--r-- | arch/alpha/mm/fault.c | 67 |
1 files changed, 39 insertions, 28 deletions
diff --git a/arch/alpha/mm/fault.c b/arch/alpha/mm/fault.c index 64ace5a9cd3..98838a05ba6 100644 --- a/arch/alpha/mm/fault.c +++ b/arch/alpha/mm/fault.c @@ -4,7 +4,6 @@ * Copyright (C) 1995 Linus Torvalds */ -#include <linux/config.h> #include <linux/sched.h> #include <linux/kernel.h> #include <linux/mm.h> @@ -22,11 +21,9 @@ #include <linux/ptrace.h> #include <linux/mman.h> #include <linux/smp.h> -#include <linux/smp_lock.h> #include <linux/interrupt.h> #include <linux/module.h> -#include <asm/system.h> #include <asm/uaccess.h> extern void die_if_kernel(char *,struct pt_regs *,long, unsigned long *); @@ -92,6 +89,7 @@ do_page_fault(unsigned long address, unsigned long mmcsr, const struct exception_table_entry *fixup; int fault, si_code = SEGV_MAPERR; siginfo_t info; + unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; /* As of EV6, a load into $31/$f31 is a prefetch, and never faults (or is suppressed by the PALcode). Support that for older CPUs @@ -109,14 +107,16 @@ do_page_fault(unsigned long address, unsigned long mmcsr, /* If we're in an interrupt context, or have no user context, we must not take the fault. */ - if (!mm || in_interrupt()) + if (!mm || in_atomic()) goto no_context; #ifdef CONFIG_ALPHA_LARGE_VMALLOC if (address >= TASK_SIZE) goto vmalloc_fault; #endif - + if (user_mode(regs)) + flags |= FAULT_FLAG_USER; +retry: down_read(&mm->mmap_sem); vma = find_vma(mm, address); if (!vma) @@ -142,29 +142,44 @@ do_page_fault(unsigned long address, unsigned long mmcsr, } else { if (!(vma->vm_flags & VM_WRITE)) goto bad_area; + flags |= FAULT_FLAG_WRITE; } - survive: /* If for any reason at all we couldn't handle the fault, make sure we exit gracefully rather than endlessly redo the fault. */ - fault = handle_mm_fault(mm, vma, address, cause > 0); - up_read(&mm->mmap_sem); + fault = handle_mm_fault(mm, vma, address, flags); + + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) + return; - switch (fault) { - case VM_FAULT_MINOR: - current->min_flt++; - break; - case VM_FAULT_MAJOR: - current->maj_flt++; - break; - case VM_FAULT_SIGBUS: - goto do_sigbus; - case VM_FAULT_OOM: - goto out_of_memory; - default: + if (unlikely(fault & VM_FAULT_ERROR)) { + if (fault & VM_FAULT_OOM) + goto out_of_memory; + else if (fault & VM_FAULT_SIGBUS) + goto do_sigbus; BUG(); } + + if (flags & FAULT_FLAG_ALLOW_RETRY) { + if (fault & VM_FAULT_MAJOR) + current->maj_flt++; + else + current->min_flt++; + if (fault & VM_FAULT_RETRY) { + flags &= ~FAULT_FLAG_ALLOW_RETRY; + + /* No need to up_read(&mm->mmap_sem) as we would + * have already released it in __lock_page_or_retry + * in mm/filemap.c. + */ + + goto retry; + } + } + + up_read(&mm->mmap_sem); + return; /* Something tried to access memory that isn't in our memory map. @@ -194,18 +209,14 @@ do_page_fault(unsigned long address, unsigned long mmcsr, /* We ran out of memory, or some other thing happened to us that made us unable to handle the page fault gracefully. */ out_of_memory: - if (current->pid == 1) { - yield(); - down_read(&mm->mmap_sem); - goto survive; - } - printk(KERN_ALERT "VM: killing process %s(%d)\n", - current->comm, current->pid); + up_read(&mm->mmap_sem); if (!user_mode(regs)) goto no_context; - do_exit(SIGKILL); + pagefault_out_of_memory(); + return; do_sigbus: + up_read(&mm->mmap_sem); /* Send a sigbus, regardless of whether we were in kernel or user mode. */ info.si_signo = SIGBUS; |
