diff mbox

[2/2] arm64/mm: add speculative page fault

Message ID 1525247672-2165-2-git-send-email-opensource.ganesh@gmail.com
State New, archived
Headers show

Commit Message

Mahendran Ganesh May 2, 2018, 7:54 a.m. UTC
This patch enables the speculative page fault on the arm64
architecture.

I completed spf porting in 4.9. From the test result,
we can see app launching time improved by about 10% in average.
For the apps which have more than 50 threads, 15% or even more
improvement can be got.

Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
---
This patch is on top of Laurent's v10 spf
---
 arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
 1 file changed, 35 insertions(+), 3 deletions(-)

Comments

Laurent Dufour May 2, 2018, 9:07 a.m. UTC | #1
On 02/05/2018 09:54, Ganesh Mahendran wrote:
> This patch enables the speculative page fault on the arm64
> architecture.
> 
> I completed spf porting in 4.9. From the test result,
> we can see app launching time improved by about 10% in average.
> For the apps which have more than 50 threads, 15% or even more
> improvement can be got.

Thanks Ganesh,

That's a great improvement, could you please provide details about the apps and
the hardware you used ?

> 
> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
> ---
> This patch is on top of Laurent's v10 spf
> ---
>  arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>  1 file changed, 35 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index 4165485..e7992a3 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
> 
>  static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>  			   unsigned int mm_flags, unsigned long vm_flags,
> -			   struct task_struct *tsk)
> +			   struct task_struct *tsk, struct vm_area_struct *vma)
>  {
> -	struct vm_area_struct *vma;
>  	int fault;
> 
> +	if (!vma || !can_reuse_spf_vma(vma, addr))
> +		vma = find_vma(mm, addr);
> +
>  	vma = find_vma(mm, addr);
>  	fault = VM_FAULT_BADMAP;
>  	if (unlikely(!vma))
> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  	int fault, major = 0;
>  	unsigned long vm_flags = VM_READ | VM_WRITE;
>  	unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
> +	struct vm_area_struct *vma;
> 
>  	if (notify_page_fault(regs, esr))
>  		return 0;
> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
> 
>  	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
> 
> +	if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {

As suggested by Punit in his v10's review, the test on
CONFIG_SPECULATIVE_PAGE_FAULT is not needed as handle_speculative_fault() is
defined to return VM_FAULT_RETRY is the config is not set.

> +		fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
> +		/*
> +		 * Page fault is done if VM_FAULT_RETRY is not returned.
> +		 * But if the memory protection keys are active, we don't know
> +		 * if the fault is due to key mistmatch or due to a
> +		 * classic protection check.
> +		 * To differentiate that, we will need the VMA we no
> +		 * more have, so let's retry with the mmap_sem held.
> +		 */

The check of VM_FAULT_SIGSEGV was needed on ppc64 because of the memory
protection key support, but as far as I know, this is not the case on arm64.
Isn't it ?

> +		if (fault != VM_FAULT_RETRY &&
> +			 fault != VM_FAULT_SIGSEGV) {
> +			perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
> +			goto done;
> +		}
> +	} else {
> +		vma = NULL;
> +	}
> +
>  	/*
>  	 * As per x86, we may deadlock here. However, since the kernel only
>  	 * validly references user space from well defined areas of the code,
> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  #endif
>  	}
> 
> -	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
> +	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>  	major |= fault & VM_FAULT_MAJOR;
> 
>  	if (fault & VM_FAULT_RETRY) {
> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  		if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>  			mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>  			mm_flags |= FAULT_FLAG_TRIED;
> +
> +			/*
> +			 * Do not try to reuse this vma and fetch it
> +			 * again since we will release the mmap_sem.
> +			 */
> +			if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
> +				vma = NULL;
> +
>  			goto retry;
>  		}
>  	}
>  	up_read(&mm->mmap_sem);
> 
> +done:
> +
>  	/*
>  	 * Handle the "normal" (no error) case first.
>  	 */
>
kernel test robot May 2, 2018, 2:07 p.m. UTC | #2
Hi Ganesh,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on arm64/for-next/core]
[also build test ERROR on v4.17-rc3 next-20180502]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Ganesh-Mahendran/arm64-mm-define-ARCH_SUPPORTS_SPECULATIVE_PAGE_FAULT/20180502-183036
base:   https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-next/core
config: arm64-defconfig (attached as .config)
compiler: aarch64-linux-gnu-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm64 

All errors (new ones prefixed by >>):

   arch/arm64/mm/fault.c: In function '__do_page_fault':
>> arch/arm64/mm/fault.c:329:15: error: implicit declaration of function 'can_reuse_spf_vma' [-Werror=implicit-function-declaration]
     if (!vma || !can_reuse_spf_vma(vma, addr))
                  ^~~~~~~~~~~~~~~~~
   arch/arm64/mm/fault.c: In function 'do_page_fault':
>> arch/arm64/mm/fault.c:416:11: error: implicit declaration of function 'handle_speculative_fault'; did you mean 'handle_mm_fault'? [-Werror=implicit-function-declaration]
      fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
              ^~~~~~~~~~~~~~~~~~~~~~~~
              handle_mm_fault
>> arch/arm64/mm/fault.c:427:18: error: 'PERF_COUNT_SW_SPF' undeclared (first use in this function); did you mean 'PERF_COUNT_SW_MAX'?
       perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
                     ^~~~~~~~~~~~~~~~~
                     PERF_COUNT_SW_MAX
   arch/arm64/mm/fault.c:427:18: note: each undeclared identifier is reported only once for each function it appears in
   cc1: some warnings being treated as errors

vim +/can_reuse_spf_vma +329 arch/arm64/mm/fault.c

   322	
   323	static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
   324				   unsigned int mm_flags, unsigned long vm_flags,
   325				   struct task_struct *tsk, struct vm_area_struct *vma)
   326	{
   327		int fault;
   328	
 > 329		if (!vma || !can_reuse_spf_vma(vma, addr))
   330			vma = find_vma(mm, addr);
   331	
   332		vma = find_vma(mm, addr);
   333		fault = VM_FAULT_BADMAP;
   334		if (unlikely(!vma))
   335			goto out;
   336		if (unlikely(vma->vm_start > addr))
   337			goto check_stack;
   338	
   339		/*
   340		 * Ok, we have a good vm_area for this memory access, so we can handle
   341		 * it.
   342		 */
   343	good_area:
   344		/*
   345		 * Check that the permissions on the VMA allow for the fault which
   346		 * occurred.
   347		 */
   348		if (!(vma->vm_flags & vm_flags)) {
   349			fault = VM_FAULT_BADACCESS;
   350			goto out;
   351		}
   352	
   353		return handle_mm_fault(vma, addr & PAGE_MASK, mm_flags);
   354	
   355	check_stack:
   356		if (vma->vm_flags & VM_GROWSDOWN && !expand_stack(vma, addr))
   357			goto good_area;
   358	out:
   359		return fault;
   360	}
   361	
   362	static bool is_el0_instruction_abort(unsigned int esr)
   363	{
   364		return ESR_ELx_EC(esr) == ESR_ELx_EC_IABT_LOW;
   365	}
   366	
   367	static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
   368					   struct pt_regs *regs)
   369	{
   370		struct task_struct *tsk;
   371		struct mm_struct *mm;
   372		struct siginfo si;
   373		int fault, major = 0;
   374		unsigned long vm_flags = VM_READ | VM_WRITE;
   375		unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
   376		struct vm_area_struct *vma;
   377	
   378		if (notify_page_fault(regs, esr))
   379			return 0;
   380	
   381		tsk = current;
   382		mm  = tsk->mm;
   383	
   384		/*
   385		 * If we're in an interrupt or have no user context, we must not take
   386		 * the fault.
   387		 */
   388		if (faulthandler_disabled() || !mm)
   389			goto no_context;
   390	
   391		if (user_mode(regs))
   392			mm_flags |= FAULT_FLAG_USER;
   393	
   394		if (is_el0_instruction_abort(esr)) {
   395			vm_flags = VM_EXEC;
   396		} else if ((esr & ESR_ELx_WNR) && !(esr & ESR_ELx_CM)) {
   397			vm_flags = VM_WRITE;
   398			mm_flags |= FAULT_FLAG_WRITE;
   399		}
   400	
   401		if (addr < TASK_SIZE && is_permission_fault(esr, regs, addr)) {
   402			/* regs->orig_addr_limit may be 0 if we entered from EL0 */
   403			if (regs->orig_addr_limit == KERNEL_DS)
   404				die("Accessing user space memory with fs=KERNEL_DS", regs, esr);
   405	
   406			if (is_el1_instruction_abort(esr))
   407				die("Attempting to execute userspace memory", regs, esr);
   408	
   409			if (!search_exception_tables(regs->pc))
   410				die("Accessing user space memory outside uaccess.h routines", regs, esr);
   411		}
   412	
   413		perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
   414	
   415		if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
 > 416			fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
   417			/*
   418			 * Page fault is done if VM_FAULT_RETRY is not returned.
   419			 * But if the memory protection keys are active, we don't know
   420			 * if the fault is due to key mistmatch or due to a
   421			 * classic protection check.
   422			 * To differentiate that, we will need the VMA we no
   423			 * more have, so let's retry with the mmap_sem held.
   424			 */
   425			if (fault != VM_FAULT_RETRY &&
   426				 fault != VM_FAULT_SIGSEGV) {
 > 427				perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
   428				goto done;
   429			}
   430		} else {
   431			vma = NULL;
   432		}
   433	
   434		/*
   435		 * As per x86, we may deadlock here. However, since the kernel only
   436		 * validly references user space from well defined areas of the code,
   437		 * we can bug out early if this is from code which shouldn't.
   438		 */
   439		if (!down_read_trylock(&mm->mmap_sem)) {
   440			if (!user_mode(regs) && !search_exception_tables(regs->pc))
   441				goto no_context;
   442	retry:
   443			down_read(&mm->mmap_sem);
   444		} else {
   445			/*
   446			 * The above down_read_trylock() might have succeeded in which
   447			 * case, we'll have missed the might_sleep() from down_read().
   448			 */
   449			might_sleep();
   450	#ifdef CONFIG_DEBUG_VM
   451			if (!user_mode(regs) && !search_exception_tables(regs->pc))
   452				goto no_context;
   453	#endif
   454		}
   455	
   456		fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
   457		major |= fault & VM_FAULT_MAJOR;
   458	
   459		if (fault & VM_FAULT_RETRY) {
   460			/*
   461			 * If we need to retry but a fatal signal is pending,
   462			 * handle the signal first. We do not need to release
   463			 * the mmap_sem because it would already be released
   464			 * in __lock_page_or_retry in mm/filemap.c.
   465			 */
   466			if (fatal_signal_pending(current)) {
   467				if (!user_mode(regs))
   468					goto no_context;
   469				return 0;
   470			}
   471	
   472			/*
   473			 * Clear FAULT_FLAG_ALLOW_RETRY to avoid any risk of
   474			 * starvation.
   475			 */
   476			if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
   477				mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
   478				mm_flags |= FAULT_FLAG_TRIED;
   479	
   480				/*
   481				 * Do not try to reuse this vma and fetch it
   482				 * again since we will release the mmap_sem.
   483				 */
   484				if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
   485					vma = NULL;
   486	
   487				goto retry;
   488			}
   489		}
   490		up_read(&mm->mmap_sem);
   491	
   492	done:
   493	
   494		/*
   495		 * Handle the "normal" (no error) case first.
   496		 */
   497		if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP |
   498				      VM_FAULT_BADACCESS)))) {
   499			/*
   500			 * Major/minor page fault accounting is only done
   501			 * once. If we go through a retry, it is extremely
   502			 * likely that the page will be found in page cache at
   503			 * that point.
   504			 */
   505			if (major) {
   506				tsk->maj_flt++;
   507				perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1, regs,
   508					      addr);
   509			} else {
   510				tsk->min_flt++;
   511				perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1, regs,
   512					      addr);
   513			}
   514	
   515			return 0;
   516		}
   517	
   518		/*
   519		 * If we are in kernel mode at this point, we have no context to
   520		 * handle this fault with.
   521		 */
   522		if (!user_mode(regs))
   523			goto no_context;
   524	
   525		if (fault & VM_FAULT_OOM) {
   526			/*
   527			 * We ran out of memory, call the OOM killer, and return to
   528			 * userspace (which will retry the fault, or kill us if we got
   529			 * oom-killed).
   530			 */
   531			pagefault_out_of_memory();
   532			return 0;
   533		}
   534	
   535		clear_siginfo(&si);
   536		si.si_addr = (void __user *)addr;
   537	
   538		if (fault & VM_FAULT_SIGBUS) {
   539			/*
   540			 * We had some memory, but were unable to successfully fix up
   541			 * this page fault.
   542			 */
   543			si.si_signo	= SIGBUS;
   544			si.si_code	= BUS_ADRERR;
   545		} else if (fault & VM_FAULT_HWPOISON_LARGE) {
   546			unsigned int hindex = VM_FAULT_GET_HINDEX(fault);
   547	
   548			si.si_signo	= SIGBUS;
   549			si.si_code	= BUS_MCEERR_AR;
   550			si.si_addr_lsb	= hstate_index_to_shift(hindex);
   551		} else if (fault & VM_FAULT_HWPOISON) {
   552			si.si_signo	= SIGBUS;
   553			si.si_code	= BUS_MCEERR_AR;
   554			si.si_addr_lsb	= PAGE_SHIFT;
   555		} else {
   556			/*
   557			 * Something tried to access memory that isn't in our memory
   558			 * map.
   559			 */
   560			si.si_signo	= SIGSEGV;
   561			si.si_code	= fault == VM_FAULT_BADACCESS ?
   562					  SEGV_ACCERR : SEGV_MAPERR;
   563		}
   564	
   565		__do_user_fault(&si, esr);
   566		return 0;
   567	
   568	no_context:
   569		__do_kernel_fault(addr, esr, regs);
   570		return 0;
   571	}
   572	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Punit Agrawal May 2, 2018, 2:46 p.m. UTC | #3
Hi Ganesh,

I was looking at evaluating speculative page fault handling on arm64 and
noticed your patch.

Some comments below -

Ganesh Mahendran <opensource.ganesh@gmail.com> writes:

> This patch enables the speculative page fault on the arm64
> architecture.
>
> I completed spf porting in 4.9. From the test result,
> we can see app launching time improved by about 10% in average.
> For the apps which have more than 50 threads, 15% or even more
> improvement can be got.
>
> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
> ---
> This patch is on top of Laurent's v10 spf
> ---
>  arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>  1 file changed, 35 insertions(+), 3 deletions(-)
>
> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
> index 4165485..e7992a3 100644
> --- a/arch/arm64/mm/fault.c
> +++ b/arch/arm64/mm/fault.c
> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
>  
>  static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>  			   unsigned int mm_flags, unsigned long vm_flags,
> -			   struct task_struct *tsk)
> +			   struct task_struct *tsk, struct vm_area_struct *vma)
>  {
> -	struct vm_area_struct *vma;
>  	int fault;
>  
> +	if (!vma || !can_reuse_spf_vma(vma, addr))
> +		vma = find_vma(mm, addr);
> +

It would be better to move this hunk to do_page_fault().

It'll help localise the fact that handle_speculative_fault() is a
stateful call which needs a corresponding can_reuse_spf_vma() to
properly update the vma reference counting.


>  	vma = find_vma(mm, addr);

Remember to drop this call in the next version. As it stands the call
the find_vma() needlessly gets duplicated.

>  	fault = VM_FAULT_BADMAP;
>  	if (unlikely(!vma))
> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  	int fault, major = 0;
>  	unsigned long vm_flags = VM_READ | VM_WRITE;
>  	unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
> +	struct vm_area_struct *vma;
>  
>  	if (notify_page_fault(regs, esr))
>  		return 0;
> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  
>  	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
>  
> +	if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {

You don't need the IS_ENABLED() check. The alternate implementation of
handle_speculative_fault() when CONFIG_SPECULATIVE_PAGE_FAULT is not
enabled takes care of this.

> +		fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
> +		/*
> +		 * Page fault is done if VM_FAULT_RETRY is not returned.
> +		 * But if the memory protection keys are active, we don't know
> +		 * if the fault is due to key mistmatch or due to a
> +		 * classic protection check.
> +		 * To differentiate that, we will need the VMA we no
> +		 * more have, so let's retry with the mmap_sem held.
> +		 */

As there is no support for memory protection keys on arm64 most of this
comment can be dropped.

> +		if (fault != VM_FAULT_RETRY &&
> +			 fault != VM_FAULT_SIGSEGV) {

Not sure if you need the VM_FAULT_SIGSEGV here.

> +			perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
> +			goto done;
> +		}
> +	} else {
> +		vma = NULL;
> +	}
> +

If vma is initiliased to NULL during declaration, the else part can be
dropped.

>  	/*
>  	 * As per x86, we may deadlock here. However, since the kernel only
>  	 * validly references user space from well defined areas of the code,
> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  #endif
>  	}
>  
> -	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
> +	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>  	major |= fault & VM_FAULT_MAJOR;
>  
>  	if (fault & VM_FAULT_RETRY) {
> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>  		if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>  			mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>  			mm_flags |= FAULT_FLAG_TRIED;
> +
> +			/*
> +			 * Do not try to reuse this vma and fetch it
> +			 * again since we will release the mmap_sem.
> +			 */
> +			if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
> +				vma = NULL;

Please drop the IS_ENABLED() check.

Thanks,
Punit

> +
>  			goto retry;
>  		}
>  	}
>  	up_read(&mm->mmap_sem);
>  
> +done:
> +
>  	/*
>  	 * Handle the "normal" (no error) case first.
>  	 */
Mahendran Ganesh May 4, 2018, 6:25 a.m. UTC | #4
2018-05-02 17:07 GMT+08:00 Laurent Dufour <ldufour@linux.vnet.ibm.com>:
> On 02/05/2018 09:54, Ganesh Mahendran wrote:
>> This patch enables the speculative page fault on the arm64
>> architecture.
>>
>> I completed spf porting in 4.9. From the test result,
>> we can see app launching time improved by about 10% in average.
>> For the apps which have more than 50 threads, 15% or even more
>> improvement can be got.
>
> Thanks Ganesh,
>
> That's a great improvement, could you please provide details about the apps and
> the hardware you used ?

We run spf on Qcom SDM845(kernel 4.9). Below is app(popular in China)
list we tested:
------
com.tencent.mobileqq
com.tencent.qqmusic
com.tencent.mtt
com.UCMobile
com.qiyi.video
com.baidu.searchbox
com.baidu.BaiduMap
tv.danmaku.bili
com.sdu.didi.psnger
com.ss.android.ugc.aweme
air.tv.douyu.android
me.ele
com.autonavi.minimap
com.duowan.kiwi
com.v.study
com.qqgame.hlddz
com.ss.android.article.lite
com.jingdong.app.mall
com.tencent.tmgp.pubgmhd
com.kugou.android
com.kuaikan.comic
com.hunantv.imgo.activity
com.mt.mtxx.mtxx
com.sankuai.meituan
com.sankuai.meituan.takeoutnew
com.tencent.karaoke
com.taobao.taobao
com.tencent.qqlive
com.tmall.wireless
com.tencent.tmgp.sgame
com.netease.cloudmusic
com.sina.weibo
com.tencent.mm
com.immomo.momo
com.xiaomi.hm.health
com.youku.phone
com.eg.android.AlipayGphone
com.meituan.qcs.c.android
------

We will do more test of the V10 spf.

>
>>
>> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
>> ---
>> This patch is on top of Laurent's v10 spf
>> ---
>>  arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>>  1 file changed, 35 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>> index 4165485..e7992a3 100644
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
>>
>>  static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>>                          unsigned int mm_flags, unsigned long vm_flags,
>> -                        struct task_struct *tsk)
>> +                        struct task_struct *tsk, struct vm_area_struct *vma)
>>  {
>> -     struct vm_area_struct *vma;
>>       int fault;
>>
>> +     if (!vma || !can_reuse_spf_vma(vma, addr))
>> +             vma = find_vma(mm, addr);
>> +
>>       vma = find_vma(mm, addr);
>>       fault = VM_FAULT_BADMAP;
>>       if (unlikely(!vma))
>> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>       int fault, major = 0;
>>       unsigned long vm_flags = VM_READ | VM_WRITE;
>>       unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
>> +     struct vm_area_struct *vma;
>>
>>       if (notify_page_fault(regs, esr))
>>               return 0;
>> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>
>>       perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
>>
>> +     if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
>
> As suggested by Punit in his v10's review, the test on
> CONFIG_SPECULATIVE_PAGE_FAULT is not needed as handle_speculative_fault() is
> defined to return VM_FAULT_RETRY is the config is not set.

Thanks, will fix.

>
>> +             fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
>> +             /*
>> +              * Page fault is done if VM_FAULT_RETRY is not returned.
>> +              * But if the memory protection keys are active, we don't know
>> +              * if the fault is due to key mistmatch or due to a
>> +              * classic protection check.
>> +              * To differentiate that, we will need the VMA we no
>> +              * more have, so let's retry with the mmap_sem held.
>> +              */
>
> The check of VM_FAULT_SIGSEGV was needed on ppc64 because of the memory
> protection key support, but as far as I know, this is not the case on arm64.
> Isn't it ?

Yes, wil fix.

>
>> +             if (fault != VM_FAULT_RETRY &&
>> +                      fault != VM_FAULT_SIGSEGV) {
>> +                     perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
>> +                     goto done;
>> +             }
>> +     } else {
>> +             vma = NULL;
>> +     }
>> +
>>       /*
>>        * As per x86, we may deadlock here. However, since the kernel only
>>        * validly references user space from well defined areas of the code,
>> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>  #endif
>>       }
>>
>> -     fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
>> +     fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>>       major |= fault & VM_FAULT_MAJOR;
>>
>>       if (fault & VM_FAULT_RETRY) {
>> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>               if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>>                       mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>>                       mm_flags |= FAULT_FLAG_TRIED;
>> +
>> +                     /*
>> +                      * Do not try to reuse this vma and fetch it
>> +                      * again since we will release the mmap_sem.
>> +                      */
>> +                     if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
>> +                             vma = NULL;
>> +
>>                       goto retry;
>>               }
>>       }
>>       up_read(&mm->mmap_sem);
>>
>> +done:
>> +
>>       /*
>>        * Handle the "normal" (no error) case first.
>>        */
>>
>
Mahendran Ganesh May 4, 2018, 6:31 a.m. UTC | #5
2018-05-02 22:46 GMT+08:00 Punit Agrawal <punit.agrawal@arm.com>:
> Hi Ganesh,
>
> I was looking at evaluating speculative page fault handling on arm64 and
> noticed your patch.
>
> Some comments below -

Thanks for your review.

>
> Ganesh Mahendran <opensource.ganesh@gmail.com> writes:
>
>> This patch enables the speculative page fault on the arm64
>> architecture.
>>
>> I completed spf porting in 4.9. From the test result,
>> we can see app launching time improved by about 10% in average.
>> For the apps which have more than 50 threads, 15% or even more
>> improvement can be got.
>>
>> Signed-off-by: Ganesh Mahendran <opensource.ganesh@gmail.com>
>> ---
>> This patch is on top of Laurent's v10 spf
>> ---
>>  arch/arm64/mm/fault.c | 38 +++++++++++++++++++++++++++++++++++---
>>  1 file changed, 35 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
>> index 4165485..e7992a3 100644
>> --- a/arch/arm64/mm/fault.c
>> +++ b/arch/arm64/mm/fault.c
>> @@ -322,11 +322,13 @@ static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
>>
>>  static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
>>                          unsigned int mm_flags, unsigned long vm_flags,
>> -                        struct task_struct *tsk)
>> +                        struct task_struct *tsk, struct vm_area_struct *vma)
>>  {
>> -     struct vm_area_struct *vma;
>>       int fault;
>>
>> +     if (!vma || !can_reuse_spf_vma(vma, addr))
>> +             vma = find_vma(mm, addr);
>> +
>
> It would be better to move this hunk to do_page_fault().
>
> It'll help localise the fact that handle_speculative_fault() is a
> stateful call which needs a corresponding can_reuse_spf_vma() to
> properly update the vma reference counting.

Yes, your suggestion is better.

>
>
>>       vma = find_vma(mm, addr);
>
> Remember to drop this call in the next version. As it stands the call
> the find_vma() needlessly gets duplicated.

Will fix

>
>>       fault = VM_FAULT_BADMAP;
>>       if (unlikely(!vma))
>> @@ -371,6 +373,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>       int fault, major = 0;
>>       unsigned long vm_flags = VM_READ | VM_WRITE;
>>       unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
>> +     struct vm_area_struct *vma;
>>
>>       if (notify_page_fault(regs, esr))
>>               return 0;
>> @@ -409,6 +412,25 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>
>>       perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
>>
>> +     if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
>
> You don't need the IS_ENABLED() check. The alternate implementation of
> handle_speculative_fault() when CONFIG_SPECULATIVE_PAGE_FAULT is not
> enabled takes care of this.

Will fix

>
>> +             fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
>> +             /*
>> +              * Page fault is done if VM_FAULT_RETRY is not returned.
>> +              * But if the memory protection keys are active, we don't know
>> +              * if the fault is due to key mistmatch or due to a
>> +              * classic protection check.
>> +              * To differentiate that, we will need the VMA we no
>> +              * more have, so let's retry with the mmap_sem held.
>> +              */
>
> As there is no support for memory protection keys on arm64 most of this
> comment can be dropped.

will fix

>
>> +             if (fault != VM_FAULT_RETRY &&
>> +                      fault != VM_FAULT_SIGSEGV) {
>
> Not sure if you need the VM_FAULT_SIGSEGV here.
>
>> +                     perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
>> +                     goto done;
>> +             }
>> +     } else {
>> +             vma = NULL;
>> +     }
>> +
>
> If vma is initiliased to NULL during declaration, the else part can be
> dropped.

will fix

>
>>       /*
>>        * As per x86, we may deadlock here. However, since the kernel only
>>        * validly references user space from well defined areas of the code,
>> @@ -431,7 +453,7 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>  #endif
>>       }
>>
>> -     fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
>> +     fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
>>       major |= fault & VM_FAULT_MAJOR;
>>
>>       if (fault & VM_FAULT_RETRY) {
>> @@ -454,11 +476,21 @@ static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
>>               if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
>>                       mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
>>                       mm_flags |= FAULT_FLAG_TRIED;
>> +
>> +                     /*
>> +                      * Do not try to reuse this vma and fetch it
>> +                      * again since we will release the mmap_sem.
>> +                      */
>> +                     if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
>> +                             vma = NULL;
>
> Please drop the IS_ENABLED() check.

will fix

>
> Thanks,
> Punit
>
>> +
>>                       goto retry;
>>               }
>>       }
>>       up_read(&mm->mmap_sem);
>>
>> +done:
>> +
>>       /*
>>        * Handle the "normal" (no error) case first.
>>        */
diff mbox

Patch

diff --git a/arch/arm64/mm/fault.c b/arch/arm64/mm/fault.c
index 4165485..e7992a3 100644
--- a/arch/arm64/mm/fault.c
+++ b/arch/arm64/mm/fault.c
@@ -322,11 +322,13 @@  static void do_bad_area(unsigned long addr, unsigned int esr, struct pt_regs *re
 
 static int __do_page_fault(struct mm_struct *mm, unsigned long addr,
 			   unsigned int mm_flags, unsigned long vm_flags,
-			   struct task_struct *tsk)
+			   struct task_struct *tsk, struct vm_area_struct *vma)
 {
-	struct vm_area_struct *vma;
 	int fault;
 
+	if (!vma || !can_reuse_spf_vma(vma, addr))
+		vma = find_vma(mm, addr);
+
 	vma = find_vma(mm, addr);
 	fault = VM_FAULT_BADMAP;
 	if (unlikely(!vma))
@@ -371,6 +373,7 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 	int fault, major = 0;
 	unsigned long vm_flags = VM_READ | VM_WRITE;
 	unsigned int mm_flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+	struct vm_area_struct *vma;
 
 	if (notify_page_fault(regs, esr))
 		return 0;
@@ -409,6 +412,25 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 
 	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, addr);
 
+	if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT)) {
+		fault = handle_speculative_fault(mm, addr, mm_flags, &vma);
+		/*
+		 * Page fault is done if VM_FAULT_RETRY is not returned.
+		 * But if the memory protection keys are active, we don't know
+		 * if the fault is due to key mistmatch or due to a
+		 * classic protection check.
+		 * To differentiate that, we will need the VMA we no
+		 * more have, so let's retry with the mmap_sem held.
+		 */
+		if (fault != VM_FAULT_RETRY &&
+			 fault != VM_FAULT_SIGSEGV) {
+			perf_sw_event(PERF_COUNT_SW_SPF, 1, regs, addr);
+			goto done;
+		}
+	} else {
+		vma = NULL;
+	}
+
 	/*
 	 * As per x86, we may deadlock here. However, since the kernel only
 	 * validly references user space from well defined areas of the code,
@@ -431,7 +453,7 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 #endif
 	}
 
-	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk);
+	fault = __do_page_fault(mm, addr, mm_flags, vm_flags, tsk, vma);
 	major |= fault & VM_FAULT_MAJOR;
 
 	if (fault & VM_FAULT_RETRY) {
@@ -454,11 +476,21 @@  static int __kprobes do_page_fault(unsigned long addr, unsigned int esr,
 		if (mm_flags & FAULT_FLAG_ALLOW_RETRY) {
 			mm_flags &= ~FAULT_FLAG_ALLOW_RETRY;
 			mm_flags |= FAULT_FLAG_TRIED;
+
+			/*
+			 * Do not try to reuse this vma and fetch it
+			 * again since we will release the mmap_sem.
+			 */
+			if (IS_ENABLED(CONFIG_SPECULATIVE_PAGE_FAULT))
+				vma = NULL;
+
 			goto retry;
 		}
 	}
 	up_read(&mm->mmap_sem);
 
+done:
+
 	/*
 	 * Handle the "normal" (no error) case first.
 	 */