Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 292506
b: refs/heads/master
c: 9be7257
h: refs/heads/master
v: v3
  • Loading branch information
Benjamin Herrenschmidt committed Mar 8, 2012
1 parent 79eee95 commit ab6576a
Show file tree
Hide file tree
Showing 2 changed files with 121 additions and 51 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 9f2f79e3a3c19ae745d0439d6e0eed31df28de3c
refs/heads/master: 9be72573a80648866ed0045db22d97c6e160a540
170 changes: 120 additions & 50 deletions trunk/arch/powerpc/mm/fault.c
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,82 @@ static int store_updates_sp(struct pt_regs *regs)
}
return 0;
}
/*
* do_page_fault error handling helpers
*/

#define MM_FAULT_RETURN 0
#define MM_FAULT_CONTINUE -1
#define MM_FAULT_ERR(sig) (sig)

static int out_of_memory(struct pt_regs *regs)
{
/*
* We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully.
*/
up_read(&current->mm->mmap_sem);
if (!user_mode(regs))
return MM_FAULT_ERR(SIGKILL);
pagefault_out_of_memory();
return MM_FAULT_RETURN;
}

static int do_sigbus(struct pt_regs *regs, unsigned long address)
{
siginfo_t info;

up_read(&current->mm->mmap_sem);

if (user_mode(regs)) {
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void __user *)address;
force_sig_info(SIGBUS, &info, current);
return MM_FAULT_RETURN;
}
return MM_FAULT_ERR(SIGBUS);
}

static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)
{
/*
* Pagefault was interrupted by SIGKILL. We have no reason to
* continue the pagefault.
*/
if (fatal_signal_pending(current)) {
/*
* If we have retry set, the mmap semaphore will have
* alrady been released in __lock_page_or_retry(). Else
* we release it now.
*/
if (!(fault & VM_FAULT_RETRY))
up_read(&current->mm->mmap_sem);
/* Coming from kernel, we need to deal with uaccess fixups */
if (user_mode(regs))
return MM_FAULT_RETURN;
return MM_FAULT_ERR(SIGKILL);
}

/* No fault: be happy */
if (!(fault & VM_FAULT_ERROR))
return MM_FAULT_CONTINUE;

/* Out of memory */
if (fault & VM_FAULT_OOM)
return out_of_memory(regs);

/* Bus error. x86 handles HWPOISON here, we'll add this if/when
* we support the feature in HW
*/
if (fault & VM_FAULT_SIGBUS)
return do_sigbus(regs, addr);

/* We don't understand the fault code, this is fatal */
BUG();
return MM_FAULT_CONTINUE;
}

/*
* For 600- and 800-family processors, the error_code parameter is DSISR
Expand All @@ -124,11 +200,12 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
{
struct vm_area_struct * vma;
struct mm_struct *mm = current->mm;
siginfo_t info;
unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
int code = SEGV_MAPERR;
int is_write = 0, ret;
int is_write = 0;
int trap = TRAP(regs);
int is_exec = trap == 0x400;
int fault;

#if !(defined(CONFIG_4xx) || defined(CONFIG_BOOKE))
/*
Expand All @@ -145,20 +222,21 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
is_write = error_code & ESR_DST;
#endif /* CONFIG_4xx || CONFIG_BOOKE */

if (is_write)
flags |= FAULT_FLAG_WRITE;

#ifdef CONFIG_PPC_ICSWX
/*
* we need to do this early because this "data storage
* interrupt" does not update the DAR/DEAR so we don't want to
* look at it
*/
if (error_code & ICSWX_DSI_UCT) {
int ret;

ret = acop_handle_fault(regs, address, error_code);
if (ret)
return ret;
int rc = acop_handle_fault(regs, address, error_code);
if (rc)
return rc;
}
#endif
#endif /* CONFIG_PPC_ICSWX */

if (notify_page_fault(regs))
return 0;
Expand Down Expand Up @@ -216,6 +294,7 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
if (!user_mode(regs) && !search_exception_tables(regs->nip))
goto bad_area_nosemaphore;

retry:
down_read(&mm->mmap_sem);
} else {
/*
Expand Down Expand Up @@ -338,30 +417,43 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,
* make sure we exit gracefully rather than endlessly redo
* the fault.
*/
ret = handle_mm_fault(mm, vma, address, is_write ? FAULT_FLAG_WRITE : 0);
if (unlikely(ret & VM_FAULT_ERROR)) {
if (ret & VM_FAULT_OOM)
goto out_of_memory;
else if (ret & VM_FAULT_SIGBUS)
goto do_sigbus;
BUG();
fault = handle_mm_fault(mm, vma, address, flags);
if (unlikely(fault & (VM_FAULT_RETRY|VM_FAULT_ERROR))) {
int rc = mm_fault_error(regs, address, fault);
if (rc >= MM_FAULT_RETURN)
return rc;
}
if (ret & VM_FAULT_MAJOR) {
current->maj_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
regs, address);

/*
* Major/minor page fault accounting is only done on the
* initial attempt. If we go through a retry, it is extremely
* likely that the page will be found in page cache at that point.
*/
if (flags & FAULT_FLAG_ALLOW_RETRY) {
if (fault & VM_FAULT_MAJOR) {
current->maj_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
regs, address);
#ifdef CONFIG_PPC_SMLPAR
if (firmware_has_feature(FW_FEATURE_CMO)) {
preempt_disable();
get_lppaca()->page_ins += (1 << PAGE_FACTOR);
preempt_enable();
if (firmware_has_feature(FW_FEATURE_CMO)) {
preempt_disable();
get_lppaca()->page_ins += (1 << PAGE_FACTOR);
preempt_enable();
}
#endif /* CONFIG_PPC_SMLPAR */
} else {
current->min_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
regs, address);
}
if (fault & VM_FAULT_RETRY) {
/* Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk
* of starvation. */
flags &= ~FAULT_FLAG_ALLOW_RETRY;
goto retry;
}
#endif
} else {
current->min_flt++;
perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
regs, address);
}

up_read(&mm->mmap_sem);
return 0;

Expand All @@ -382,28 +474,6 @@ int __kprobes do_page_fault(struct pt_regs *regs, unsigned long address,

return SIGSEGV;

/*
* We ran out of memory, or some other thing happened to us that made
* us unable to handle the page fault gracefully.
*/
out_of_memory:
up_read(&mm->mmap_sem);
if (!user_mode(regs))
return SIGKILL;
pagefault_out_of_memory();
return 0;

do_sigbus:
up_read(&mm->mmap_sem);
if (user_mode(regs)) {
info.si_signo = SIGBUS;
info.si_errno = 0;
info.si_code = BUS_ADRERR;
info.si_addr = (void __user *)address;
force_sig_info(SIGBUS, &info, current);
return 0;
}
return SIGBUS;
}

/*
Expand Down

0 comments on commit ab6576a

Please sign in to comment.