From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:17:33
As reported by Anton, there is a large penalty to signal handling
performance on radix systems using KUAP. The signal handling code
performs many user access operations, each of which needs to switch the
KUAP permissions bit to open and then close user access. This involves a
costly 'mtspr' operation [0].
There is existing work done on x86 and by Christopher Leroy for PPC32 to
instead open up user access in "blocks" using user_*_access_{begin,end}.
We can do the same in PPC64 to bring performance back up on KUAP-enabled
radix and now also hash MMU systems [1].
Hash MMU KUAP support along with uaccess flush has landed in linuxppc/next
since the last revision. This series also provides a large benefit on hash
with KUAP. However, in the hash implementation of KUAP the user AMR is
always restored during system_call_exception() which cannot be avoided.
Fewer user access switches naturally also result in less uaccess flushing.
The first two patches add some needed 'unsafe' versions of copy-from
functions. While these do not make use of asm-goto they still allow for
avoiding the repeated uaccess switches.
The third patch moves functions called by setup_sigcontext() into a new
prepare_setup_sigcontext() to simplify converting setup_sigcontext()
into an 'unsafe' version which assumes an open uaccess window later.
The fourth and fifths patches clean-up some of the Transactional Memory
ifdef stuff to simplify using uaccess blocks later.
The next two patches rewrite some of the signal64 helper functions to
be 'unsafe'. Finally, the last three patches update the main signal
handling functions to make use of the new 'unsafe' helpers and eliminate
some additional uaccess switching.
I used the will-it-scale signal1 benchmark to measure and compare
performance [2]. The below results are from running a minimal
kernel+initramfs QEMU/KVM guest on a POWER9 Blackbird:
signal1_threads -t1 -s10
| | hash | radix |
| --------------------------- | ------ | ------ |
| linuxppc/next | 118693 | 133296 |
| linuxppc/next w/o KUAP+KUEP | 228911 | 228654 |
| unsafe-signal64 | 199443 | 234716 |
[0]: https://github.com/linuxppc/issues/issues/277
[1]: https://patchwork.ozlabs.org/project/linuxppc-dev/list/?series=196278
[2]: https://github.com/antonblanchard/will-it-scale/blob/master/tests/signal1.c
v4: * Fix issues identified by Christophe Leroy (thanks for review)
* Use __get_user() directly to copy the 8B sigset_t
v3: * Rebase on latest linuxppc/next
* Reword confusing commit messages
* Add missing comma in macro in signal.h which broke compiles without
CONFIG_ALTIVEC
* Validate hash KUAP signal performance improvements
v2: * Rebase on latest linuxppc/next + Christophe Leroy's PPC32
signal series
* Simplify/remove TM ifdefery similar to PPC32 series and clean
up the uaccess begin/end calls
* Isolate non-inline functions so they are not called when
uaccess window is open
Christopher M. Riedl (8):
powerpc/uaccess: Add unsafe_copy_from_user
powerpc/signal: Add unsafe_copy_{vsx,fpr}_from_user()
powerpc/signal64: Move non-inline functions out of setup_sigcontext()
powerpc: Reference param in MSR_TM_ACTIVE() macro
powerpc/signal64: Remove TM ifdefery in middle of if/else block
powerpc/signal64: Replace setup_sigcontext() w/
unsafe_setup_sigcontext()
powerpc/signal64: Replace restore_sigcontext() w/
unsafe_restore_sigcontext()
powerpc/signal64: Use __get_user() to copy sigset_t
Daniel Axtens (2):
powerpc/signal64: Rewrite handle_rt_signal64() to minimise uaccess
switches
powerpc/signal64: Rewrite rt_sigreturn() to minimise uaccess switches
arch/powerpc/include/asm/reg.h | 2 +-
arch/powerpc/include/asm/uaccess.h | 3 +
arch/powerpc/kernel/signal.h | 33 ++++
arch/powerpc/kernel/signal_64.c | 251 ++++++++++++++++++-----------
4 files changed, 196 insertions(+), 93 deletions(-)
--
2.26.1
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:08:27
Usually sigset_t is exactly 8B which is a "trivial" size and does not
warrant using __copy_from_user(). Use __get_user() directly in
anticipation of future work to remove the trivial size optimizations
from __copy_from_user(). Calling __get_user() also results in a small
boost to signal handling throughput here.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:10:12
Unlike the other MSR_TM_* macros, MSR_TM_ACTIVE does not reference or
use its parameter unless CONFIG_PPC_TRANSACTIONAL_MEM is defined. This
causes an 'unused variable' compile warning unless the variable is also
guarded with CONFIG_PPC_TRANSACTIONAL_MEM.
Reference but do nothing with the argument in the macro to avoid a
potential compile warning.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/include/asm/reg.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:12:07
From: Daniel Axtens <redacted>
Add uaccess blocks and use the 'unsafe' versions of functions doing user
access where possible to reduce the number of times uaccess has to be
opened/closed.
Signed-off-by: Daniel Axtens <redacted>
Co-developed-by: Christopher M. Riedl <redacted>
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
@@ -794,10 +798,12 @@ SYSCALL_DEFINE0(rt_sigreturn)/* Trying to start TM on non TM system */if(!cpu_has_feature(CPU_FTR_TM))-gotobadframe;+gotobadframe_block;++unsafe_get_user(uc_transact,&uc->uc_link,badframe_block);++user_read_access_end();-if(__get_user(uc_transact,&uc->uc_link))-gotobadframe;if(restore_tm_sigcontexts(current,&uc->uc_mcontext,&uc_transact->uc_mcontext))gotobadframe;
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:13:45
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:15:39
Just wrap __copy_tofrom_user() for the usual 'unsafe' pattern which
takes in a label to goto on error.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/include/asm/uaccess.h | 3 +++
1 file changed, 3 insertions(+)
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:19:16
Rework the messy ifdef breaking up the if-else for TM similar to
commit f1cf4f93de2f ("powerpc/signal32: Remove ifdefery in middle of if/else").
Unlike that commit for ppc32, the ifdef can't be removed entirely since
uc_transact in sigframe depends on CONFIG_PPC_TRANSACTIONAL_MEM.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
@@ -818,10 +818,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,unsignedlongnewsp=0;longerr=0;structpt_regs*regs=tsk->thread.regs;-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM/* Save the thread's msr before get_tm_stackpointer() changes it */unsignedlongmsr=regs->msr;-#endifframe=get_sigframe(ksig,tsk,sizeof(*frame),0);if(!access_ok(frame,sizeof(*frame)))
@@ -836,8 +834,9 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,/* Create the ucontext. */err|=__put_user(0,&frame->uc.uc_flags);err|=__save_altstack(&frame->uc.uc_stack,regs->gpr[1]);-#ifdef CONFIG_PPC_TRANSACTIONAL_MEM+if(MSR_TM_ACTIVE(msr)){+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM/* The ucontext_t passed to userland points to the second*ucontext_t(fortransactionalstate)withitsuc_linkptr.*/
@@ -847,9 +846,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,tsk,ksig->sig,NULL,(unsignedlong)ksig->ka.sa.sa_handler,msr);-}else#endif-{+}else{err|=__put_user(0,&frame->uc.uc_link);prepare_setup_sigcontext(tsk,1);err|=setup_sigcontext(&frame->uc.uc_mcontext,tsk,ksig->sig,
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:21:03
Previously setup_sigcontext() performed a costly KUAP switch on every
uaccess operation. These repeated uaccess switches cause a significant
drop in signal handling performance.
Rewrite setup_sigcontext() to assume that a userspace write access window
is open. Replace all uaccess functions with their 'unsafe' versions
which avoid the repeated uaccess switches.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 70 ++++++++++++++++++++-------------
1 file changed, 43 insertions(+), 27 deletions(-)
@@ -101,9 +101,13 @@ static void prepare_setup_sigcontext(struct task_struct *tsk, int ctx_has_vsx_re*Setupthesigcontextforthesignalframe.*/-staticlongsetup_sigcontext(structsigcontext__user*sc,-structtask_struct*tsk,intsignr,sigset_t*set,-unsignedlonghandler,intctx_has_vsx_region)+#define unsafe_setup_sigcontext(sc, tsk, signr, set, handler, \+ctx_has_vsx_region,e)\+unsafe_op_wrap(__unsafe_setup_sigcontext(sc,tsk,signr,set,\+handler,ctx_has_vsx_region),e)+staticlongnotrace__unsafe_setup_sigcontext(structsigcontext__user*sc,+structtask_struct*tsk,intsignr,sigset_t*set,+unsignedlonghandler,intctx_has_vsx_region){/* When CONFIG_ALTIVEC is set, we _always_ setup v_regs even if the*processneverusedaltivecyet(MSR_VECiszeroinpt_regsof
@@ -118,20 +122,19 @@ static long setup_sigcontext(struct sigcontext __user *sc,#endifstructpt_regs*regs=tsk->thread.regs;unsignedlongmsr=regs->msr;-longerr=0;/* Force usr to alway see softe as 1 (interrupts enabled) */unsignedlongsofte=0x1;BUG_ON(tsk!=current);#ifdef CONFIG_ALTIVEC-err|=__put_user(v_regs,&sc->v_regs);+unsafe_put_user(v_regs,&sc->v_regs,efault_out);/* save altivec registers */if(tsk->thread.used_vr){/* Copy 33 vec registers (vr0..31 and vscr) to the stack */-err|=__copy_to_user(v_regs,&tsk->thread.vr_state,-33*sizeof(vector128));+unsafe_copy_to_user(v_regs,&tsk->thread.vr_state,+33*sizeof(vector128),efault_out);/* set MSR_VEC in the MSR value in the frame to indicate that sc->v_reg)*containsvaliddata.*/
@@ -140,12 +143,12 @@ static long setup_sigcontext(struct sigcontext __user *sc,/* We always copy to/from vrsave, it's 0 if we don't have or don't*usealtivec.*/-err|=__put_user(tsk->thread.vrsave,(u32__user*)&v_regs[33]);+unsafe_put_user(tsk->thread.vrsave,(u32__user*)&v_regs[33],efault_out);#else /* CONFIG_ALTIVEC */-err|=__put_user(0,&sc->v_regs);+unsafe_put_user(0,&sc->v_regs,efault_out);#endif /* CONFIG_ALTIVEC *//* copy fpr regs and fpscr */-err|=copy_fpr_to_user(&sc->fp_regs,tsk);+unsafe_copy_fpr_to_user(&sc->fp_regs,tsk,efault_out);/**CleartheMSRVSXbittoindicatethereisnovalidstateattached
@@ -160,24 +163,27 @@ static long setup_sigcontext(struct sigcontext __user *sc,*/if(tsk->thread.used_vsr&&ctx_has_vsx_region){v_regs+=ELF_NVRREG;-err|=copy_vsx_to_user(v_regs,tsk);+unsafe_copy_vsx_to_user(v_regs,tsk,efault_out);/* set MSR_VSX in the MSR value in the frame to*indicatethatsc->vs_reg)containsvaliddata.*/msr|=MSR_VSX;}#endif /* CONFIG_VSX */-err|=__put_user(&sc->gp_regs,&sc->regs);+unsafe_put_user(&sc->gp_regs,&sc->regs,efault_out);WARN_ON(!FULL_REGS(regs));-err|=__copy_to_user(&sc->gp_regs,regs,GP_REGS_SIZE);-err|=__put_user(msr,&sc->gp_regs[PT_MSR]);-err|=__put_user(softe,&sc->gp_regs[PT_SOFTE]);-err|=__put_user(signr,&sc->signal);-err|=__put_user(handler,&sc->handler);+unsafe_copy_to_user(&sc->gp_regs,regs,GP_REGS_SIZE,efault_out);+unsafe_put_user(msr,&sc->gp_regs[PT_MSR],efault_out);+unsafe_put_user(softe,&sc->gp_regs[PT_SOFTE],efault_out);+unsafe_put_user(signr,&sc->signal,efault_out);+unsafe_put_user(handler,&sc->handler,efault_out);if(set!=NULL)-err|=__put_user(set->sig[0],&sc->oldmask);+unsafe_put_user(set->sig[0],&sc->oldmask,efault_out);-returnerr;+return0;++efault_out:+return-EFAULT;}#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:22:37
From: Daniel Axtens <redacted>
Add uaccess blocks and use the 'unsafe' versions of functions doing user
access where possible to reduce the number of times uaccess has to be
opened/closed.
There is no 'unsafe' version of copy_siginfo_to_user, so move it
slightly to allow for a "longer" uaccess block.
Signed-off-by: Daniel Axtens <redacted>
Co-developed-by: Christopher M. Riedl <redacted>
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 54 +++++++++++++++++++++------------
1 file changed, 34 insertions(+), 20 deletions(-)
@@ -849,44 +849,51 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,unsignedlongmsr=regs->msr;frame=get_sigframe(ksig,tsk,sizeof(*frame),0);-if(!access_ok(frame,sizeof(*frame)))-gotobadframe;-err|=__put_user(&frame->info,&frame->pinfo);-err|=__put_user(&frame->uc,&frame->puc);-err|=copy_siginfo_to_user(&frame->info,&ksig->info);-if(err)+/* This only applies when calling unsafe_setup_sigcontext() and must be+*calledbeforeopeningtheuaccesswindow.+*/+if(!MSR_TM_ACTIVE(msr))+prepare_setup_sigcontext(tsk,1);++if(!user_write_access_begin(frame,sizeof(*frame)))gotobadframe;+unsafe_put_user(&frame->info,&frame->pinfo,badframe_block);+unsafe_put_user(&frame->uc,&frame->puc,badframe_block);+/* Create the ucontext. */-err|=__put_user(0,&frame->uc.uc_flags);-err|=__save_altstack(&frame->uc.uc_stack,regs->gpr[1]);+unsafe_put_user(0,&frame->uc.uc_flags,badframe_block);+unsafe_save_altstack(&frame->uc.uc_stack,regs->gpr[1],badframe_block);if(MSR_TM_ACTIVE(msr)){#ifdef CONFIG_PPC_TRANSACTIONAL_MEM/* The ucontext_t passed to userland points to the second*ucontext_t(fortransactionalstate)withitsuc_linkptr.*/-err|=__put_user(&frame->uc_transact,&frame->uc.uc_link);+unsafe_put_user(&frame->uc_transact,&frame->uc.uc_link,badframe_block);++user_write_access_end();+err|=setup_tm_sigcontexts(&frame->uc.uc_mcontext,&frame->uc_transact.uc_mcontext,tsk,ksig->sig,NULL,(unsignedlong)ksig->ka.sa.sa_handler,msr);++if(!user_write_access_begin(frame,sizeof(structrt_sigframe)))+gotobadframe;+#endif}else{-err|=__put_user(0,&frame->uc.uc_link);-prepare_setup_sigcontext(tsk,1);-if(!user_write_access_begin(frame,sizeof(structrt_sigframe)))-return-EFAULT;-err|=__unsafe_setup_sigcontext(&frame->uc.uc_mcontext,tsk,-ksig->sig,NULL,-(unsignedlong)ksig->ka.sa.sa_handler,1);-user_write_access_end();+unsafe_put_user(0,&frame->uc.uc_link,badframe_block);+unsafe_setup_sigcontext(&frame->uc.uc_mcontext,tsk,ksig->sig,+NULL,(unsignedlong)ksig->ka.sa.sa_handler,+1,badframe_block);}-err|=__copy_to_user(&frame->uc.uc_sigmask,set,sizeof(*set));-if(err)-gotobadframe;++unsafe_copy_to_user(&frame->uc.uc_sigmask,set,sizeof(*set),badframe_block);+user_write_access_end();/* Make sure signal handler doesn't get spurious FP exceptions */tsk->thread.fp_state.fpscr=0;
@@ -901,6 +908,11 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,regs->nip=(unsignedlong)&frame->tramp[0];}++/* Save the siginfo outside of the unsafe block. */+if(copy_siginfo_to_user(&frame->info,&ksig->info))+gotobadframe;+/* Allocate a dummy caller frame for the signal handler. */newsp=((unsignedlong)frame)-__SIGNAL_FRAMESIZE;err|=put_user(regs->gpr[1],(unsignedlong__user*)newsp);
@@ -940,6 +952,8 @@ int handle_rt_signal64(struct ksignal *ksig, sigset_t *set,return0;+badframe_block:+user_write_access_end();badframe:signal_fault(current,regs,"handle_rt_signal64",frame);
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:24:13
There are non-inline functions which get called in setup_sigcontext() to
save register state to the thread struct. Move these functions into a
separate prepare_setup_sigcontext() function so that
setup_sigcontext() can be refactored later into an "unsafe" version
which assumes an open uaccess window. Non-inline functions should be
avoided when uaccess is open.
The majority of setup_sigcontext() can be refactored to execute in an
"unsafe" context (uaccess window is opened) except for some non-inline
functions. Move these out into a separate prepare_setup_sigcontext()
function which must be called first and before opening up a uaccess
window. A follow-up commit converts setup_sigcontext() to be "unsafe".
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 32 +++++++++++++++++++++-----------
1 file changed, 21 insertions(+), 11 deletions(-)
@@ -112,7 +129,6 @@ static long setup_sigcontext(struct sigcontext __user *sc,/* save altivec registers */if(tsk->thread.used_vr){-flush_altivec_to_thread(tsk);/* Copy 33 vec registers (vr0..31 and vscr) to the stack */err|=__copy_to_user(v_regs,&tsk->thread.vr_state,33*sizeof(vector128));
@@ -124,17 +140,10 @@ static long setup_sigcontext(struct sigcontext __user *sc,/* We always copy to/from vrsave, it's 0 if we don't have or don't*usealtivec.*/-vrsave=0;-if(cpu_has_feature(CPU_FTR_ALTIVEC)){-vrsave=mfspr(SPRN_VRSAVE);-tsk->thread.vrsave=vrsave;-}--err|=__put_user(vrsave,(u32__user*)&v_regs[33]);+err|=__put_user(tsk->thread.vrsave,(u32__user*)&v_regs[33]);#else /* CONFIG_ALTIVEC */err|=__put_user(0,&sc->v_regs);#endif /* CONFIG_ALTIVEC */-flush_fp_to_thread(tsk);/* copy fpr regs and fpscr */err|=copy_fpr_to_user(&sc->fp_regs,tsk);
@@ -150,7 +159,6 @@ static long setup_sigcontext(struct sigcontext __user *sc,*VMXdata.*/if(tsk->thread.used_vsr&&ctx_has_vsx_region){-flush_vsx_to_thread(tsk);v_regs+=ELF_NVRREG;err|=copy_vsx_to_user(v_regs,tsk);/* set MSR_VSX in the MSR value in the frame to
From: Christopher M. Riedl <hidden> Date: 2021-01-28 04:25:55
Previously restore_sigcontext() performed a costly KUAP switch on every
uaccess operation. These repeated uaccess switches cause a significant
drop in signal handling performance.
Rewrite restore_sigcontext() to assume that a userspace read access
window is open. Replace all uaccess functions with their 'unsafe'
versions which avoid the repeated uaccess switches.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal_64.c | 68 ++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 27 deletions(-)
@@ -348,27 +348,28 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,save_r13=regs->gpr[13];/* copy the GPRs */-err|=__copy_from_user(regs->gpr,sc->gp_regs,sizeof(regs->gpr));-err|=__get_user(regs->nip,&sc->gp_regs[PT_NIP]);+unsafe_copy_from_user(regs->gpr,sc->gp_regs,sizeof(regs->gpr),+efault_out);+unsafe_get_user(regs->nip,&sc->gp_regs[PT_NIP],efault_out);/* get MSR separately, transfer the LE bit if doing signal return */-err|=__get_user(msr,&sc->gp_regs[PT_MSR]);+unsafe_get_user(msr,&sc->gp_regs[PT_MSR],efault_out);if(sig)regs->msr=(regs->msr&~MSR_LE)|(msr&MSR_LE);-err|=__get_user(regs->orig_gpr3,&sc->gp_regs[PT_ORIG_R3]);-err|=__get_user(regs->ctr,&sc->gp_regs[PT_CTR]);-err|=__get_user(regs->link,&sc->gp_regs[PT_LNK]);-err|=__get_user(regs->xer,&sc->gp_regs[PT_XER]);-err|=__get_user(regs->ccr,&sc->gp_regs[PT_CCR]);+unsafe_get_user(regs->orig_gpr3,&sc->gp_regs[PT_ORIG_R3],efault_out);+unsafe_get_user(regs->ctr,&sc->gp_regs[PT_CTR],efault_out);+unsafe_get_user(regs->link,&sc->gp_regs[PT_LNK],efault_out);+unsafe_get_user(regs->xer,&sc->gp_regs[PT_XER],efault_out);+unsafe_get_user(regs->ccr,&sc->gp_regs[PT_CCR],efault_out);/* Don't allow userspace to set SOFTE */set_trap_norestart(regs);-err|=__get_user(regs->dar,&sc->gp_regs[PT_DAR]);-err|=__get_user(regs->dsisr,&sc->gp_regs[PT_DSISR]);-err|=__get_user(regs->result,&sc->gp_regs[PT_RESULT]);+unsafe_get_user(regs->dar,&sc->gp_regs[PT_DAR],efault_out);+unsafe_get_user(regs->dsisr,&sc->gp_regs[PT_DSISR],efault_out);+unsafe_get_user(regs->result,&sc->gp_regs[PT_RESULT],efault_out);if(!sig)regs->gpr[13]=save_r13;if(set!=NULL)-err|=__get_user(set->sig[0],&sc->oldmask);+unsafe_get_user(set->sig[0],&sc->oldmask,efault_out);/**ForcereloadofFP/VEC.
@@ -378,29 +379,28 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,regs->msr&=~(MSR_FP|MSR_FE0|MSR_FE1|MSR_VEC|MSR_VSX);#ifdef CONFIG_ALTIVEC-err|=__get_user(v_regs,&sc->v_regs);-if(err)-returnerr;+unsafe_get_user(v_regs,&sc->v_regs,efault_out);if(v_regs&&!access_ok(v_regs,34*sizeof(vector128)))return-EFAULT;/* Copy 33 vec registers (vr0..31 and vscr) from the stack */if(v_regs!=NULL&&(msr&MSR_VEC)!=0){-err|=__copy_from_user(&tsk->thread.vr_state,v_regs,-33*sizeof(vector128));+unsafe_copy_from_user(&tsk->thread.vr_state,v_regs,+33*sizeof(vector128),efault_out);tsk->thread.used_vr=true;}elseif(tsk->thread.used_vr){memset(&tsk->thread.vr_state,0,33*sizeof(vector128));}/* Always get VRSAVE back */if(v_regs!=NULL)-err|=__get_user(tsk->thread.vrsave,(u32__user*)&v_regs[33]);+unsafe_get_user(tsk->thread.vrsave,(u32__user*)&v_regs[33],+efault_out);elsetsk->thread.vrsave=0;if(cpu_has_feature(CPU_FTR_ALTIVEC))mtspr(SPRN_VRSAVE,tsk->thread.vrsave);#endif /* CONFIG_ALTIVEC *//* restore floating point */-err|=copy_fpr_from_user(tsk,&sc->fp_regs);+unsafe_copy_fpr_from_user(tsk,&sc->fp_regs,efault_out);#ifdef CONFIG_VSX/**GetadditionalVSXdata.Updatev_regstopointafterthe
@@ -409,14 +409,17 @@ static long restore_sigcontext(struct task_struct *tsk, sigset_t *set, int sig,*/v_regs+=ELF_NVRREG;if((msr&MSR_VSX)!=0){-err|=copy_vsx_from_user(tsk,v_regs);+unsafe_copy_vsx_from_user(tsk,v_regs,efault_out);tsk->thread.used_vsr=true;}else{for(i=0;i<32;i++)tsk->thread.fp_state.fpr[i][TS_VSRLOWOFFSET]=0;}#endif-returnerr;+return0;++efault_out:+return-EFAULT;}#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
@@ -701,8 +704,14 @@ SYSCALL_DEFINE3(swapcontext, struct ucontext __user *, old_ctx,if(__copy_from_user(&set,&new_ctx->uc_sigmask,sizeof(set)))do_exit(SIGSEGV);set_current_blocked(&set);-if(restore_sigcontext(current,NULL,0,&new_ctx->uc_mcontext))++if(!user_read_access_begin(new_ctx,ctx_size))+return-EFAULT;+if(__unsafe_restore_sigcontext(current,NULL,0,&new_ctx->uc_mcontext)){+user_read_access_end();do_exit(SIGSEGV);+}+user_read_access_end();/* This returns like rt_sigreturn */set_thread_flag(TIF_RESTOREALL);
From: David Laight <hidden> Date: 2021-01-28 10:42:02
From: Christopher M. Riedl
quoted hunk
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
From: Christopher M. Riedl <hidden> Date: 2021-02-01 15:56:26
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
From: David Laight <hidden> Date: 2021-02-01 16:18:28
From: Christopher M. Riedl
Sent: 01 February 2021 15:56
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
quoted
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
Zero :-)
quoted
quoted
+ int i; \
+ \
+ unsafe_copy_from_user(buf, __f, ELF_NFPREG * sizeof(double), \
+ label); \
+ for (i = 0; i < ELF_NFPREG - 1; i++) \
+ __t->thread.TS_FPR(i) = buf[i]; \
+ __t->thread.fp_state.fpscr = buf[i]; \
+} while (0)
On further reflection, since you immediately loop through the buffer
why not just use user_access_begin() and unsafe_get_user() in the loop.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
From: Christopher M. Riedl <hidden> Date: 2021-02-01 16:58:58
On Mon Feb 1, 2021 at 10:15 AM CST, David Laight wrote:
From: Christopher M. Riedl
quoted
Sent: 01 February 2021 15:56
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
quoted
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
Zero :-)
Hehe good point!
quoted
quoted
quoted
+ int i; \
+ \
+ unsafe_copy_from_user(buf, __f, ELF_NFPREG * sizeof(double), \
+ label); \
+ for (i = 0; i < ELF_NFPREG - 1; i++) \
+ __t->thread.TS_FPR(i) = buf[i]; \
+ __t->thread.fp_state.fpscr = buf[i]; \
+} while (0)
On further reflection, since you immediately loop through the buffer
why not just use user_access_begin() and unsafe_get_user() in the loop.
Christophe had suggested this a few revisions ago as well. When I tried
this approach, the signal handling performance took a pretty big hit:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2020-October/219351.html
I included some numbers on v3 as well but decided to drop the approach
altogether for this one since it just didn't seem worth the hit.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes,
MK1 1PT, UK
Registration No: 1397386 (Wales)
From: Gabriel Paubert <hidden> Date: 2021-02-01 17:08:02
On Mon, Feb 01, 2021 at 09:55:44AM -0600, Christopher M. Riedl wrote:
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
quoted
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
See include/linux/poll.h, where the limit is of the order of 800 bytes
and the number of entries in an on stack array is chosen at compile time
(different between 32 and 64 bit for example).
The values are used in do_sys_poll, which, with almost 1000 bytes of
stack footprint, appears close to the top of "make checkstack". In
addition do_sys_poll has to call the ->poll function of every file
descriptor in its table, so it is not a tail function.
This 264 bytes array looks reasonable, but please use 'make checkstack'
to verify that the function's total stack usage stays within reason.
Gabriel
From: David Laight <hidden> Date: 2021-02-01 17:39:52
From: Christopher M. Riedl
Sent: 01 February 2021 16:55
...
quoted
quoted
quoted
quoted
+ int i; \
+ \
+ unsafe_copy_from_user(buf, __f, ELF_NFPREG * sizeof(double), \
+ label); \
+ for (i = 0; i < ELF_NFPREG - 1; i++) \
+ __t->thread.TS_FPR(i) = buf[i]; \
+ __t->thread.fp_state.fpscr = buf[i]; \
+} while (0)
On further reflection, since you immediately loop through the buffer
why not just use user_access_begin() and unsafe_get_user() in the loop.
Christophe had suggested this a few revisions ago as well. When I tried
this approach, the signal handling performance took a pretty big hit:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2020-October/219351.html
I included some numbers on v3 as well but decided to drop the approach
altogether for this one since it just didn't seem worth the hit.
Was that using unsafe_get_user (which relies on user_access_begin()
having 'opened up' user accesses) or just get_user() that does
it for every access?
The former should be ok, the latter will be horrid.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
From: Christopher M. Riedl <hidden> Date: 2021-02-01 17:45:58
On Mon Feb 1, 2021 at 11:37 AM CST, David Laight wrote:
From: Christopher M. Riedl
quoted
Sent: 01 February 2021 16:55
...
quoted
quoted
quoted
quoted
quoted
+ int i; \
+ \
+ unsafe_copy_from_user(buf, __f, ELF_NFPREG * sizeof(double), \
+ label); \
+ for (i = 0; i < ELF_NFPREG - 1; i++) \
+ __t->thread.TS_FPR(i) = buf[i]; \
+ __t->thread.fp_state.fpscr = buf[i]; \
+} while (0)
On further reflection, since you immediately loop through the buffer
why not just use user_access_begin() and unsafe_get_user() in the loop.
Christophe had suggested this a few revisions ago as well. When I tried
this approach, the signal handling performance took a pretty big hit:
https://lists.ozlabs.org/pipermail/linuxppc-dev/2020-October/219351.html
I included some numbers on v3 as well but decided to drop the approach
altogether for this one since it just didn't seem worth the hit.
Was that using unsafe_get_user (which relies on user_access_begin()
having 'opened up' user accesses) or just get_user() that does
it for every access?
The former should be ok, the latter will be horrid.
It was using unsafe_get_user() whereas unsafe_copy_from_user() will just
call the optimized __copy_tofrom_user() a single time - assuming that
user access is open.
David
-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes,
MK1 1PT, UK
Registration No: 1397386 (Wales)
From: Christopher M. Riedl <hidden> Date: 2021-02-01 20:59:27
On Mon Feb 1, 2021 at 10:54 AM CST, Gabriel Paubert wrote:
On Mon, Feb 01, 2021 at 09:55:44AM -0600, Christopher M. Riedl wrote:
quoted
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
quoted
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
See include/linux/poll.h, where the limit is of the order of 800 bytes
and the number of entries in an on stack array is chosen at compile time
(different between 32 and 64 bit for example).
The values are used in do_sys_poll, which, with almost 1000 bytes of
stack footprint, appears close to the top of "make checkstack". In
addition do_sys_poll has to call the ->poll function of every file
descriptor in its table, so it is not a tail function.
This 264 bytes array looks reasonable, but please use 'make checkstack'
to verify that the function's total stack usage stays within reason.
Neat, looks like total usage is a bit larger but still reasonable and
less than half of 800B:
0xc000000000017e900 __unsafe_restore_sigcontext.constprop.0 [vmlinux]:352
Thanks for the tip!
From: Michael Ellerman <mpe@ellerman.id.au> Date: 2021-02-04 06:11:24
"Christopher M. Riedl" [off-list ref] writes:
On Mon Feb 1, 2021 at 10:54 AM CST, Gabriel Paubert wrote:
quoted
On Mon, Feb 01, 2021 at 09:55:44AM -0600, Christopher M. Riedl wrote:
quoted
On Thu Jan 28, 2021 at 4:38 AM CST, David Laight wrote:
quoted
From: Christopher M. Riedl
quoted
Sent: 28 January 2021 04:04
Reuse the "safe" implementation from signal.c except for calling
unsafe_copy_from_user() to copy into a local buffer.
Signed-off-by: Christopher M. Riedl <redacted>
---
arch/powerpc/kernel/signal.h | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
@@ -53,6 +53,33 @@ unsigned long copy_ckfpr_from_user(struct task_struct *task, void __user *from);&buf[i],label);\}while(0)+#define unsafe_copy_fpr_from_user(task, from, label) do { \+structtask_struct*__t=task;\+u64__user*__f=(u64__user*)from;\+u64buf[ELF_NFPREG];\
How big is that buffer?
Isn't is likely to be reasonably large compared to a reasonable
kernel stack frame.
Especially since this isn't even a leaf function.
I think Christophe answered this - I don't really have an opinion either
way. What would be a 'reasonable' kernel stack frame for reference?
See include/linux/poll.h, where the limit is of the order of 800 bytes
and the number of entries in an on stack array is chosen at compile time
(different between 32 and 64 bit for example).
The values are used in do_sys_poll, which, with almost 1000 bytes of
stack footprint, appears close to the top of "make checkstack". In
addition do_sys_poll has to call the ->poll function of every file
descriptor in its table, so it is not a tail function.
This 264 bytes array looks reasonable, but please use 'make checkstack'
to verify that the function's total stack usage stays within reason.
Neat, looks like total usage is a bit larger but still reasonable and
less than half of 800B:
0xc000000000017e900 __unsafe_restore_sigcontext.constprop.0 [vmlinux]:352
We warn for frames larger than 2KB on 64-bit, see FRAME_WARN in
lib/Kconfig.debug.
So 264 bytes is entirely reasonable IMHO.
cheers