diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-31 15:32:05 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-07-31 15:32:05 -0700 |
commit | 26847fa6eb4fd653171f86d249caa761ce1e87c7 (patch) | |
tree | 8b315fd9fb903b1c3a942389a7be0580de5cd184 /arch/s390/mm/fault.c | |
parent | 08843b79fb35d33859e0f8f11a7318341076e4d1 (diff) | |
parent | 7d25617597ff8dcfe4d0e1d0ac9214e7cc7ded92 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux
Pull s390 updates from Martin Schwidefsky:
"This it the second batch of s390 patches for the 3.6 merge window.
Included is enablement for two common code changes, killable page
faults and sorted exception tables. And the regular set of cleanup
and bug fix patches."
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/s390/linux:
s390: make use of user_mode() macro where possible
s390/mm: rename user_mode variable to addressing_mode
s390/mm: fix fault handling for page table walk case
s390/mm: make page faults killable
s390: update defconfig
s390/mm: downgrade page table after fork of a 31 bit process
s390/ipl: Use diagnose 8 command separation
s390/linker script: use RO_DATA_SECTION
s390/exceptions: sort exception table at build time
s390/debug: remove module_exit function / move EXPORT_SYMBOLs
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 35 |
1 files changed, 23 insertions, 12 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index 6a12d1bb6e09..6c013f544146 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -49,6 +49,7 @@ #define VM_FAULT_BADCONTEXT 0x010000 #define VM_FAULT_BADMAP 0x020000 #define VM_FAULT_BADACCESS 0x040000 +#define VM_FAULT_SIGNAL 0x080000 static unsigned long store_indication; @@ -110,7 +111,7 @@ static inline int user_space_fault(unsigned long trans_exc_code) if (trans_exc_code == 2) /* Access via secondary space, set_fs setting decides */ return current->thread.mm_segment.ar4; - if (user_mode == HOME_SPACE_MODE) + if (addressing_mode == HOME_SPACE_MODE) /* User space if the access has been done via home space. */ return trans_exc_code == 3; /* @@ -219,7 +220,7 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault) case VM_FAULT_BADACCESS: case VM_FAULT_BADMAP: /* Bad memory access. Check if it is kernel or user space. */ - if (regs->psw.mask & PSW_MASK_PSTATE) { + if (user_mode(regs)) { /* User mode accesses just cause a SIGSEGV */ si_code = (fault == VM_FAULT_BADMAP) ? SEGV_MAPERR : SEGV_ACCERR; @@ -229,15 +230,19 @@ static noinline void do_fault_error(struct pt_regs *regs, int fault) case VM_FAULT_BADCONTEXT: do_no_context(regs); break; + case VM_FAULT_SIGNAL: + if (!user_mode(regs)) + do_no_context(regs); + break; default: /* fault & VM_FAULT_ERROR */ if (fault & VM_FAULT_OOM) { - if (!(regs->psw.mask & PSW_MASK_PSTATE)) + if (!user_mode(regs)) do_no_context(regs); else pagefault_out_of_memory(); } else if (fault & VM_FAULT_SIGBUS) { /* Kernel mode? Handle exceptions or die */ - if (!(regs->psw.mask & PSW_MASK_PSTATE)) + if (!user_mode(regs)) do_no_context(regs); else do_sigbus(regs); @@ -286,7 +291,7 @@ static inline int do_exception(struct pt_regs *regs, int access) address = trans_exc_code & __FAIL_ADDR_MASK; perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address); - flags = FAULT_FLAG_ALLOW_RETRY; + flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; if (access == VM_WRITE || (trans_exc_code & store_indication) == 0x400) flags |= FAULT_FLAG_WRITE; down_read(&mm->mmap_sem); @@ -335,6 +340,11 @@ retry: * the fault. */ fault = handle_mm_fault(mm, vma, address, flags); + /* No reason to continue if interrupted by SIGKILL. */ + if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current)) { + fault = VM_FAULT_SIGNAL; + goto out; + } if (unlikely(fault & VM_FAULT_ERROR)) goto out_up; @@ -426,7 +436,7 @@ void __kprobes do_asce_exception(struct pt_regs *regs) } /* User mode accesses just cause a SIGSEGV */ - if (regs->psw.mask & PSW_MASK_PSTATE) { + if (user_mode(regs)) { do_sigsegv(regs, SEGV_MAPERR); return; } @@ -441,6 +451,7 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) struct pt_regs regs; int access, fault; + /* Emulate a uaccess fault from kernel mode. */ regs.psw.mask = psw_kernel_bits | PSW_MASK_DAT | PSW_MASK_MCHECK; if (!irqs_disabled()) regs.psw.mask |= PSW_MASK_IO | PSW_MASK_EXT; @@ -450,12 +461,12 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) regs.int_parm_long = (uaddr & PAGE_MASK) | 2; access = write ? VM_WRITE : VM_READ; fault = do_exception(®s, access); - if (unlikely(fault)) { - if (fault & VM_FAULT_OOM) - return -EFAULT; - else if (fault & VM_FAULT_SIGBUS) - do_sigbus(®s); - } + /* + * Since the fault happened in kernel mode while performing a uaccess + * all we need to do now is emulating a fixup in case "fault" is not + * zero. + * For the calling uaccess functions this results always in -EFAULT. + */ return fault ? -EFAULT : 0; } |