[PATCH 1/1] powerpc: correct DSCR during TM context switch

Sam Bobroff sam.bobroff at au1.ibm.com
Wed Jun 4 13:33:56 EST 2014


Correct the DSCR SPR becoming temporarily corrupted when a task is
context switched when within a transaction. It is corrected when
the transaction is aborted (which will happen after a context switch)
but if the task has suspended (TSUSPEND) the transaction the incorrect
value can be seen.

The problem is caused by saving a thread's DSCR after it has already
been reverted to the CPU's default value:

__switch_to() calls __switch_to_tm()
	which calls tm_reclaim_task()
	which calls tm_reclaim_thread()
	which calls tm_reclaim() where the DSCR is reset
__switch_to() calls _switch
	_switch() saves the DSCR to thread.dscr

The fix is to treat the DSCR similarly to the TAR and save it early
in __switch_to().

The program below will expose the problem:

  #include <inttypes.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <assert.h>
  #include <asm/tm.h>

  #define TBEGIN          ".long 0x7C00051D ;"
  #define TEND            ".long 0x7C00055D ;"
  #define TCHECK          ".long 0x7C00059C ;"
  #define TSUSPEND        ".long 0x7C0005DD ;"
  #define TRESUME         ".long 0x7C2005DD ;"
  #define SPRN_TEXASR     0x82
  #define SPRN_DSCR       0x03

  int main(void) {
    uint64_t i = 0, rv, dscr1 = 1, dscr2, texasr;

    for (;;) {
      rv = 1;
      asm __volatile__ (
      "ld      3, %[dscr1];"
      "mtspr   %[sprn_dscr], 3;"
      TBEGIN
      "beq     1f;"
      TSUSPEND
      "2: ;"
      TCHECK
      "bc      4, 0, 2b;"
      "mfspr   3, %[sprn_dscr];"
      "std     3, %[dscr2];"
      "mfspr   3, %[sprn_texasr];"
      "std     3, %[texasr];"
      TRESUME
      TEND
      "li      %[rv], 0;"
      "1: ;"
      : [rv]"=r"(rv), [dscr2]"=m"(dscr2), [texasr]"=m"(texasr)
      : [dscr1]"m"(dscr1)
      , [sprn_dscr]"i"(SPRN_DSCR), [sprn_texasr]"i"(SPRN_TEXASR)
      : "memory", "r3"
      );
      assert(rv);
      if ((texasr >> 56) == TM_CAUSE_RESCHED) {
        putchar('!');
        fflush(stdout);
        i++;
      }
      else {
        putchar('.');
        fflush(stdout);
      }
      if (dscr2 != dscr1) {
        printf("\n==== DSCR incorrect: 0x%lx (expecting 0x%lx)\n", dscr2, dscr1);
        exit(EXIT_FAILURE);
      }
      if (i > 10) {
        printf("\n==== DSCR TM context switching seems OK.\n");
        exit(EXIT_SUCCESS);
      }
    }
  }

Signed-off-by: Sam Bobroff <sam.bobroff at au1.ibm.com>
---
 arch/powerpc/include/asm/switch_to.h |    6 ++++--
 arch/powerpc/kernel/entry_64.S       |    6 ------
 arch/powerpc/kernel/process.c        |    8 ++++----
 3 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/arch/powerpc/include/asm/switch_to.h b/arch/powerpc/include/asm/switch_to.h
index 2737f46..3efd0e5 100644
--- a/arch/powerpc/include/asm/switch_to.h
+++ b/arch/powerpc/include/asm/switch_to.h
@@ -16,13 +16,15 @@ struct thread_struct;
 extern struct task_struct *_switch(struct thread_struct *prev,
 				   struct thread_struct *next);
 #ifdef CONFIG_PPC_BOOK3S_64
-static inline void save_tar(struct thread_struct *prev)
+static inline void save_early_sprs(struct thread_struct *prev)
 {
 	if (cpu_has_feature(CPU_FTR_ARCH_207S))
 		prev->tar = mfspr(SPRN_TAR);
+	if (cpu_has_feature(CPU_FTR_DSCR))
+		prev->dscr = mfspr(SPRN_DSCR);
 }
 #else
-static inline void save_tar(struct thread_struct *prev) {}
+static inline void save_early_sprs(struct thread_struct *prev) {}
 #endif
 
 extern void enable_kernel_fp(void);
diff --git a/arch/powerpc/kernel/entry_64.S b/arch/powerpc/kernel/entry_64.S
index 662c6dd..a107f4a 100644
--- a/arch/powerpc/kernel/entry_64.S
+++ b/arch/powerpc/kernel/entry_64.S
@@ -432,12 +432,6 @@ BEGIN_FTR_SECTION
 	std	r24,THREAD_VRSAVE(r3)
 END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 #endif /* CONFIG_ALTIVEC */
-#ifdef CONFIG_PPC64
-BEGIN_FTR_SECTION
-	mfspr	r25,SPRN_DSCR
-	std	r25,THREAD_DSCR(r3)
-END_FTR_SECTION_IFSET(CPU_FTR_DSCR)
-#endif
 	and.	r0,r0,r22
 	beq+	1f
 	andc	r22,r22,r0
diff --git a/arch/powerpc/kernel/process.c b/arch/powerpc/kernel/process.c
index e247898..8d2065e 100644
--- a/arch/powerpc/kernel/process.c
+++ b/arch/powerpc/kernel/process.c
@@ -771,15 +771,15 @@ struct task_struct *__switch_to(struct task_struct *prev,
 
 	WARN_ON(!irqs_disabled());
 
-	/* Back up the TAR across context switches.
+	/* Back up the TAR and DSCR across context switches.
 	 * Note that the TAR is not available for use in the kernel.  (To
 	 * provide this, the TAR should be backed up/restored on exception
 	 * entry/exit instead, and be in pt_regs.  FIXME, this should be in
 	 * pt_regs anyway (for debug).)
-	 * Save the TAR here before we do treclaim/trecheckpoint as these
-	 * will change the TAR.
+	 * Save the TAR and DSCR here before we do treclaim/trecheckpoint as
+	 * these will change them.
 	 */
-	save_tar(&prev->thread);
+	save_early_sprs(&prev->thread);
 
 	__switch_to_tm(prev);
 
-- 
1.7.10.4



More information about the Linuxppc-dev mailing list