powerpc: Restore correct DSCR in context switch
authorAnton Blanchard <anton@samba.org>
Mon, 3 Sep 2012 16:51:10 +0000 (16:51 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 14 Sep 2012 17:00:49 +0000 (10:00 -0700)
commit 714332858bfd40dcf8f741498336d93875c23aa7 upstream.

During a context switch we always restore the per thread DSCR value.
If we aren't doing explicit DSCR management
(ie thread.dscr_inherit == 0) and the default DSCR changed while
the process has been sleeping we end up with the wrong value.

Check thread.dscr_inherit and select the default DSCR or per thread
DSCR as required.

This was found with the following test case, when running with
more threads than CPUs (ie forcing context switching):

http://ozlabs.org/~anton/junkcode/dscr_default_test.c

With the four patches applied I can run a combination of all
test cases successfully at the same time:

http://ozlabs.org/~anton/junkcode/dscr_default_test.c
http://ozlabs.org/~anton/junkcode/dscr_explicit_test.c
http://ozlabs.org/~anton/junkcode/dscr_inherit_test.c

Signed-off-by: Anton Blanchard <anton@samba.org>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/entry_64.S

index 36e1c8a29be885c498e04b77b2f11e683221e08d..4c12a751beaa39c1ab4b65c3cb956f191b1c568a 100644 (file)
@@ -75,6 +75,7 @@ int main(void)
        DEFINE(SIGSEGV, SIGSEGV);
        DEFINE(NMI_MASK, NMI_MASK);
        DEFINE(THREAD_DSCR, offsetof(struct thread_struct, dscr));
+       DEFINE(THREAD_DSCR_INHERIT, offsetof(struct thread_struct, dscr_inherit));
 #else
        DEFINE(THREAD_INFO, offsetof(struct task_struct, stack));
 #endif /* CONFIG_PPC64 */
index d834425186aeaa7fae6905bd8bffb50d0d99354d..654fc535ed00531f70db0ea35ae53d7ed676f54b 100644 (file)
@@ -380,6 +380,12 @@ _GLOBAL(ret_from_fork)
        li      r3,0
        b       syscall_exit
 
+       .section        ".toc","aw"
+DSCR_DEFAULT:
+       .tc dscr_default[TC],dscr_default
+
+       .section        ".text"
+
 /*
  * This routine switches between two different tasks.  The process
  * state of one is saved on its kernel stack.  Then the state
@@ -519,9 +525,6 @@ END_MMU_FTR_SECTION_IFSET(MMU_FTR_1T_SEGMENT)
        mr      r1,r8           /* start using new stack pointer */
        std     r7,PACAKSAVE(r13)
 
-       ld      r6,_CCR(r1)
-       mtcrf   0xFF,r6
-
 #ifdef CONFIG_ALTIVEC
 BEGIN_FTR_SECTION
        ld      r0,THREAD_VRSAVE(r4)
@@ -530,14 +533,22 @@ END_FTR_SECTION_IFSET(CPU_FTR_ALTIVEC)
 #endif /* CONFIG_ALTIVEC */
 #ifdef CONFIG_PPC64
 BEGIN_FTR_SECTION
+       lwz     r6,THREAD_DSCR_INHERIT(r4)
+       ld      r7,DSCR_DEFAULT@toc(2)
        ld      r0,THREAD_DSCR(r4)
-       cmpd    r0,r25
-       beq     1f
+       cmpwi   r6,0
+       bne     1f
+       ld      r0,0(r7)
+1:     cmpd    r0,r25
+       beq     2f
        mtspr   SPRN_DSCR,r0
-1:     
+2:
 END_FTR_SECTION_IFSET(CPU_FTR_DSCR)
 #endif
 
+       ld      r6,_CCR(r1)
+       mtcrf   0xFF,r6
+
        /* r3-r13 are destroyed -- Cort */
        REST_8GPRS(14, r1)
        REST_10GPRS(22, r1)