diff options
Diffstat (limited to 'arch/s390/mm/fault.c')
-rw-r--r-- | arch/s390/mm/fault.c | 62 |
1 files changed, 39 insertions, 23 deletions
diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index a0f9e730f26a..fe103e891e7a 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c | |||
@@ -34,7 +34,7 @@ | |||
34 | #include <asm/asm-offsets.h> | 34 | #include <asm/asm-offsets.h> |
35 | #include <asm/system.h> | 35 | #include <asm/system.h> |
36 | #include <asm/pgtable.h> | 36 | #include <asm/pgtable.h> |
37 | #include <asm/s390_ext.h> | 37 | #include <asm/irq.h> |
38 | #include <asm/mmu_context.h> | 38 | #include <asm/mmu_context.h> |
39 | #include <asm/compat.h> | 39 | #include <asm/compat.h> |
40 | #include "../kernel/entry.h" | 40 | #include "../kernel/entry.h" |
@@ -245,9 +245,12 @@ static noinline void do_fault_error(struct pt_regs *regs, long int_code, | |||
245 | do_no_context(regs, int_code, trans_exc_code); | 245 | do_no_context(regs, int_code, trans_exc_code); |
246 | break; | 246 | break; |
247 | default: /* fault & VM_FAULT_ERROR */ | 247 | default: /* fault & VM_FAULT_ERROR */ |
248 | if (fault & VM_FAULT_OOM) | 248 | if (fault & VM_FAULT_OOM) { |
249 | pagefault_out_of_memory(); | 249 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) |
250 | else if (fault & VM_FAULT_SIGBUS) { | 250 | do_no_context(regs, int_code, trans_exc_code); |
251 | else | ||
252 | pagefault_out_of_memory(); | ||
253 | } else if (fault & VM_FAULT_SIGBUS) { | ||
251 | /* Kernel mode? Handle exceptions or die */ | 254 | /* Kernel mode? Handle exceptions or die */ |
252 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) | 255 | if (!(regs->psw.mask & PSW_MASK_PSTATE)) |
253 | do_no_context(regs, int_code, trans_exc_code); | 256 | do_no_context(regs, int_code, trans_exc_code); |
@@ -277,7 +280,8 @@ static inline int do_exception(struct pt_regs *regs, int access, | |||
277 | struct mm_struct *mm; | 280 | struct mm_struct *mm; |
278 | struct vm_area_struct *vma; | 281 | struct vm_area_struct *vma; |
279 | unsigned long address; | 282 | unsigned long address; |
280 | int fault, write; | 283 | unsigned int flags; |
284 | int fault; | ||
281 | 285 | ||
282 | if (notify_page_fault(regs)) | 286 | if (notify_page_fault(regs)) |
283 | return 0; | 287 | return 0; |
@@ -296,6 +300,10 @@ static inline int do_exception(struct pt_regs *regs, int access, | |||
296 | 300 | ||
297 | address = trans_exc_code & __FAIL_ADDR_MASK; | 301 | address = trans_exc_code & __FAIL_ADDR_MASK; |
298 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); | 302 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, 0, regs, address); |
303 | flags = FAULT_FLAG_ALLOW_RETRY; | ||
304 | if (access == VM_WRITE || (trans_exc_code & store_indication) == 0x400) | ||
305 | flags |= FAULT_FLAG_WRITE; | ||
306 | retry: | ||
299 | down_read(&mm->mmap_sem); | 307 | down_read(&mm->mmap_sem); |
300 | 308 | ||
301 | fault = VM_FAULT_BADMAP; | 309 | fault = VM_FAULT_BADMAP; |
@@ -325,21 +333,31 @@ static inline int do_exception(struct pt_regs *regs, int access, | |||
325 | * make sure we exit gracefully rather than endlessly redo | 333 | * make sure we exit gracefully rather than endlessly redo |
326 | * the fault. | 334 | * the fault. |
327 | */ | 335 | */ |
328 | write = (access == VM_WRITE || | 336 | fault = handle_mm_fault(mm, vma, address, flags); |
329 | (trans_exc_code & store_indication) == 0x400) ? | ||
330 | FAULT_FLAG_WRITE : 0; | ||
331 | fault = handle_mm_fault(mm, vma, address, write); | ||
332 | if (unlikely(fault & VM_FAULT_ERROR)) | 337 | if (unlikely(fault & VM_FAULT_ERROR)) |
333 | goto out_up; | 338 | goto out_up; |
334 | 339 | ||
335 | if (fault & VM_FAULT_MAJOR) { | 340 | /* |
336 | tsk->maj_flt++; | 341 | * Major/minor page fault accounting is only done on the |
337 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, | 342 | * initial attempt. If we go through a retry, it is extremely |
338 | regs, address); | 343 | * likely that the page will be found in page cache at that point. |
339 | } else { | 344 | */ |
340 | tsk->min_flt++; | 345 | if (flags & FAULT_FLAG_ALLOW_RETRY) { |
341 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, | 346 | if (fault & VM_FAULT_MAJOR) { |
342 | regs, address); | 347 | tsk->maj_flt++; |
348 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, 0, | ||
349 | regs, address); | ||
350 | } else { | ||
351 | tsk->min_flt++; | ||
352 | perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, 0, | ||
353 | regs, address); | ||
354 | } | ||
355 | if (fault & VM_FAULT_RETRY) { | ||
356 | /* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk | ||
357 | * of starvation. */ | ||
358 | flags &= ~FAULT_FLAG_ALLOW_RETRY; | ||
359 | goto retry; | ||
360 | } | ||
343 | } | 361 | } |
344 | /* | 362 | /* |
345 | * The instruction that caused the program check will | 363 | * The instruction that caused the program check will |
@@ -429,10 +447,9 @@ int __handle_fault(unsigned long uaddr, unsigned long pgm_int_code, int write) | |||
429 | access = write ? VM_WRITE : VM_READ; | 447 | access = write ? VM_WRITE : VM_READ; |
430 | fault = do_exception(®s, access, uaddr | 2); | 448 | fault = do_exception(®s, access, uaddr | 2); |
431 | if (unlikely(fault)) { | 449 | if (unlikely(fault)) { |
432 | if (fault & VM_FAULT_OOM) { | 450 | if (fault & VM_FAULT_OOM) |
433 | pagefault_out_of_memory(); | 451 | return -EFAULT; |
434 | fault = 0; | 452 | else if (fault & VM_FAULT_SIGBUS) |
435 | } else if (fault & VM_FAULT_SIGBUS) | ||
436 | do_sigbus(®s, pgm_int_code, uaddr); | 453 | do_sigbus(®s, pgm_int_code, uaddr); |
437 | } | 454 | } |
438 | return fault ? -EFAULT : 0; | 455 | return fault ? -EFAULT : 0; |
@@ -485,7 +502,6 @@ int pfault_init(void) | |||
485 | "2:\n" | 502 | "2:\n" |
486 | EX_TABLE(0b,1b) | 503 | EX_TABLE(0b,1b) |
487 | : "=d" (rc) : "a" (&refbk), "m" (refbk) : "cc"); | 504 | : "=d" (rc) : "a" (&refbk), "m" (refbk) : "cc"); |
488 | __ctl_set_bit(0, 9); | ||
489 | return rc; | 505 | return rc; |
490 | } | 506 | } |
491 | 507 | ||
@@ -500,7 +516,6 @@ void pfault_fini(void) | |||
500 | 516 | ||
501 | if (!MACHINE_IS_VM || pfault_disable) | 517 | if (!MACHINE_IS_VM || pfault_disable) |
502 | return; | 518 | return; |
503 | __ctl_clear_bit(0,9); | ||
504 | asm volatile( | 519 | asm volatile( |
505 | " diag %0,0,0x258\n" | 520 | " diag %0,0,0x258\n" |
506 | "0:\n" | 521 | "0:\n" |
@@ -615,6 +630,7 @@ static int __init pfault_irq_init(void) | |||
615 | rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; | 630 | rc = pfault_init() == 0 ? 0 : -EOPNOTSUPP; |
616 | if (rc) | 631 | if (rc) |
617 | goto out_pfault; | 632 | goto out_pfault; |
633 | service_subclass_irq_register(); | ||
618 | hotcpu_notifier(pfault_cpu_notify, 0); | 634 | hotcpu_notifier(pfault_cpu_notify, 0); |
619 | return 0; | 635 | return 0; |
620 | 636 | ||