This series attempts to clean the page fault handler in the way it has
been done previously for the x86 architecture [1].
The goal is to manage the mmap_sem earlier and only in
do_page_fault(). This done by handling the retry case earlier, before
handling the error case. This way the semaphore can be released
earlier and the error path processed without holding it.
The first patch is just moving a unlock to the caller of the service,
which as no functional impact.
The second patch is handling the retry case earlier in
do_page_fault(). This is where most the change are done, but I was
conservative here, not changing the use of mm_fault_error() in the
case of the second retry. It may be smarter to handle that case
separately but this may create duplicate code.
The last patch is moving up semaphore releasing from mm_fault_error()
to do_page_fault().
[1] see commits from Linus Torvalds
26178ec11ef3 ("x86: mm: consolidate VM_FAULT_RETRY handling")
7fb08eca4527 ("x86: mm: move mmap_sem unlock from mm_fault_error() to
caller")
Laurent Dufour (3):
powerpc/mm: move mmap_sem unlock up from do_sigbus
powerpc/mm: handle VM_FAULT_RETRY earlier
powerpc/mm: move mmap_sem unlocking in do_page_fault()
arch/powerpc/mm/fault.c | 82 ++++++++++++++++++++++++-------------------------
1 file changed, 40 insertions(+), 42 deletions(-)
--
2.7.4
In do_page_fault() if handle_mm_fault() returns VM_FAULT_RETRY, retry
the page fault handling before anything else.
This would simplify the handling of the mmap_sem lock in this part of
the code.
Signed-off-by: Laurent Dufour <redacted>
---
arch/powerpc/mm/fault.c | 67 ++++++++++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 29 deletions(-)
@@ -434,6 +434,26 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,*thefault.*/fault=handle_mm_fault(vma,address,flags);++/*+*Handletheretryrightnow,themmap_semhasbeenreleasedinthat+*case.+*/+if(unlikely(fault&VM_FAULT_RETRY)){+/* We retry only once */+if(flags&FAULT_FLAG_ALLOW_RETRY){+/*+*ClearFAULT_FLAG_ALLOW_RETRYtoavoidanyrisk+*ofstarvation.+*/+flags&=~FAULT_FLAG_ALLOW_RETRY;+flags|=FAULT_FLAG_TRIED;+if(!fatal_signal_pending(current))+gotoretry;+}+/* We will enter mm_fault_error() below */+}+if(unlikely(fault&(VM_FAULT_RETRY|VM_FAULT_ERROR))){if(fault&VM_FAULT_SIGSEGV)gotobad_area;
@@ -445,38 +465,27 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,}/*-*Major/minorpagefaultaccountingisonlydoneonthe-*initialattempt.Ifwegothrougharetry,itisextremely-*likelythatthepagewillbefoundinpagecacheatthatpoint.+*Major/minorpagefaultaccounting.*/-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);+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)){-u32page_ins;--preempt_disable();-page_ins=be32_to_cpu(get_lppaca()->page_ins);-page_ins+=1<<PAGE_FACTOR;-get_lppaca()->page_ins=cpu_to_be32(page_ins);-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-*ofstarvation.*/-flags&=~FAULT_FLAG_ALLOW_RETRY;-flags|=FAULT_FLAG_TRIED;-gotoretry;+if(firmware_has_feature(FW_FEATURE_CMO)){+u32page_ins;++preempt_disable();+page_ins=be32_to_cpu(get_lppaca()->page_ins);+page_ins+=1<<PAGE_FACTOR;+get_lppaca()->page_ins=cpu_to_be32(page_ins);+preempt_enable();}+#endif /* CONFIG_PPC_SMLPAR */+}else{+current->min_flt++;+perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN,1,+regs,address);}up_read(&mm->mmap_sem);
Since the fault retry is now handled earlier, we can release the
mmap_sem lock earlier too and remove later unlocking previously done in
mm_fault_error().
Signed-off-by: Laurent Dufour <redacted>
---
arch/powerpc/mm/fault.c | 19 ++++---------------
1 file changed, 4 insertions(+), 15 deletions(-)
@@ -151,13 +151,6 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)*continuethepagefault.*/if(fatal_signal_pending(current)){-/*-*Ifwehaveretryset,themmapsemaphorewillhave-*alradybeenreleasedin__lock_page_or_retry().Else-*wereleaseitnow.-*/-if(!(fault&VM_FAULT_RETRY))-up_read(¤t->mm->mmap_sem);/* Coming from kernel, we need to deal with uaccess fixups */if(user_mode(regs))returnMM_FAULT_RETURN;
@@ -170,8 +163,6 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)/* Out of memory */if(fault&VM_FAULT_OOM){-up_read(¤t->mm->mmap_sem);-/**Weranoutofmemory,orsomeotherthinghappenedtousthat*madeusunabletohandlethepagefaultgracefully.
@@ -182,10 +173,8 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)returnMM_FAULT_RETURN;}-if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)){-up_read(¤t->mm->mmap_sem);+if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE))returndo_sigbus(regs,addr,fault);-}/* We don't understand the fault code, this is fatal */BUG();
@@ -452,11 +441,12 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,gotoretry;}/* We will enter mm_fault_error() below */-}+}else+up_read(¤t->mm->mmap_sem);if(unlikely(fault&(VM_FAULT_RETRY|VM_FAULT_ERROR))){if(fault&VM_FAULT_SIGSEGV)-gotobad_area;+gotobad_area_nosemaphore;rc=mm_fault_error(regs,address,fault);if(rc>=MM_FAULT_RETURN)gotobail;
@@ -488,7 +478,6 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,regs,address);}-up_read(&mm->mmap_sem);gotobail;bad_area:
@@ -119,8 +119,6 @@ static int do_sigbus(struct pt_regs *regs, unsigned long address,siginfo_tinfo;unsignedintlsb=0;-up_read(¤t->mm->mmap_sem);-if(!user_mode(regs))returnMM_FAULT_ERR(SIGBUS);
@@ -184,8 +182,10 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)returnMM_FAULT_RETURN;}-if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE))+if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)){+up_read(¤t->mm->mmap_sem);returndo_sigbus(regs,addr,fault);+}/* We don't understand the fault code, this is fatal */BUG();
Kindly ping...
On 14/02/2017 17:45, Laurent Dufour wrote:
This series attempts to clean the page fault handler in the way it has
been done previously for the x86 architecture [1].
The goal is to manage the mmap_sem earlier and only in
do_page_fault(). This done by handling the retry case earlier, before
handling the error case. This way the semaphore can be released
earlier and the error path processed without holding it.
The first patch is just moving a unlock to the caller of the service,
which as no functional impact.
The second patch is handling the retry case earlier in
do_page_fault(). This is where most the change are done, but I was
conservative here, not changing the use of mm_fault_error() in the
case of the second retry. It may be smarter to handle that case
separately but this may create duplicate code.
The last patch is moving up semaphore releasing from mm_fault_error()
to do_page_fault().
[1] see commits from Linus Torvalds
26178ec11ef3 ("x86: mm: consolidate VM_FAULT_RETRY handling")
7fb08eca4527 ("x86: mm: move mmap_sem unlock from mm_fault_error() to
caller")
Laurent Dufour (3):
powerpc/mm: move mmap_sem unlock up from do_sigbus
powerpc/mm: handle VM_FAULT_RETRY earlier
powerpc/mm: move mmap_sem unlocking in do_page_fault()
arch/powerpc/mm/fault.c | 82 ++++++++++++++++++++++++-------------------------
1 file changed, 40 insertions(+), 42 deletions(-)
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2017-03-03 13:13:59
Laurent Dufour [off-list ref] writes:
Kindly ping...
On my list for 4.12, but still dealing with the mess that is 4.11.
Will review it in the next few weeks, unless someone else beats me to it
... ;)
cheers
@@ -119,8 +119,6 @@ static int do_sigbus(struct pt_regs *regs, unsigned long address,siginfo_tinfo;unsignedintlsb=0;-up_read(¤t->mm->mmap_sem);-if(!user_mode(regs))returnMM_FAULT_ERR(SIGBUS);
@@ -184,8 +182,10 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)returnMM_FAULT_RETURN;}-if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE))+if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)){+up_read(¤t->mm->mmap_sem);returndo_sigbus(regs,addr,fault);+}/* We don't understand the fault code, this is fatal */BUG();
In do_page_fault() if handle_mm_fault() returns VM_FAULT_RETRY, retry
the page fault handling before anything else.
This would simplify the handling of the mmap_sem lock in this part of
the code.
Signed-off-by: Laurent Dufour <redacted>
---
arch/powerpc/mm/fault.c | 67 ++++++++++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 29 deletions(-)
@@ -434,6 +434,26 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,*thefault.*/fault=handle_mm_fault(vma,address,flags);++/*+*Handletheretryrightnow,themmap_semhasbeenreleasedinthat+*case.+*/+if(unlikely(fault&VM_FAULT_RETRY)){+/* We retry only once */+if(flags&FAULT_FLAG_ALLOW_RETRY){+/*+*ClearFAULT_FLAG_ALLOW_RETRYtoavoidanyrisk+*ofstarvation.+*/+flags&=~FAULT_FLAG_ALLOW_RETRY;+flags|=FAULT_FLAG_TRIED;+if(!fatal_signal_pending(current))+gotoretry;+}+/* We will enter mm_fault_error() below */+}+if(unlikely(fault&(VM_FAULT_RETRY|VM_FAULT_ERROR))){if(fault&VM_FAULT_SIGSEGV)gotobad_area;
@@ -445,38 +465,27 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,}
We could make it further simpler, by handling the FAULT_RETRY without
FLAG_ALLOW_RETRY set earlier. But i guess that can be done later ?
Reviewed-by: Aneesh Kumar K.V <redacted>
/*
- * 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.
+ * Major/minor page fault accounting.
*/
- 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);
+ 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)) {
- u32 page_ins;
-
- preempt_disable();
- page_ins = be32_to_cpu(get_lppaca()->page_ins);
- page_ins += 1 << PAGE_FACTOR;
- get_lppaca()->page_ins = cpu_to_be32(page_ins);
- 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;
- flags |= FAULT_FLAG_TRIED;
- goto retry;
+ if (firmware_has_feature(FW_FEATURE_CMO)) {
+ u32 page_ins;
+
+ preempt_disable();
+ page_ins = be32_to_cpu(get_lppaca()->page_ins);
+ page_ins += 1 << PAGE_FACTOR;
+ get_lppaca()->page_ins = cpu_to_be32(page_ins);
+ preempt_enable();
}
+#endif /* CONFIG_PPC_SMLPAR */
+ } else {
+ current->min_flt++;
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
+ regs, address);
}
up_read(&mm->mmap_sem);
--
2.7.4
Since the fault retry is now handled earlier, we can release the
mmap_sem lock earlier too and remove later unlocking previously done in
mm_fault_error().
@@ -151,13 +151,6 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)*continuethepagefault.*/if(fatal_signal_pending(current)){-/*-*Ifwehaveretryset,themmapsemaphorewillhave-*alradybeenreleasedin__lock_page_or_retry().Else-*wereleaseitnow.-*/-if(!(fault&VM_FAULT_RETRY))-up_read(¤t->mm->mmap_sem);/* Coming from kernel, we need to deal with uaccess fixups */if(user_mode(regs))returnMM_FAULT_RETURN;
@@ -170,8 +163,6 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)/* Out of memory */if(fault&VM_FAULT_OOM){-up_read(¤t->mm->mmap_sem);-/**Weranoutofmemory,orsomeotherthinghappenedtousthat*madeusunabletohandlethepagefaultgracefully.
@@ -182,10 +173,8 @@ static int mm_fault_error(struct pt_regs *regs, unsigned long addr, int fault)returnMM_FAULT_RETURN;}-if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE)){-up_read(¤t->mm->mmap_sem);+if(fault&(VM_FAULT_SIGBUS|VM_FAULT_HWPOISON|VM_FAULT_HWPOISON_LARGE))returndo_sigbus(regs,addr,fault);-}/* We don't understand the fault code, this is fatal */BUG();
@@ -452,11 +441,12 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,gotoretry;}/* We will enter mm_fault_error() below */-}+}else+up_read(¤t->mm->mmap_sem);if(unlikely(fault&(VM_FAULT_RETRY|VM_FAULT_ERROR))){if(fault&VM_FAULT_SIGSEGV)-gotobad_area;+gotobad_area_nosemaphore;rc=mm_fault_error(regs,address,fault);if(rc>=MM_FAULT_RETURN)gotobail;
@@ -488,7 +478,6 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,regs,address);}-up_read(&mm->mmap_sem);gotobail;bad_area:
In do_page_fault() if handle_mm_fault() returns VM_FAULT_RETRY, retry
the page fault handling before anything else.
This would simplify the handling of the mmap_sem lock in this part of
the code.
Signed-off-by: Laurent Dufour <redacted>
---
arch/powerpc/mm/fault.c | 67 ++++++++++++++++++++++++++++---------------------
1 file changed, 38 insertions(+), 29 deletions(-)
@@ -434,6 +434,26 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,*thefault.*/fault=handle_mm_fault(vma,address,flags);++/*+*Handletheretryrightnow,themmap_semhasbeenreleasedinthat+*case.+*/+if(unlikely(fault&VM_FAULT_RETRY)){+/* We retry only once */+if(flags&FAULT_FLAG_ALLOW_RETRY){+/*+*ClearFAULT_FLAG_ALLOW_RETRYtoavoidanyrisk+*ofstarvation.+*/+flags&=~FAULT_FLAG_ALLOW_RETRY;+flags|=FAULT_FLAG_TRIED;+if(!fatal_signal_pending(current))+gotoretry;+}+/* We will enter mm_fault_error() below */+}+if(unlikely(fault&(VM_FAULT_RETRY|VM_FAULT_ERROR))){if(fault&VM_FAULT_SIGSEGV)gotobad_area;
@@ -445,38 +465,27 @@ int do_page_fault(struct pt_regs *regs, unsigned long address,}
We could make it further simpler, by handling the FAULT_RETRY without
FLAG_ALLOW_RETRY set earlier. But i guess that can be done later ?
Thanks for the review,
I agree that double checking against VM_FAULT_RETRY is confusing here.
But handling all the retry path in the first if() statement means that
we'll have to handle part of the mm_fault_error() code and segv here...
Unless we can't identify what is really relevant in that retry path.
It would take time to review all that tricky part, but I agree it should
be simplified later.
Reviewed-by: Aneesh Kumar K.V <redacted>
quoted
/*
- * 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.
+ * Major/minor page fault accounting.
*/
- 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);
+ 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)) {
- u32 page_ins;
-
- preempt_disable();
- page_ins = be32_to_cpu(get_lppaca()->page_ins);
- page_ins += 1 << PAGE_FACTOR;
- get_lppaca()->page_ins = cpu_to_be32(page_ins);
- 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;
- flags |= FAULT_FLAG_TRIED;
- goto retry;
+ if (firmware_has_feature(FW_FEATURE_CMO)) {
+ u32 page_ins;
+
+ preempt_disable();
+ page_ins = be32_to_cpu(get_lppaca()->page_ins);
+ page_ins += 1 << PAGE_FACTOR;
+ get_lppaca()->page_ins = cpu_to_be32(page_ins);
+ preempt_enable();
}
+#endif /* CONFIG_PPC_SMLPAR */
+ } else {
+ current->min_flt++;
+ perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
+ regs, address);
}
up_read(&mm->mmap_sem);
--
2.7.4