Re: [PATCHv2] arm: Preserve TPIDRURW on context switch

From: Russell King - ARM Linux
Date: Fri May 03 2013 - 05:56:08 EST


On Fri, May 03, 2013 at 10:21:34AM +0100, Jonathan Austin wrote:
> .macro set_tls_v6k, tp, tmp1, tmp2
> - mcr p15, 0, \tp, c13, c0, 3 @ set TLS register
> - mov \tmp1, #0
> - mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
> + ldrd \tmp1, \tmp2, [\tp]
> + mcr p15, 0, \tmp1, c13, c0, 3 @ set user r/o TLS register
> + mcr p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register

So we're still back at stalling the pipeline with result delays on older
CPUs?

> + .endm
> +
> + .macro save_tlsuser_v6k, tp, tmp1, tmp2
> + @ TPIDRURW can be updated from userspace, so we have to re-read it
> + mrc p15, 0, \tmp2, c13, c0, 2 @ load user r/w TLS register
> + str \tmp2, [\tp, #4]
> .endm
>
> .macro set_tls_v6, tp, tmp1, tmp2
> @@ -16,15 +25,26 @@
> ldr \tmp1, [\tmp1, #0]
> mov \tmp2, #0xffff0fff
> tst \tmp1, #HWCAP_TLS @ hardware TLS available?
> - mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register
> - movne \tmp1, #0
> - mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
> - streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
> + ldrned \tmp1, \tmp2, [\tp]
> + ldreq \tmp1, [\tp]
> + mcrne p15, 0, \tmp1, c13, c0, 3 @ yes, set user r/o TLS register
> + mcrne p15, 0, \tmp2, c13, c0, 2 @ set user r/w TLS register
> + streq \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0

This at least is better.

> + .endm
> +
> + .macro save_tlsuser_v6, tp, tmp1, tmp2
> + @ TPIDRURW can be updated from userspace, so we have to re-read it
> + ldr \tmp1, =elf_hwcap
> + ldr \tmp1, [\tmp1, #0]
> + tst \tmp1, #HWCAP_TLS @ hardware TLS available?

But this isn't - this involves two delays.

> + mrcne p15, 0, \tmp2, c13, c0, 2 @ read user r/w TLS register
> + strne \tmp2, [\tp, #4] @ save in to thread_info
> .endm
>
> .macro set_tls_software, tp, tmp1, tmp2
> - mov \tmp1, #0xffff0fff
> - str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0
> + ldr \tmp1, [\tp]
> + mov \tmp2, #0xffff0fff
> + str \tmp1, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
> .endm
> #endif
>
> @@ -32,18 +52,31 @@
> #define tls_emu 1
> #define has_tls_reg 1
> #define set_tls set_tls_none
> +#define save_tlsuser save_tlsuser_none
> +#define get_tlsuser get_tlsuser_none
> #elif defined(CONFIG_CPU_V6)
> #define tls_emu 0
> #define has_tls_reg (elf_hwcap & HWCAP_TLS)
> #define set_tls set_tls_v6
> +#define save_tlsuser save_tlsuser_v6
> +#define get_tlsuser get_tlsuser_v6
> #elif defined(CONFIG_CPU_32v6K)
> #define tls_emu 0
> #define has_tls_reg 1
> #define set_tls set_tls_v6k
> +#define save_tlsuser save_tlsuser_v6k
> +#define get_tlsuser get_tlsuser_v6k
> #else
> #define tls_emu 0
> #define has_tls_reg 0
> #define set_tls set_tls_software
> +#define save_tlsuser save_tlsuser_none
> +#define get_tlsuser get_tlsuser_none
> #endif

This separation of setting and saving the TLS value is actually quite
silly. They're called from the same place, so lets just call it
"switch_tls" instead.

Here's just the assembly bits doing that - this is totally untested
of course:

arch/arm/include/asm/tls.h | 28 +++++++++++++++-------------
arch/arm/kernel/entry-armv.S | 4 ++--
2 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/arch/arm/include/asm/tls.h b/arch/arm/include/asm/tls.h
index 73409e6..9c377f1 100644
--- a/arch/arm/include/asm/tls.h
+++ b/arch/arm/include/asm/tls.h
@@ -2,27 +2,29 @@
#define __ASMARM_TLS_H

#ifdef __ASSEMBLY__
- .macro set_tls_none, tp, tmp1, tmp2
+ .macro switch_tls_none, base, tp, trw, tmp1, tmp2
.endm

- .macro set_tls_v6k, tp, tmp1, tmp2
+ .macro switch_tls_v6k, base, tp, trw, tmp1, tmp2
+ mrc p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register
mcr p15, 0, \tp, c13, c0, 3 @ set TLS register
- mov \tmp1, #0
- mcr p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
+ mcr p15, 0, \trw, c13, c0, 2 @ and the user r/w register
+ str \tmp2, [\base, #TI_TP_VALUE + 4]@ save it
.endm

- .macro set_tls_v6, tp, tmp1, tmp2
+ .macro switch_tls_v6, base, tp, trw, tmp1, tmp2
ldr \tmp1, =elf_hwcap
ldr \tmp1, [\tmp1, #0]
mov \tmp2, #0xffff0fff
tst \tmp1, #HWCAP_TLS @ hardware TLS available?
- mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register
- movne \tmp1, #0
- mcrne p15, 0, \tmp1, c13, c0, 2 @ clear user r/w TLS register
streq \tp, [\tmp2, #-15] @ set TLS value at 0xffff0ff0
+ mrcne p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register
+ mcrne p15, 0, \tp, c13, c0, 3 @ yes, set TLS register
+ mcrne p15, 0, \trw, c13, c0, 2 @ set user r/w register
+ strne \tmp2, [\base, #TI_TP_VALUE + 4]@ save it
.endm

- .macro set_tls_software, tp, tmp1, tmp2
+ .macro switch_tls_software, base, tp, trw, tmp1, tmp2
mov \tmp1, #0xffff0fff
str \tp, [\tmp1, #-15] @ set TLS value at 0xffff0ff0
.endm
@@ -31,19 +33,19 @@
#ifdef CONFIG_TLS_REG_EMUL
#define tls_emu 1
#define has_tls_reg 1
-#define set_tls set_tls_none
+#define switch_tls switch_tls_none
#elif defined(CONFIG_CPU_V6)
#define tls_emu 0
#define has_tls_reg (elf_hwcap & HWCAP_TLS)
-#define set_tls set_tls_v6
+#define switch_tls switch_tls_v6
#elif defined(CONFIG_CPU_32v6K)
#define tls_emu 0
#define has_tls_reg 1
-#define set_tls set_tls_v6k
+#define switch_tls switch_tls_v6k
#else
#define tls_emu 0
#define has_tls_reg 0
-#define set_tls set_tls_software
+#define switch_tls switch_tls_software
#endif

#endif /* __ASMARM_TLS_H */
diff --git a/arch/arm/kernel/entry-armv.S b/arch/arm/kernel/entry-armv.S
index 0f82098..81a08b1 100644
--- a/arch/arm/kernel/entry-armv.S
+++ b/arch/arm/kernel/entry-armv.S
@@ -728,15 +728,15 @@ ENTRY(__switch_to)
UNWIND(.fnstart )
UNWIND(.cantunwind )
add ip, r1, #TI_CPU_SAVE
- ldr r3, [r2, #TI_TP_VALUE]
ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack
THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack
THUMB( str sp, [ip], #4 )
THUMB( str lr, [ip], #4 )
+ ldrd r4, r5, [r2, #TI_TP_VALUE]
#ifdef CONFIG_CPU_USE_DOMAINS
ldr r6, [r2, #TI_CPU_DOMAIN]
#endif
- set_tls r3, r4, r5
+ switch_tls r2, r4, r5, r3, r7
#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
ldr r7, [r2, #TI_TASK]
ldr r8, =__stack_chk_guard

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/