powerpc: Restore dbcr0 on user space exit
authorBharat Bhushan <r65777@freescale.com>
Wed, 22 May 2013 04:20:59 +0000 (09:50 +0530)
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>
Thu, 20 Jun 2013 07:04:19 +0000 (17:04 +1000)
On BookE (Branch taken + Single Step) is as same as Branch Taken
on BookS and in Linux we simulate BookS behavior for BookE as well.
When doing so, in Branch taken handling we want to set DBCR0_IC but
we update the current->thread->dbcr0 and not DBCR0.

Now on 64bit the current->thread.dbcr0 (and other debug registers)
is synchronized ONLY on context switch flow. But after handling
Branch taken in debug exception if we return back to user space
without context switch then single stepping change (DBCR0_ICMP)
does not get written in h/w DBCR0 and Instruction Complete exception
does not happen.

This fixes using ptrace reliably on BookE-PowerPC

lmbench latency test (lat_syscall) Results are (they varies a little
on each run)

1) ./lat_syscall <action> /dev/shm/uImage

action: Open read write stat fstat null
Before: 3.8618 0.2017 0.2851 1.6789 0.2256 0.0856
After: 3.8580 0.2017 0.2851 1.6955 0.2255 0.0856

1) ./lat_syscall -P 2 -N 10 <action> /dev/shm/uImage
action: Open read write stat fstat null
Before: 4.1388 0.2238 0.3066 1.7106 0.2256 0.0856
After: 4.1413 0.2236 0.3062 1.7107 0.2256 0.0856

[ Slightly modified to avoid extra branch in the fast path
  on Book3S and fix build on all non-BookE 64-bit -- BenH
]

Signed-off-by: Bharat Bhushan <bharat.bhushan@freescale.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
arch/powerpc/kernel/asm-offsets.c
arch/powerpc/kernel/entry_64.S

index 6f16ffa..dc684b1 100644 (file)
@@ -105,9 +105,6 @@ int main(void)
        DEFINE(KSP_VSID, offsetof(struct thread_struct, ksp_vsid));
 #else /* CONFIG_PPC64 */
        DEFINE(PGDIR, offsetof(struct thread_struct, pgdir));
-#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
-       DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
-#endif
 #ifdef CONFIG_SPE
        DEFINE(THREAD_EVR0, offsetof(struct thread_struct, evr[0]));
        DEFINE(THREAD_ACC, offsetof(struct thread_struct, acc));
@@ -115,6 +112,9 @@ int main(void)
        DEFINE(THREAD_USED_SPE, offsetof(struct thread_struct, used_spe));
 #endif /* CONFIG_SPE */
 #endif /* CONFIG_PPC64 */
+#if defined(CONFIG_4xx) || defined(CONFIG_BOOKE)
+       DEFINE(THREAD_DBCR0, offsetof(struct thread_struct, dbcr0));
+#endif
 #ifdef CONFIG_KVM_BOOK3S_32_HANDLER
        DEFINE(THREAD_KVM_SVCPU, offsetof(struct thread_struct, kvm_shadow_vcpu));
 #endif
index 8741c85..ab15b8d 100644 (file)
@@ -629,21 +629,43 @@ _GLOBAL(ret_from_except_lite)
 
        CURRENT_THREAD_INFO(r9, r1)
        ld      r3,_MSR(r1)
+#ifdef CONFIG_PPC_BOOK3E
+       ld      r10,PACACURRENT(r13)
+#endif /* CONFIG_PPC_BOOK3E */
        ld      r4,TI_FLAGS(r9)
        andi.   r3,r3,MSR_PR
        beq     resume_kernel
+#ifdef CONFIG_PPC_BOOK3E
+       lwz     r3,(THREAD+THREAD_DBCR0)(r10)
+#endif /* CONFIG_PPC_BOOK3E */
 
        /* Check current_thread_info()->flags */
        andi.   r0,r4,_TIF_USER_WORK_MASK
+#ifdef CONFIG_PPC_BOOK3E
+       bne     1f
+       /*
+        * Check to see if the dbcr0 register is set up to debug.
+        * Use the internal debug mode bit to do this.
+        */
+       andis.  r0,r3,DBCR0_IDM@h
        beq     restore
-
-       andi.   r0,r4,_TIF_NEED_RESCHED
-       beq     1f
+       mfmsr   r0
+       rlwinm  r0,r0,0,~MSR_DE /* Clear MSR.DE */
+       mtmsr   r0
+       mtspr   SPRN_DBCR0,r3
+       li      r10, -1
+       mtspr   SPRN_DBSR,r10
+       b       restore
+#else
+       beq     restore
+#endif
+1:     andi.   r0,r4,_TIF_NEED_RESCHED
+       beq     2f
        bl      .restore_interrupts
        SCHEDULE_USER
        b       .ret_from_except_lite
 
-1:     bl      .save_nvgprs
+2:     bl      .save_nvgprs
        bl      .restore_interrupts
        addi    r3,r1,STACK_FRAME_OVERHEAD
        bl      .do_notify_resume