diff options
Diffstat (limited to 'arch/mips/mm/fault.c')
| -rw-r--r-- | arch/mips/mm/fault.c | 67 | 
1 files changed, 51 insertions, 16 deletions
diff --git a/arch/mips/mm/fault.c b/arch/mips/mm/fault.c index 137ee76a004..becc42bb184 100644 --- a/arch/mips/mm/fault.c +++ b/arch/mips/mm/fault.c @@ -5,6 +5,7 @@   *   * Copyright (C) 1995 - 2000 by Ralf Baechle   */ +#include <linux/context_tracking.h>  #include <linux/signal.h>  #include <linux/sched.h>  #include <linux/interrupt.h> @@ -22,7 +23,6 @@  #include <asm/branch.h>  #include <asm/mmu_context.h> -#include <asm/system.h>  #include <asm/uaccess.h>  #include <asm/ptrace.h>  #include <asm/highmem.h>		/* For VMALLOC_END */ @@ -33,8 +33,8 @@   * and the problem, and then passes it off to one of the appropriate   * routines.   */ -asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long write, -			      unsigned long address) +static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write, +	unsigned long address)  {  	struct vm_area_struct * vma = NULL;  	struct task_struct *tsk = current; @@ -42,6 +42,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long writ  	const int field = sizeof(unsigned long) * 2;  	siginfo_t info;  	int fault; +	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;  #if 0  	printk("Cpu%d[%s:%d:%0*lx:%ld:%0*lx]\n", raw_smp_processor_id(), @@ -51,7 +52,7 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long writ  #ifdef CONFIG_KPROBES  	/* -	 * This is to notify the fault handler of the kprobes.  The +	 * This is to notify the fault handler of the kprobes.	The  	 * exception code is redundant as it is also carried in REGS,  	 * but we pass it anyhow.  	 */ @@ -91,6 +92,9 @@ asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, unsigned long writ  	if (in_atomic() || !mm)  		goto bad_area_nosemaphore; +	if (user_mode(regs)) +		flags |= FAULT_FLAG_USER; +retry:  	down_read(&mm->mmap_sem);  	vma = find_vma(mm, address);  	if (!vma) @@ -111,8 +115,9 @@ good_area:  	if (write) {  		if (!(vma->vm_flags & VM_WRITE))  			goto bad_area; +		flags |= FAULT_FLAG_WRITE;  	} else { -		if (kernel_uses_smartmips_rixi) { +		if (cpu_has_rixi) {  			if (address == regs->cp0_epc && !(vma->vm_flags & VM_EXEC)) {  #if 0  				pr_notice("Cpu%d[%s:%d:%0*lx:%ld:%0*lx] XI violation\n", @@ -144,8 +149,12 @@ good_area:  	 * make sure we exit gracefully rather than endlessly redo  	 * the fault.  	 */ -	fault = handle_mm_fault(mm, vma, address, write ? FAULT_FLAG_WRITE : 0); -	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); +	fault = handle_mm_fault(mm, vma, address, flags); + +	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) +		return; + +	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);  	if (unlikely(fault & VM_FAULT_ERROR)) {  		if (fault & VM_FAULT_OOM)  			goto out_of_memory; @@ -153,14 +162,28 @@ good_area:  			goto do_sigbus;  		BUG();  	} -	if (fault & VM_FAULT_MAJOR) { -		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, -				1, 0, regs, address); -		tsk->maj_flt++; -	} else { -		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, -				1, 0, regs, address); -		tsk->min_flt++; +	if (flags & FAULT_FLAG_ALLOW_RETRY) { +		if (fault & VM_FAULT_MAJOR) { +			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, +						  regs, address); +			tsk->maj_flt++; +		} else { +			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, +						  regs, address); +			tsk->min_flt++; +		} +		if (fault & VM_FAULT_RETRY) { +			flags &= ~FAULT_FLAG_ALLOW_RETRY; +			flags |= FAULT_FLAG_TRIED; + +			/* +			 * 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); @@ -196,7 +219,7 @@ bad_area_nosemaphore:  	}  no_context: -	/* Are we prepared to handle this kernel fault?  */ +	/* Are we prepared to handle this kernel fault?	 */  	if (fixup_exception(regs)) {  		current->thread.cp0_baduaddr = address;  		return; @@ -220,6 +243,8 @@ out_of_memory:  	 * (which will retry the fault, or kill us if we got oom-killed).  	 */  	up_read(&mm->mmap_sem); +	if (!user_mode(regs)) +		goto no_context;  	pagefault_out_of_memory();  	return; @@ -292,3 +317,13 @@ vmalloc_fault:  	}  #endif  } + +asmlinkage void __kprobes do_page_fault(struct pt_regs *regs, +	unsigned long write, unsigned long address) +{ +	enum ctx_state prev_state; + +	prev_state = exception_enter(); +	__do_page_fault(regs, write, address); +	exception_exit(prev_state); +}  | 
