diff options
Diffstat (limited to 'arch/powerpc/mm/fault.c')
-rw-r--r-- | arch/powerpc/mm/fault.c | 181 |
1 files changed, 131 insertions, 50 deletions
diff --git a/arch/powerpc/mm/fault.c b/arch/powerpc/mm/fault.c index 2f0d1b032a89..19f2f9498b27 100644 --- a/arch/powerpc/mm/fault.c +++ b/arch/powerpc/mm/fault.c | |||
@@ -105,6 +105,82 @@ static int store_updates_sp(struct pt_regs *regs) | |||
105 | } | 105 | } |
106 | return 0; | 106 | return 0; |
107 | } | 107 | } |
108 | /* | ||
109 | * do_page_fault error handling helpers | ||
110 | */ | ||
111 | |||
112 | #define MM_FAULT_RETURN 0 | ||
113 | #define MM_FAULT_CONTINUE -1 | ||
114 | #define MM_FAULT_ERR(sig) (sig) | ||
115 | |||
116 | static int out_of_memory(struct pt_regs *regs) | ||
117 | { | ||
118 | /* | ||
119 | * We ran out of memory, or some other thing happened to us that made | ||
120 | * us unable to handle the page fault gracefully. | ||
121 | */ | ||
122 | up_read(¤t->mm->mmap_sem); | ||
123 | if (!user_mode(regs)) | ||
124 | return MM_FAULT_ERR(SIGKILL); | ||
125 | pagefault_out_of_memory(); | ||
126 | return MM_FAULT_RETURN; | ||
127 | } | ||
128 | |||
129 | static int do_sigbus(struct pt_regs *regs, unsigned long address) | ||
130 | { | ||
131 | siginfo_t info; | ||
132 | |||
133 | up_read(¤t->mm->mmap_sem); | ||
134 | |||
135 | if (user_mode(regs)) { | ||
136 | info.si_signo = SIGBUS; | ||
137 | info.si_errno = 0; | ||
138 | info.si_code = BUS_ADRERR; | ||
139 | info.si_addr = (void __user *)address; | ||
140 | force_sig_info(SIGBUS, &info, current); | ||
141 | return MM_FAULT_RETURN; | ||
142 | } | ||
143 | return MM_FAULT_ERR(SIGBUS); | ||
144 | } | ||
145 | |||
146 | static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault) | ||
147 | { | ||
148 | /* | ||
149 | * Pagefault was interrupted by SIGKILL. We have no reason to | ||
150 | * continue the pagefault. | ||
151 | */ | ||
152 | if (fatal_signal_pending(current)) { | ||
153 | /* | ||
154 | * If we have retry set, the mmap semaphore will have | ||
155 | * alrady been released in __lock_page_or_retry(). Else | ||
156 | * we release it now. | ||
157 | */ | ||
158 | if (!(fault & VM_FAULT_RETRY)) | ||
159 | up_read(¤t->mm->mmap_sem); | ||
160 | /* Coming from kernel, we need to deal with uaccess fixups */ | ||
161 | if (user_mode(regs)) | ||
162 | return MM_FAULT_RETURN; | ||
163 | return MM_FAULT_ERR(SIGKILL); | ||
164 | } | ||
165 | |||
166 | /* No fault: be happy */ | ||
167 | if (!(fault & VM_FAULT_ERROR)) | ||
168 | return MM_FAULT_CONTINUE; | ||
169 | |||
170 | /* Out of memory */ | ||
171 | if (fault & VM_FAULT_OOM) | ||
172 | return out_of_memory(regs); | ||
173 | |||
174 | /* Bus error. x86 handles HWPOISON here, we'll add this if/when | ||
175 | * we support the feature in HW | ||
176 | */ | ||
177 | if (fault & VM_FAULT_SIGBUS) | ||
178 | return do_sigbus(regs, addr); | ||
179 | |||
180 | /* We don't understand the fault code, this is fatal */ | ||
181 | BUG(); | ||
182 | return MM_FAULT_CONTINUE; | ||
183 | } | ||
108 | 184 | ||
109 | /* | 185 | /* |
110 | * For 600- and 800-family processors, the error_code parameter is DSISR | 186 | * For 600- and 800-family processors, the error_code parameter is DSISR |
@@ -124,11 +200,12 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, | |||
124 | { | 200 | { |
125 | struct vm_area_struct * vma; | 201 | struct vm_area_struct * vma; |
126 | struct mm_struct *mm = current->mm; | 202 | struct mm_struct *mm = current->mm; |
127 | siginfo_t info; | 203 | unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE; |
128 | int code = SEGV_MAPERR; | 204 | int code = SEGV_MAPERR; |
129 | int is_write = 0, ret; | 205 | int is_write = 0; |
130 | int trap = TRAP(regs); | 206 | int trap = TRAP(regs); |
131 | int is_exec = trap == 0x400; | 207 | int is_exec = trap == 0x400; |
208 | int fault; | ||
132 | 209 | ||
133 | #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE)) | 210 | #if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE)) |
134 | /* | 211 | /* |
@@ -145,6 +222,9 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, | |||
145 | is_write = error_code & ESR_DST; | 222 | is_write = error_code & ESR_DST; |
146 | #endif /* CONFIG_4xx || CONFIG_BOOKE */ | 223 | #endif /* CONFIG_4xx || CONFIG_BOOKE */ |
147 | 224 | ||
225 | if (is_write) | ||
226 | flags |= FAULT_FLAG_WRITE; | ||
227 | |||
148 | #ifdef CONFIG_PPC_ICSWX | 228 | #ifdef CONFIG_PPC_ICSWX |
149 | /* | 229 | /* |
150 | * we need to do this early because this "data storage | 230 | * we need to do this early because this "data storage |
@@ -152,13 +232,11 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, | |||
152 | * look at it | 232 | * look at it |
153 | */ | 233 | */ |
154 | if (error_code & ICSWX_DSI_UCT) { | 234 | if (error_code & ICSWX_DSI_UCT) { |
155 | int ret; | 235 | int rc = acop_handle_fault(regs, address, error_code); |
156 | 236 | if (rc) | |
157 | ret = acop_handle_fault(regs, address, error_code); | 237 | return rc; |
158 | if (ret) | ||
159 | return ret; | ||
160 | } | 238 | } |
161 | #endif | 239 | #endif /* CONFIG_PPC_ICSWX */ |
162 | 240 | ||
163 | if (notify_page_fault(regs)) | 241 | if (notify_page_fault(regs)) |
164 | return 0; | 242 | return 0; |
@@ -179,6 +257,10 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, | |||
179 | } | 257 | } |
180 | #endif | 258 | #endif |
181 | 259 | ||
260 | /* We restore the interrupt state now */ | ||
261 | if (!arch_irq_disabled_regs(regs)) | ||
262 | local_irq_enable(); | ||
263 | |||
182 | if (in_atomic() || mm == NULL) { | 264 | if (in_atomic() || mm == NULL) { |
183 | if (!user_mode(regs)) | 265 | if (!user_mode(regs)) |
184 | return SIGSEGV; | 266 | return SIGSEGV; |
@@ -212,7 +294,15 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address, | |||
212 | if (!user_mode(regs) && !search_exception_tables(regs->nip)) | 294 | if (!user_mode(regs) && !search_exception_tables(regs->nip)) |
213 | goto bad_area_nosemaphore; | 295 | goto bad_area_nosemaphore; |
214 | 296 | ||
297 | retry: | ||
215 | down_read(&mm->mmap_sem); | 298 | down_read(&mm->mmap_sem); |
299 | } else { | ||
300 | /* | ||
301 | * The above down_read_trylock() might have succeeded in | ||
302 | * which case we'll have missed the might_sleep() from | ||
303 | * down_read(): | ||
304 | */ | ||
305 | might_sleep(); | ||
216 | } | 306 | } |
217 | 307 | ||
218 | vma = find_vma(mm, address); | 308 | vma = find_vma(mm, address); |
@@ -327,30 +417,43 @@ good_area: | |||
327 | * make sure we exit gracefully rather than endlessly redo | 417 | * make sure we exit gracefully rather than endlessly redo |
328 | * the fault. | 418 | * the fault. |
329 | */ | 419 | */ |
330 | ret = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0); | 420 | fault = handle_mm_fault(mm, vma, address, flags); |
331 | if (unlikely(ret & VM_FAULT_ERROR)) { | 421 | if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) { |
332 | if (ret & VM_FAULT_OOM) | 422 | int rc = mm_fault_error(regs, address, fault); |
333 | goto out_of_memory; | 423 | if (rc >= MM_FAULT_RETURN) |
334 | else if (ret & VM_FAULT_SIGBUS) | 424 | return rc; |
335 | goto do_sigbus; | ||
336 | BUG(); | ||
337 | } | 425 | } |
338 | if (ret & VM_FAULT_MAJOR) { | 426 | |
339 | current->maj_flt++; | 427 | /* |
340 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, | 428 | * Major/minor page fault accounting is only done on the |
341 | regs, address); | 429 | * initial attempt. If we go through a retry, it is extremely |
430 | * likely that the page will be found in page cache at that point. | ||
431 | */ | ||
432 | if (flags & FAULT_FLAG_ALLOW_RETRY) { | ||
433 | if (fault & VM_FAULT_MAJOR) { | ||
434 | current->maj_flt++; | ||
435 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, | ||
436 | regs, address); | ||
342 | #ifdef CONFIG_PPC_SMLPAR | 437 | #ifdef CONFIG_PPC_SMLPAR |
343 | if (firmware_has_feature(FW_FEATURE_CMO)) { | 438 | if (firmware_has_feature(FW_FEATURE_CMO)) { |
344 | preempt_disable(); | 439 | preempt_disable(); |
345 | get_lppaca()->page_ins += (1 << PAGE_FACTOR); | 440 | get_lppaca()->page_ins += (1 << PAGE_FACTOR); |
346 | preempt_enable(); | 441 | preempt_enable(); |
442 | } | ||
443 | #endif /* CONFIG_PPC_SMLPAR */ | ||
444 | } else { | ||
445 | current->min_flt++; | ||
446 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, | ||
447 | regs, address); | ||
448 | } | ||
449 | if (fault & VM_FAULT_RETRY) { | ||
450 | /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk | ||
451 | * of starvation. */ | ||
452 | flags &= ~FAULT_FLAG_ALLOW_RETRY; | ||
453 | goto retry; | ||
347 | } | 454 | } |
348 | #endif | ||
349 | } else { | ||
350 | current->min_flt++; | ||
351 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, | ||
352 | regs, address); | ||
353 | } | 455 | } |
456 | |||
354 | up_read(&mm->mmap_sem); | 457 | up_read(&mm->mmap_sem); |
355 | return 0; | 458 | return 0; |
356 | 459 | ||
@@ -371,28 +474,6 @@ bad_area_nosemaphore: | |||
371 | 474 | ||
372 | return SIGSEGV; | 475 | return SIGSEGV; |
373 | 476 | ||
374 | /* | ||
375 | * We ran out of memory, or some other thing happened to us that made | ||
376 | * us unable to handle the page fault gracefully. | ||
377 | */ | ||
378 | out_of_memory: | ||
379 | up_read(&mm->mmap_sem); | ||
380 | if (!user_mode(regs)) | ||
381 | return SIGKILL; | ||
382 | pagefault_out_of_memory(); | ||
383 | return 0; | ||
384 | |||
385 | do_sigbus: | ||
386 | up_read(&mm->mmap_sem); | ||
387 | if (user_mode(regs)) { | ||
388 | info.si_signo = SIGBUS; | ||
389 | info.si_errno = 0; | ||
390 | info.si_code = BUS_ADRERR; | ||
391 | info.si_addr = (void __user *)address; | ||
392 | force_sig_info(SIGBUS, &info, current); | ||
393 | return 0; | ||
394 | } | ||
395 | return SIGBUS; | ||
396 | } | 477 | } |
397 | 478 | ||
398 | /* | 479 | /* |