This is very much a proof of concept and if it isn't clear from the
commit names, still a work in progress.
I believe I have something that works - all the powerpc selftests
pass. I would like to get some eyes on it to a) see if I've missed
anything big and b) some opinions on if it is looking like a net
improvement.
Obviously it is still a bit rough around the edges, I'll have to
convince myself that the SPR code is correct. I don't think the
TM_KERNEL_ENTRY macro needs to check that we came from userspace, if
TM is on then we can probably assume. Maybe a check not in the
fastpath. Some of the BUG_ON()s will probably go.
Background:
Currently TM is dealt with when we need to. That is, when we switch
processes, we'll (if nessesary) reclaim the outgoing process and (if
nessesary) recheckpoint the incoming process. Same with signals, if we
need to deliver a signal, we'll ensure we've reclaimed in order to
have all the information and go from there.
I, along with some others got curious to see what it would look like if
we did the 'opposite'.
At all kernel entry points that won't simply just zoom straight to an
RFID we now check if the thread was transactional and do the reclaim.
Correspondingly do the recheckpoint quite late on exception exit. It
turns out we already had a lot of the code pathes set up on the exit
path as there were things that TM had special cased on exit already.
I wasn't sure it it would lead to more or less complexity and though
I'd have to try it to see. I feel like it was almost a win but SPRs
did add some annoying caveats.
In order to get this past Michael I'm going to prove it performs, or
rather, doesn't slow anything down - workload suggestions welcome.
Thanks,
Cyril Bur (12):
powerpc/tm: Remove struct thread_info param from tm_reclaim_thread()
selftests/powerpc: Fix tm.h helpers
selftests/powerpc: Add tm-signal-drop-transaction TM test
selftests/powerpc: Use less common thread names
[WIP] powerpc/tm: Reclaim/recheckpoint on entry/exit
[WIP] powerpc/tm: Remove dead code from __switch_to_tm()
[WIP] powerpc/tm: Add TM_KERNEL_ENTRY in more delicate exception
pathes
[WIP] powerpc/tm: Fix *unavailable_tm exceptions
[WIP] powerpc/tm: Tweak signal code to handle new reclaim/recheckpoint
times
[WIP] powerpc/tm: Correctly save/restore checkpointed sprs
[WIP] powerpc/tm: Afterthoughts
[WIP] selftests/powerpc: Remove incorrect tm-syscall selftest
arch/powerpc/include/asm/exception-64s.h | 25 ++++
arch/powerpc/kernel/entry_64.S | 20 ++-
arch/powerpc/kernel/exceptions-64s.S | 31 ++++-
arch/powerpc/kernel/process.c | 145 ++++++++++++++++++---
arch/powerpc/kernel/ptrace.c | 9 +-
arch/powerpc/kernel/signal.c | 11 +-
arch/powerpc/kernel/signal_32.c | 16 +--
arch/powerpc/kernel/signal_64.c | 41 ++++--
arch/powerpc/kernel/traps.c | 3 -
tools/testing/selftests/powerpc/tm/Makefile | 5 +-
.../powerpc/tm/tm-signal-drop-transaction.c | 74 +++++++++++
.../testing/selftests/powerpc/tm/tm-syscall-asm.S | 28 ----
tools/testing/selftests/powerpc/tm/tm-syscall.c | 106 ---------------
.../testing/selftests/powerpc/tm/tm-unavailable.c | 4 +-
tools/testing/selftests/powerpc/tm/tm.h | 10 +-
15 files changed, 319 insertions(+), 209 deletions(-)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-signal-drop-transaction.c
delete mode 100644 tools/testing/selftests/powerpc/tm/tm-syscall-asm.S
delete mode 100644 tools/testing/selftests/powerpc/tm/tm-syscall.c
--
2.16.2
@@ -35,6 +35,7 @@*implementationsaspossible.*/#include<asm/head-64.h>+#include<asm/tm.h>/* PACA save area offsets (exgen, exmc, etc) */#define EX_R9 0
@@ -127,6 +128,26 @@hrfid;\bhrfi_flush_fallback+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM+#define TM_KERNEL_ENTRY \+ldr3,_MSR(r1);\+/* Probably don't need to check if coming from user/kernel */\+/* If TM is suspended or active then we must have come from*/\+/* userspace */\+andi.r0,r3,MSR_PR;\+beq1f;\+rldicl.r3,r3,(64-MSR_TS_LG),(64-2);/* SUSPENDED or ACTIVE*/\+beql+1f;/* Not SUSPENDED or ACTIVE */\+blsave_nvgprs;\+RECONCILE_IRQ_STATE(r10,r11);\+lir3,TM_CAUSE_MISC;\+bltm_reclaim_current;/* uint8 cause */\+1:++#else /* CONFIG_PPC_TRANSACTIONAL_MEM */+#define TM_KERNEL_ENTRY+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */+#ifdef CONFIG_RELOCATABLE#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \mfsprr11,SPRN_##h##SRR0;/* save SRR0 */\
@@ -675,6 +696,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL)EXCEPTION_PROLOG_COMMON(trap,area);\/* Volatile regs are potentially clobbered here */\additions;\+/* This is going to need to go somewhere else as well */\+/* See comment in tm_recheckpoint() */\+TM_KERNEL_ENTRY;\addir3,r1,STACK_FRAME_OVERHEAD;\blhdlr;\bret
@@ -689,6 +713,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL)EXCEPTION_PROLOG_COMMON_3(trap);\/* Volatile regs are potentially clobbered here */\additions;\+TM_KERNEL_ENTRY;\addir3,r1,STACK_FRAME_OVERHEAD;\blhdlr
@@ -951,6 +951,23 @@ void tm_recheckpoint(struct thread_struct *thread)if(!(thread->regs->msr&MSR_TM))return;+/*+*Thisis'that'comment.+*+*Ifwegetwherewithtmsuspendedoractivethensomething+*hasgonewrong.I'veaddedthisnowasaproofofconcept.+*+*TheproblemI'mseeingwithoutitisanattemptto+*recheckpointaCPUwithoutapreviousreclaim.+*+*I'mprobablymissedanexceptionentrywiththe+*TM_KERNEL_ENTRYmacro.Shouldbeeasyenoughtofind.+*/+if(MSR_TM_ACTIVE(mfmsr()))+return;++tm_enable();+/* We really can't be interrupted here as the TEXASR registers can't*changeandlaterinthetrecheckpointcode,wehaveauserspaceR1.*Solet'sharddisableoverthisregion.
This test uses a signal to 'discard' a transaction. That is, it will
take a signal of a thread in a suspended transaction and just remove
the suspended MSR bit. Because this will send the userspace thread back
to the tebgin + 4 address, we should also set CR0 to be nice.
Signed-off-by: Cyril Bur <redacted>
---
tools/testing/selftests/powerpc/tm/Makefile | 1 +
.../powerpc/tm/tm-signal-drop-transaction.c | 74 ++++++++++++++++++++++
2 files changed, 75 insertions(+)
create mode 100644 tools/testing/selftests/powerpc/tm/tm-signal-drop-transaction.c
@@ -0,0 +1,74 @@+/*+*Copyright2018,CyrilBur,IBMCorp.+*LicensedunderGPLv2.+*+*Thistestusesasignalhandlertomakeathreadgofrom+*transactionalstatetonothingstate.Inpracticeuserspace,why+*woulduserspaceeverdothis?Intheory,theycan.+*/++#include<errno.h>+#include<inttypes.h>+#include<pthread.h>+#include<signal.h>+#include<stdio.h>+#include<stdlib.h>+#include<unistd.h>++#include"utils.h"+#include"tm.h"++staticboolpassed;++staticvoidsignal_usr1(intsignum,siginfo_t*info,void*uc)+{+ucontext_t*ucp=uc;+structpt_regs*regs=ucp->uc_mcontext.regs;++passed=true;++/* I really hope I got that right, we wan't to clear both the MSR_TS bits */+regs->msr&=~(3ULL<<33);+/* Set CR0 to 0b0010 */+regs->ccr&=~(0xDULL<<28);+}++inttest_drop(void)+{+structsigactionact;++SKIP_IF(!have_htm());++act.sa_sigaction=signal_usr1;+sigemptyset(&act.sa_mask);+act.sa_flags=SA_SIGINFO;+if(sigaction(SIGUSR1,&act,NULL)<0){+perror("sigaction sigusr1");+exit(1);+}+++asm__volatile__(+"tbegin.;"+"beq 1f; "+"tsuspend.;"+"1: ;"+:::"memory","cr0");++if(!passed&&!tcheck_transactional()){+fprintf(stderr,"Not in suspended state: 0x%1x\n",tcheck());+exit(1);+}++kill(getpid(),SIGUSR1);++/* If we reach here, we've passed. Otherwise we've probably crashed+*thekernel*/++return0;+}++intmain(intargc,char*argv[])+{+returntest_harness(test_drop,"tm_signal_drop_transaction");+}
@@ -860,21 +860,9 @@ static long restore_tm_user_regs(struct pt_regs *regs,tm_enable();/* Make sure the transaction is marked as failed */current->thread.tm_texasr|=TEXASR_FS;-/* This loads the checkpointed FP/VEC state, if used */-tm_recheckpoint(¤t->thread);-/* This loads the speculative FP/VEC state, if used */-msr_check_and_set(msr&(MSR_FP|MSR_VEC));-if(msr&MSR_FP){-load_fp_state(¤t->thread.fp_state);-regs->msr|=(MSR_FP|current->thread.fpexc_mode);-}-#ifdef CONFIG_ALTIVEC-if(msr&MSR_VEC){-load_vr_state(¤t->thread.vr_state);-regs->msr|=MSR_VEC;-}-#endif+/* See comment in signal_64.c */+set_thread_flag(TIF_RESTORE_TM);return0;}
@@ -568,21 +568,20 @@ static long restore_tm_sigcontexts(struct task_struct *tsk,}}#endif-tm_enable();/* Make sure the transaction is marked as failed */tsk->thread.tm_texasr|=TEXASR_FS;-/* This loads the checkpointed FP/VEC state, if used */-tm_recheckpoint(&tsk->thread);-msr_check_and_set(msr&(MSR_FP|MSR_VEC));-if(msr&MSR_FP){-load_fp_state(&tsk->thread.fp_state);-regs->msr|=(MSR_FP|tsk->thread.fpexc_mode);-}-if(msr&MSR_VEC){-load_vr_state(&tsk->thread.vr_state);-regs->msr|=MSR_VEC;-}+/*+*Ibelievethisisonlynessesaryifthe+*clear_thread_flag(TIF_RESTORE_TM);inrestore_tm_state()+*staysbeforetheif(!MSR_TM_ACTIVE(regs->msr).+*+*Actuallyno,weshouldfollowthecommentin+*restore_tm_state()butthisshouldALSObehereif+*ifthesignalhandlerdoessomethingcrazylike'generate'+*atransaction.+*/+set_thread_flag(TIF_RESTORE_TM);returnerr;}
@@ -734,6 +733,22 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5,if(MSR_TM_SUSPENDED(mfmsr()))tm_reclaim_current(0);+/*+*Thereisauniversewherethesignalhandlerdidsomething+*crazylikedropthetransactionentirely.Thatis,themain+*threadwasintransactionalorsuspendedmodeandthe+*signalhandlerhasputtheminnontransactionalmode.+*Inthatcasewe'llneedtocleartheTIF_RESTORE_TMflag.+*I'llneedtoponderitexactlybutfornowthatsallI+*thinkthatneedstobedone.Atthemomentitallworks+*becausenosignalhanlderisnutsenoughtodoit.+*+*Add...somewhere...Iguessintheelseblock,inthe+*afterthe#endif+*+*clear_thread_flag(TIF_RESTORE_TM);+*/+if(__get_user(msr,&uc->uc_mcontext.gp_regs[PT_MSR]))gotobadframe;if(MSR_TM_ACTIVE(msr)){
@@ -748,6 +763,8 @@ int sys_rt_sigreturn(unsigned long r3, unsigned long r4, unsigned long r5,else/* Fall through, for non-TM restore */#endif+clear_thread_flag(TIF_RESTORE_TM);+if(restore_sigcontext(current,NULL,1,&uc->uc_mcontext))gotobadframe;
@@ -504,6 +504,11 @@ EXC_COMMON_BEGIN(data_access_common)lir5,0x300stdr3,_DAR(r1)stdr4,_DSISR(r1)+/*+*Can't do TM_KERNEL_ENTRY here as do_hash_page might jump to+*verylateintheexpectionexitcode,wellafterany+*possiblityofdoingarecheckpoint+*/BEGIN_MMU_FTR_SECTIONbdo_hash_page/*Trytohandleashptefault*/MMU_FTR_SECTION_ELSE
@@ -548,6 +553,11 @@ EXC_COMMON_BEGIN(instruction_access_common)lir5,0x400stdr3,_DAR(r1)stdr4,_DSISR(r1)+/*+*Can't do TM_KERNEL_ENTRY here as do_hash_page might jump to+*verylateintheexpectionexitcode,wellafterany+*possiblityofdoingarecheckpoint+*/BEGIN_MMU_FTR_SECTIONbdo_hash_page/*Trytohandleashptefault*/MMU_FTR_SECTION_ELSE
@@ -1685,6 +1698,10 @@ handle_page_fault:/*Wehaveadatabreakpointexception-handleit*/handle_dabr_fault:+/*+*Don't need to do TM_KERNEL_ENTRY here as we'll+*comefromhandle_page_fault:whichhasdoneitalready+*/blsave_nvgprsldr4,_DAR(r1)ldr5,_DSISR(r1)
tm_reclaim_thread() doesn't use the parameter anymore, both callers have
to bother getting it as they have no need for a struct thread_info
either.
It was previously used but became unused in dc3106690b20 ("powerpc: tm:
Always use fp_state and vr_state to store live registers")
Just remove it and adjust the callers.
Signed-off-by: Cyril Bur <redacted>
---
arch/powerpc/kernel/process.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
"ping" and "pong" (in particular "ping") are common names. If a
selftests causes a kernel BUG_ON or any kind of backtrace the process
name is displayed. Setting a more unique name avoids confusion as to
which process caused the problem.
Signed-off-by: Cyril Bur <redacted>
---
tools/testing/selftests/powerpc/tm/tm-unavailable.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
@@ -359,7 +359,7 @@ int main(int argc, char **argv)pr_err(rc,"pthread_create()");/* Name it for systemtap convenience */-rc=pthread_setname_np(t1,"pong");+rc=pthread_setname_np(t1,"tm-unavailable-pong");if(rc)pr_warn(rc,"pthread_create()");
@@ -991,6 +995,37 @@ void tm_recheckpoint(struct thread_struct *thread)__tm_recheckpoint(thread);+/*+*Thisisastrippeddownrestore_sprs(),weneedtodothis+*nowaswemightgostraightouttouserspaceandcurrently+*thecheckpointedvaluesareontheCPU.+*+*TODO:Improve+*/+#ifdef CONFIG_ALTIVEC+if(cpu_has_feature(CPU_FTR_ALTIVEC))+mtspr(SPRN_VRSAVE,thread->vrsave);+#endif+#ifdef CONFIG_PPC_BOOK3S_64+if(cpu_has_feature(CPU_FTR_DSCR)){+u64dscr=get_paca()->dscr_default;+if(thread->dscr_inherit)+dscr=thread->dscr;++mtspr(SPRN_DSCR,dscr);+}++if(cpu_has_feature(CPU_FTR_ARCH_207S)){+/* The EBB regs aren't checkpointed */+mtspr(SPRN_FSCR,thread->fscr);++mtspr(SPRN_TAR,thread->tar);+}++/* I think we don't need to */+if(cpu_has_feature(CPU_FTR_ARCH_300))+mtspr(SPRN_TIDR,thread->tidr);+#endiflocal_irq_restore(flags);}
Currently we perform transactional memory work at late as possible.
That is we run in the kernel with the userspace checkpointed state on
the CPU untill we absolultely must remove it and store it away. Likely
a process switch, but possibly also signals or ptrace.
What this means is that if userspace does a system call in suspended
mode, it is possible that we will handle the system call and return
them without the need to to a reclaim/recheckpoint and so they can
expect to resume their transaction.
This is what tm-syscall tests for - the ability to perform a system
call in suspended state and still resume it afterwards.
TM reworks have meant that we now deal with any transactional state on
entry to the kernel, no matter the reason for entry (some expections
apply). We will categorically doom any suspended transaction that makes
a system call, making that transaction unresumeable.
This test will now always fail no matter what. I would like to note
here that this new behaviour does not break userspace at all. Hardware
Transactional Memory gives zero guarantee of forward progress and any
correct userspace has already had and will always have to implement a
non HTM fallback. Relying on this specific kernel behaviour also meant
relying on the stars aligning in the hardware such that there was no
cache overlaps and that it had a large enough footprint to handle
any system call without dooming a transaction.
---
tools/testing/selftests/powerpc/tm/Makefile | 4 +-
.../testing/selftests/powerpc/tm/tm-syscall-asm.S | 28 ------
tools/testing/selftests/powerpc/tm/tm-syscall.c | 106 ---------------------
3 files changed, 1 insertion(+), 137 deletions(-)
delete mode 100644 tools/testing/selftests/powerpc/tm/tm-syscall-asm.S
delete mode 100644 tools/testing/selftests/powerpc/tm/tm-syscall.c
@@ -1,106 +0,0 @@-/*- * Copyright 2015, Sam Bobroff, IBM Corp.- * Licensed under GPLv2.- *- * Test the kernel's system call code to ensure that a system call- * made from within an active HTM transaction is aborted with the- * correct failure code.- * Conversely, ensure that a system call made from within a- * suspended transaction can succeed.- */--#include <stdio.h>-#include <unistd.h>-#include <sys/syscall.h>-#include <asm/tm.h>-#include <sys/time.h>-#include <stdlib.h>--#include "utils.h"-#include "tm.h"--extern int getppid_tm_active(void);-extern int getppid_tm_suspended(void);--unsigned retries = 0;--#define TEST_DURATION 10 /* seconds */-#define TM_RETRIES 100--pid_t getppid_tm(bool suspend)-{- int i;- pid_t pid;-- for (i = 0; i < TM_RETRIES; i++) {- if (suspend)- pid = getppid_tm_suspended();- else- pid = getppid_tm_active();-- if (pid >= 0)- return pid;-- if (failure_is_persistent()) {- if (failure_is_syscall())- return -1;-- printf("Unexpected persistent transaction failure.\n");- printf("TEXASR 0x%016lx, TFIAR 0x%016lx.\n",- __builtin_get_texasr(), __builtin_get_tfiar());- exit(-1);- }-- retries++;- }-- printf("Exceeded limit of %d temporary transaction failures.\n", TM_RETRIES);- printf("TEXASR 0x%016lx, TFIAR 0x%016lx.\n",- __builtin_get_texasr(), __builtin_get_tfiar());-- exit(-1);-}--int tm_syscall(void)-{- unsigned count = 0;- struct timeval end, now;-- SKIP_IF(!have_htm_nosc());-- setbuf(stdout, NULL);-- printf("Testing transactional syscalls for %d seconds...\n", TEST_DURATION);-- gettimeofday(&end, NULL);- now.tv_sec = TEST_DURATION;- now.tv_usec = 0;- timeradd(&end, &now, &end);-- for (count = 0; timercmp(&now, &end, <); count++) {- /*- * Test a syscall within a suspended transaction and verify- * that it succeeds.- */- FAIL_IF(getppid_tm(true) == -1); /* Should succeed. */-- /*- * Test a syscall within an active transaction and verify that- * it fails with the correct failure code.- */- FAIL_IF(getppid_tm(false) != -1); /* Should fail... */- FAIL_IF(!failure_is_persistent()); /* ...persistently... */- FAIL_IF(!failure_is_syscall()); /* ...with code syscall. */- gettimeofday(&now, 0);- }-- printf("%d active and suspended transactions behaved correctly.\n", count);- printf("(There were %d transaction retries.)\n", retries);-- return 0;-}--int main(void)-{- return test_harness(tm_syscall, "tm_syscall");-}
=20
/* PACA save area offsets (exgen, exmc, etc) */
#define EX_R9 0
@@ -127,6 +128,26 @@ hrfid; \ b hrfi_flush_fallback
=20
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+#define TM_KERNEL_ENTRY \
+ ld r3,_MSR(r1); \
+ /* Probably don't need to check if coming from user/kernel */ \
+ /* If TM is suspended or active then we must have come from*/ \
+ /* userspace */ \
+ andi. r0,r3,MSR_PR; \
+ beq 1f; \
+ rldicl. r3,r3,(64-MSR_TS_LG),(64-2); /* SUSPENDED or ACTIVE*/ \
+ beql+ 1f; /* Not SUSPENDED or ACTIVE */ \
+ bl save_nvgprs; \
+ RECONCILE_IRQ_STATE(r10,r11); \
+ li r3,TM_CAUSE_MISC; \
+ bl tm_reclaim_current; /* uint8 cause */ \
+1:
+
+#else /* CONFIG_PPC_TRANSACTIONAL_MEM */
+#define TM_KERNEL_ENTRY
+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */
+
#ifdef CONFIG_RELOCATABLE
#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \
mfspr r11,SPRN_##h##SRR0; /* save SRR0 */ \
@@ -675,6 +696,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL) EXCEPTION_PROLOG_COMMON(trap, area); \ /* Volatile regs are potentially clobbered here */ \ additions; \+ /* This is going to need to go somewhere else as well */\+ /* See comment in tm_recheckpoint() */\+ TM_KERNEL_ENTRY; \ addi r3,r1,STACK_FRAME_OVERHEAD; \ bl hdlr; \ b ret
I think I'm in the loop here but I don't actually know what this means.=20
Senior Mikey moment or Crazy Cyril comments? I'll let the peanut gallery de=
cide.
+ *
+ * If we get where with tm suspended or active then something
s/where/here/
+ * has gone wrong. I've added this now as a proof of concept.
+ *
+ * The problem I'm seeing without it is an attempt to
+ * recheckpoint a CPU without a previous reclaim.
+ *
+ * I'm probably missed an exception entry with the
+ * TM_KERNEL_ENTRY macro. Should be easy enough to find.
+ */
+ if (MSR_TM_ACTIVE(mfmsr()))
+ return;
I don't really get this. Wouldn't this test apply now?
+
+ tm_enable();
Why did we add this?
quoted hunk
+
/* We really can't be interrupted here as the TEXASR registers can't
* change and later in the trecheckpoint code, we have a userspace R1.
* So let's hard disable over this region.
static inline void __switch_to_tm(struct task_struct *prev,
struct task_struct *new)
{
+ /*
+ * So, with the rework none of this code should not be needed.
+ * I've left in the reclaim for now. This *should* save us
+ * from any mistake in the new code. Also the
+ * enabling/disabling logic of MSR_TM really should be
+ * refactored into a common way with MSR_{FP,VEC,VSX}
+ */
if (cpu_has_feature(CPU_FTR_TM)) {
if (tm_enabled(prev) || tm_enabled(new))
tm_enable();
if (tm_enabled(prev)) {
prev->thread.load_tm++;
tm_reclaim_task(prev);
- if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm =
=3D=3D 0)
- prev->thread.regs->msr &=3D ~MSR_TM;
+ /*
+ * The disabling logic may be confused don't
+ * disable for now
+ *
+ * if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm=
=3D=3D 0)
+ * prev->thread.regs->msr &=3D ~MSR_TM;
+ */
Why are you doing this when you just remove all this code in the next patch=
?
struct task_struct *new)
{
/*
- * So, with the rework none of this code should not be needed.
- * I've left in the reclaim for now. This *should* save us
- * from any mistake in the new code. Also the
- * enabling/disabling logic of MSR_TM really should be
+ * The enabling/disabling logic of MSR_TM really should be
* refactored into a common way with MSR_{FP,VEC,VSX}
*/
- if (cpu_has_feature(CPU_FTR_TM)) {
- if (tm_enabled(prev) || tm_enabled(new))
- tm_enable();
-
- if (tm_enabled(prev)) {
- prev->thread.load_tm++;
- tm_reclaim_task(prev);
- /*
- * The disabling logic may be confused don't
- * disable for now
- *
- * if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm=
=20
__tm_recheckpoint(thread);
=20
+ /*
+ * This is a stripped down restore_sprs(), we need to do this
+ * now as we might go straight out to userspace and currently
+ * the checkpointed values are on the CPU.
+ *
+ * TODO: Improve
+ */
+#ifdef CONFIG_ALTIVEC
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ mtspr(SPRN_VRSAVE, thread->vrsave);
+#endif
+#ifdef CONFIG_PPC_BOOK3S_64
+ if (cpu_has_feature(CPU_FTR_DSCR)) {
+ u64 dscr =3D get_paca()->dscr_default;
+ if (thread->dscr_inherit)
+ dscr =3D thread->dscr;
+
+ mtspr(SPRN_DSCR, dscr);
+ }
+
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ /* The EBB regs aren't checkpointed */
+ mtspr(SPRN_FSCR, thread->fscr);
+
+ mtspr(SPRN_TAR, thread->tar);
+ }
+
+ /* I think we don't need to */
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ mtspr(SPRN_TIDR, thread->tidr);
+#endif
*prev,
#endif
=20
new_thread =3D &new->thread;
+ /*
+ * Why not &prev->thread; ?
+ * What is the difference between &prev->thread and
+ * ¤t->thread ?
+ */
Why not just work it out and FIX THE CODE, rather than just rabbiting on ab=
out
it! :-P
*prev,
/*
* We need to save SPRs before treclaim/trecheckpoint as these will
* change a number of them.
+ *
+ * Because we're now reclaiming on kernel entry, we've had to
+ * already save them. Don't do it again.
+ * Note: To deliver a signal in the signal context, we'll have
+ * turned off TM because we don't want the signal context to
+ * have the transactional state of the main thread - what if
+ * we go through switch to at that point? Can we?
*/
- save_sprs(&prev->thread);
+ if (!prev->thread.regs || !MSR_TM_ACTIVE(prev->thread.regs->msr))
+ save_sprs(&prev->thread);
=20
/* Save FPU, Altivec, VSX and SPE state */
giveup_all(prev);
*prev,
* for this is we manually create a stack frame for new tasks that
* directly returns through ret_from_fork() or
* ret_from_kernel_thread(). See copy_thread() for details.
+ *
+ * It isn't stricly nessesary that we avoid the restore here
+ * because we'll simply restore again after the recheckpoint,
+ * but we can avoid it for performance reasons.
*/
- restore_sprs(old_thread, new_thread);
+ if (!new_thread->regs || !MSR_TM_ACTIVE(new_thread->regs->msr))
+ restore_sprs(old_thread, new_thread);
=20
last =3D _switch(old_thread, new_thread);
=20
@@ -1,106 +0,0 @@-/*- * Copyright 2015, Sam Bobroff, IBM Corp.- * Licensed under GPLv2.- *- * Test the kernel's system call code to ensure that a system call- * made from within an active HTM transaction is aborted with the- * correct failure code.
The above is still true
- * Conversely, ensure that a system call made from within a
- * suspended transaction can succeed.
This is true anymore....
So can we just modify the test to remove the second part?
Mikey
@@ -1,106 +0,0 @@-/*- * Copyright 2015, Sam Bobroff, IBM Corp.- * Licensed under GPLv2.- *- * Test the kernel's system call code to ensure that a system call- * made from within an active HTM transaction is aborted with the- * correct failure code.
The above is still true
quoted
- * Conversely, ensure that a system call made from within a
- * suspended transaction can succeed.
This is true anymore....
So can we just modify the test to remove the second part?
On Tue, 2018-02-20 at 13:52 +1100, Michael Neuling wrote:
Not sure I understand this.. should it be merged with the last patch?
Its all going to have to be one patch - I've left it split out to make
it more obvious which bits have had to mess with, this series
absolutely doesn't bisect.
Needs a comment here.
On Tue, 2018-02-20 at 11:22 +1100, Cyril Bur wrote:
@@ -35,6 +35,7 @@*implementationsaspossible.*/#include<asm/head-64.h>+#include<asm/tm.h>/* PACA save area offsets (exgen, exmc, etc) */#define EX_R9 0
@@ -127,6 +128,26 @@hrfid;\bhrfi_flush_fallback+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM+#define TM_KERNEL_ENTRY \+ldr3,_MSR(r1);\+/* Probably don't need to check if coming from user/kernel */\+/* If TM is suspended or active then we must have come from*/\+/* userspace */\+andi.r0,r3,MSR_PR;\+beq1f;\+rldicl.r3,r3,(64-MSR_TS_LG),(64-2);/* SUSPENDED or ACTIVE*/\+beql+1f;/* Not SUSPENDED or ACTIVE */\+blsave_nvgprs;\+RECONCILE_IRQ_STATE(r10,r11);\+lir3,TM_CAUSE_MISC;\+bltm_reclaim_current;/* uint8 cause */\+1:++#else /* CONFIG_PPC_TRANSACTIONAL_MEM */+#define TM_KERNEL_ENTRY+#endif /* CONFIG_PPC_TRANSACTIONAL_MEM */+#ifdef CONFIG_RELOCATABLE#define __EXCEPTION_RELON_PROLOG_PSERIES_1(label, h) \mfsprr11,SPRN_##h##SRR0;/* save SRR0 */\
@@ -675,6 +696,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL)EXCEPTION_PROLOG_COMMON(trap,area);\/* Volatile regs are potentially clobbered here */\additions;\+/* This is going to need to go somewhere else as well */\+/* See comment in tm_recheckpoint() */\+TM_KERNEL_ENTRY;\addir3,r1,STACK_FRAME_OVERHEAD;\blhdlr;\bret
@@ -689,6 +713,7 @@ END_FTR_SECTION_IFSET(CPU_FTR_CTRL)EXCEPTION_PROLOG_COMMON_3(trap);\/* Volatile regs are potentially clobbered here */\additions;\+TM_KERNEL_ENTRY;\addir3,r1,STACK_FRAME_OVERHEAD;\blhdlr
I think I'm in the loop here but I don't actually know what this means.
Senior Mikey moment or Crazy Cyril comments? I'll let the peanut gallery decide.
Oh quite possibly crazy Cyril comment that will have to be...
normalised. I should actually delete this and see if that's still the
case.
quoted
+ *
+ * If we get where with tm suspended or active then something
s/where/here/
quoted
+ * has gone wrong. I've added this now as a proof of concept.
+ *
+ * The problem I'm seeing without it is an attempt to
+ * recheckpoint a CPU without a previous reclaim.
+ *
+ * I'm probably missed an exception entry with the
+ * TM_KERNEL_ENTRY macro. Should be easy enough to find.
+ */
+ if (MSR_TM_ACTIVE(mfmsr()))
+ return;
I don't really get this. Wouldn't this test apply now?
quoted
+
+ tm_enable();
Why did we add this?
Ah yes that was a cleanup I noticed along the way and clearly forgot to
finish.
At the moment there's a bunch of tm_enable()s either before calling
functions like tm_recheckpoint() or tm_reclaim_current() or inside
helpers (tm_reclaim_current() for example again). I feel like callers
shouldn't have to worry, it should be up to the function actually doing
the TM work to enable it.
quoted
+
/* We really can't be interrupted here as the TEXASR registers can't
* change and later in the trecheckpoint code, we have a userspace R1.
* So let's hard disable over this region.
@@ -1009,6 +1026,13 @@ static inline void tm_recheckpoint_new_task(struct task_struct *new) static inline void __switch_to_tm(struct task_struct *prev, struct task_struct *new) {+ /*+ * So, with the rework none of this code should not be needed.+ * I've left in the reclaim for now. This *should* save us+ * from any mistake in the new code. Also the+ * enabling/disabling logic of MSR_TM really should be+ * refactored into a common way with MSR_{FP,VEC,VSX}+ */ if (cpu_has_feature(CPU_FTR_TM)) { if (tm_enabled(prev) || tm_enabled(new)) tm_enable();
@@ -1016,11 +1040,14 @@ static inline void __switch_to_tm(struct task_struct *prev, if (tm_enabled(prev)) { prev->thread.load_tm++; tm_reclaim_task(prev);- if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm == 0)- prev->thread.regs->msr &= ~MSR_TM;+ /*+ * The disabling logic may be confused don't+ * disable for now+ *+ * if (!MSR_TM_ACTIVE(prev->thread.regs->msr) && prev->thread.load_tm == 0)+ * prev->thread.regs->msr &= ~MSR_TM;+ */
Why are you doing this when you just remove all this code in the next patch?
The next 3 or so patches will need squashing into this one before
merging.
So why do we do tm_recheckpoint at all? Shouldn't most of the tm_blah code go
away in process.c after all this?
I'm not sure I follow, we need to recheckpoint because we're going back
to userspace? Or would you rather calling the tm.S code directly from
the exception return path?
Yes, I hope we'll be able to have a fairly big cleanup commit of tm_
code in process.c at the end of this series.
quoted
/* Ensure that restore_math() will restore */
if (msr_diff & MSR_FP)
current->thread.load_fp = 1;
@@ -991,6 +995,37 @@ void tm_recheckpoint(struct thread_struct *thread) __tm_recheckpoint(thread);+ /*+ * This is a stripped down restore_sprs(), we need to do this+ * now as we might go straight out to userspace and currently+ * the checkpointed values are on the CPU.+ *+ * TODO: Improve+ */+#ifdef CONFIG_ALTIVEC+ if (cpu_has_feature(CPU_FTR_ALTIVEC))+ mtspr(SPRN_VRSAVE, thread->vrsave);+#endif+#ifdef CONFIG_PPC_BOOK3S_64+ if (cpu_has_feature(CPU_FTR_DSCR)) {+ u64 dscr = get_paca()->dscr_default;+ if (thread->dscr_inherit)+ dscr = thread->dscr;++ mtspr(SPRN_DSCR, dscr);+ }++ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {+ /* The EBB regs aren't checkpointed */+ mtspr(SPRN_FSCR, thread->fscr);++ mtspr(SPRN_TAR, thread->tar);+ }++ /* I think we don't need to */+ if (cpu_has_feature(CPU_FTR_ARCH_300))+ mtspr(SPRN_TIDR, thread->tidr);+#endif
Why are you touching all the above hunk?
I copied restore_sprs. I'm tidying that up now - we can't call
restore_sprs because we don't have a prev and next thread.
*prev,
#endif
new_thread = &new->thread;
+ /*
+ * Why not &prev->thread; ?
+ * What is the difference between &prev->thread and
+ * ¤t->thread ?
+ */
Why not just work it out and FIX THE CODE, rather than just rabbiting on about
it! :-P
Agreed - I started to and then had a mini freakout that things would
end really badly if they're not the same. So I left that comment as a
reminder to investigate.
They should be the same though right?
*prev,
/*
* We need to save SPRs before treclaim/trecheckpoint as these will
* change a number of them.
+ *
+ * Because we're now reclaiming on kernel entry, we've had to
+ * already save them. Don't do it again.
+ * Note: To deliver a signal in the signal context, we'll have
+ * turned off TM because we don't want the signal context to
+ * have the transactional state of the main thread - what if
+ * we go through switch to at that point? Can we?
*/
- save_sprs(&prev->thread);
+ if (!prev->thread.regs || !MSR_TM_ACTIVE(prev->thread.regs->msr))
+ save_sprs(&prev->thread);
/* Save FPU, Altivec, VSX and SPE state */
giveup_all(prev);
*prev,
* for this is we manually create a stack frame for new tasks that
* directly returns through ret_from_fork() or
* ret_from_kernel_thread(). See copy_thread() for details.
+ *
+ * It isn't stricly nessesary that we avoid the restore here
+ * because we'll simply restore again after the recheckpoint,
+ * but we can avoid it for performance reasons.
*/
- restore_sprs(old_thread, new_thread);
+ if (!new_thread->regs || !MSR_TM_ACTIVE(new_thread->regs->msr))
+ restore_sprs(old_thread, new_thread);
last = _switch(old_thread, new_thread);
=20
So why do we do tm_recheckpoint at all? Shouldn't most of the tm_blah c=
ode go
quoted
away in process.c after all this?
=20
=20
I'm not sure I follow, we need to recheckpoint because we're going back
to userspace? Or would you rather calling the tm.S code directly from
the exception return path?
Yeah, I was thinking the point of this series was. We do tm_reclaim right =
on
entry and tm_recheckpoint right on exit. =20
The bits in between (ie. the tm_blah() calls process.c) would mostly go awa=
y.
Yes, I hope we'll be able to have a fairly big cleanup commit of tm_
code in process.c at the end of this series.
=20
__tm_recheckpoint(thread);
=20
+ /*
+ * This is a stripped down restore_sprs(), we need to do this
+ * now as we might go straight out to userspace and currently
+ * the checkpointed values are on the CPU.
+ *
+ * TODO: Improve
+ */
+#ifdef CONFIG_ALTIVEC
+ if (cpu_has_feature(CPU_FTR_ALTIVEC))
+ mtspr(SPRN_VRSAVE, thread->vrsave);
+#endif
+#ifdef CONFIG_PPC_BOOK3S_64
+ if (cpu_has_feature(CPU_FTR_DSCR)) {
+ u64 dscr =3D get_paca()->dscr_default;
+ if (thread->dscr_inherit)
+ dscr =3D thread->dscr;
+
+ mtspr(SPRN_DSCR, dscr);
+ }
+
+ if (cpu_has_feature(CPU_FTR_ARCH_207S)) {
+ /* The EBB regs aren't checkpointed */
+ mtspr(SPRN_FSCR, thread->fscr);
+
+ mtspr(SPRN_TAR, thread->tar);
+ }
+
+ /* I think we don't need to */
+ if (cpu_has_feature(CPU_FTR_ARCH_300))
+ mtspr(SPRN_TIDR, thread->tidr);
+#endif
=20
Why are you touching all the above hunk?
=20
I copied restore_sprs. I'm tidying that up now - we can't call
restore_sprs because we don't have a prev and next thread.
Yeah needs to be tided up... we can't have another copy of the code.. obvio=
usly.
*prev,
#endif
=20
new_thread =3D &new->thread;
+ /*
+ * Why not &prev->thread; ?
+ * What is the difference between &prev->thread and
+ * ¤t->thread ?
+ */
=20
Why not just work it out and FIX THE CODE, rather than just rabbiting o=
n about
quoted
it! :-P
=20
Agreed - I started to and then had a mini freakout that things would
end really badly if they're not the same. So I left that comment as a
reminder to investigate.
=20
They should be the same though right?
So why do we do tm_recheckpoint at all? Shouldn't most of the tm_blah code go
away in process.c after all this?
I'm not sure I follow, we need to recheckpoint because we're going back
to userspace? Or would you rather calling the tm.S code directly from
the exception return path?
Yeah, I was thinking the point of this series was. We do tm_reclaim right on
entry and tm_recheckpoint right on exit.
Yeah that's the ultimate goal, considering I haven't been attacked or
offered more drugs I feel like what I've done isn't crazy. Your
feedback is great, thanks.
The bits in between (ie. the tm_blah() calls process.c) would mostly go away.
quoted
Yes, I hope we'll be able to have a fairly big cleanup commit of tm_
code in process.c at the end of this series.
Hi Cyril,
On 02/19/2018 09:22 PM, Cyril Bur wrote:
This is very much a proof of concept and if it isn't clear from the
commit names, still a work in progress.
I believe I have something that works - all the powerpc selftests
pass. I would like to get some eyes on it to a) see if I've missed
anything big and b) some opinions on if it is looking like a net
improvement.
I started to look at this patchset. The patchset apply cleanly on top of the
current kernel and I started to run some tests.
It seems there is a different behavior when there is a trap (as a illegal
instruction or a 'trap' instruction) inside the transaction.
In this case, the signal handler does not seem to be called, and and the task
segfaults. On current upstream, the signal handler is called and the program
can continue.
4.17 pristine
-------------
$ ./illegal
Failure
4.17 plus your patches
----------------------
$ ./illegal
[1] 2504 segmentation fault ./illegal
Here is a minimal example that is able to recreate this behaviour:
#include <stdio.h>
#include <signal.h>
int htm(){
asm goto ("tbegin. \n\t"
"beq %l[failure] \n\t"
"li 3, 3 \n\t"
"trap \n\t"
"tend. \n\t"
: : : : failure);
return 0;
failure:
printf("Failure\n");
return 1;
}
void signal_handler(int signo, siginfo_t *si, void *data) {
// Do nothing
}
int main(){
struct sigaction sa;
sa.sa_flags = SA_SIGINFO;
sa.sa_sigaction = signal_handler;
sigaction(SIGTRAP, &sa, NULL);
sigaction(SIGILL, &sa, NULL);
return htm();
}