diff options
author | Johannes Weiner <hannes@cmpxchg.org> | 2013-09-12 18:13:40 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2013-09-12 18:38:01 -0400 |
commit | 3a13c4d761b4b979ba8767f42345fed3274991b0 (patch) | |
tree | 9f6f0b09875b6b510720bea2c2a0ac1fd6878f67 /arch/x86 | |
parent | 759496ba6407c6994d6a5ce3a5e74937d7816208 (diff) |
x86: finish user fault error path with fatal signal
The x86 fault handler bails in the middle of error handling when the
task has a fatal signal pending. For a subsequent patch this is a
problem in OOM situations because it relies on pagefault_out_of_memory()
being called even when the task has been killed, to perform proper
per-task OOM state unwinding.
Shortcutting the fault like this is a rather minor optimization that
saves a few instructions in rare cases. Just remove it for
user-triggered faults.
Use the opportunity to split the fault retry handling from actual fault
errors and add locking documentation that reads suprisingly similar to
ARM's.
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
Reviewed-by: Michal Hocko <mhocko@suse.cz>
Acked-by: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
Cc: David Rientjes <rientjes@google.com>
Cc: KAMEZAWA Hiroyuki <kamezawa.hiroyu@jp.fujitsu.com>
Cc: azurIt <azurit@pobox.sk>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'arch/x86')
-rw-r--r-- | arch/x86/mm/fault.c | 35 |
1 files changed, 17 insertions, 18 deletions
diff --git a/arch/x86/mm/fault.c b/arch/x86/mm/fault.c index 6d77c3866faa..3aaeffcfd67a 100644 --- a/arch/x86/mm/fault.c +++ b/arch/x86/mm/fault.c | |||
@@ -842,23 +842,15 @@ do_sigbus(struct pt_regs *regs, unsigned long error_code, unsigned long address, | |||
842 | force_sig_info_fault(SIGBUS, code, address, tsk, fault); | 842 | force_sig_info_fault(SIGBUS, code, address, tsk, fault); |
843 | } | 843 | } |
844 | 844 | ||
845 | static noinline int | 845 | static noinline void |
846 | mm_fault_error(struct pt_regs *regs, unsigned long error_code, | 846 | mm_fault_error(struct pt_regs *regs, unsigned long error_code, |
847 | unsigned long address, unsigned int fault) | 847 | unsigned long address, unsigned int fault) |
848 | { | 848 | { |
849 | /* | 849 | if (fatal_signal_pending(current) && !(error_code & PF_USER)) { |
850 | * Pagefault was interrupted by SIGKILL. We have no reason to | 850 | up_read(¤t->mm->mmap_sem); |
851 | * continue pagefault. | 851 | no_context(regs, error_code, address, 0, 0); |
852 | */ | 852 | return; |
853 | if (fatal_signal_pending(current)) { | ||
854 | if (!(fault & VM_FAULT_RETRY)) | ||
855 | up_read(¤t->mm->mmap_sem); | ||
856 | if (!(error_code & PF_USER)) | ||
857 | no_context(regs, error_code, address, 0, 0); | ||
858 | return 1; | ||
859 | } | 853 | } |
860 | if (!(fault & VM_FAULT_ERROR)) | ||
861 | return 0; | ||
862 | 854 | ||
863 | if (fault & VM_FAULT_OOM) { | 855 | if (fault & VM_FAULT_OOM) { |
864 | /* Kernel mode? Handle exceptions or die: */ | 856 | /* Kernel mode? Handle exceptions or die: */ |
@@ -866,7 +858,7 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code, | |||
866 | up_read(¤t->mm->mmap_sem); | 858 | up_read(¤t->mm->mmap_sem); |
867 | no_context(regs, error_code, address, | 859 | no_context(regs, error_code, address, |
868 | SIGSEGV, SEGV_MAPERR); | 860 | SIGSEGV, SEGV_MAPERR); |
869 | return 1; | 861 | return; |
870 | } | 862 | } |
871 | 863 | ||
872 | up_read(¤t->mm->mmap_sem); | 864 | up_read(¤t->mm->mmap_sem); |
@@ -884,7 +876,6 @@ mm_fault_error(struct pt_regs *regs, unsigned long error_code, | |||
884 | else | 876 | else |
885 | BUG(); | 877 | BUG(); |
886 | } | 878 | } |
887 | return 1; | ||
888 | } | 879 | } |
889 | 880 | ||
890 | static int spurious_fault_check(unsigned long error_code, pte_t *pte) | 881 | static int spurious_fault_check(unsigned long error_code, pte_t *pte) |
@@ -1189,9 +1180,17 @@ good_area: | |||
1189 | */ | 1180 | */ |
1190 | fault = handle_mm_fault(mm, vma, address, flags); | 1181 | fault = handle_mm_fault(mm, vma, address, flags); |
1191 | 1182 | ||
1192 | if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) { | 1183 | /* |
1193 | if (mm_fault_error(regs, error_code, address, fault)) | 1184 | * If we need to retry but a fatal signal is pending, handle the |
1194 | return; | 1185 | * signal first. We do not need to release the mmap_sem because it |
1186 | * would already be released in __lock_page_or_retry in mm/filemap.c. | ||
1187 | */ | ||
1188 | if (unlikely((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))) | ||
1189 | return; | ||
1190 | |||
1191 | if (unlikely(fault & VM_FAULT_ERROR)) { | ||
1192 | mm_fault_error(regs, error_code, address, fault); | ||
1193 | return; | ||
1195 | } | 1194 | } |
1196 | 1195 | ||
1197 | /* | 1196 | /* |