ARM: smp: defer TPIDRURO update for SMP v6 configurations too
authorArd Biesheuvel <ardb@kernel.org>
Thu, 25 Nov 2021 22:21:45 +0000 (23:21 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Mon, 6 Dec 2021 11:49:17 +0000 (12:49 +0100)
Defer TPIDURO updates for user space until exit also for CPU_V6+SMP
configurations so that we can decide at runtime whether to use it to
carry the current pointer, provided that we are running on a CPU that
actually implements this register. This is needed for
THREAD_INFO_IN_TASK support for UP systems, which requires that all SMP
capable systems use the TPIDRURO based access to 'current' as the only
remaining alternative will be a global variable which only works on UP.

Acked-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Nicolas Pitre <nico@fluxnic.net>
Signed-off-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Marc Zyngier <maz@kernel.org>
Tested-by: Vladimir Murzin <vladimir.murzin@arm.com> # ARMv7M
arch/arm/include/asm/tls.h
arch/arm/kernel/entry-header.S

index c329649..d712c17 100644 (file)
        .endm
 
        .macro switch_tls_v6, base, tp, tpuser, tmp1, tmp2
-       ldr     \tmp1, =elf_hwcap
-       ldr     \tmp1, [\tmp1, #0]
+       ldr_va  \tmp1, elf_hwcap
        mov     \tmp2, #0xffff0fff
        tst     \tmp1, #HWCAP_TLS               @ hardware TLS available?
        streq   \tp, [\tmp2, #-15]              @ set TLS value at 0xffff0ff0
        mrcne   p15, 0, \tmp2, c13, c0, 2       @ get the user r/w register
+#ifndef CONFIG_SMP
        mcrne   p15, 0, \tp, c13, c0, 3         @ yes, set TLS register
+#endif
        mcrne   p15, 0, \tpuser, c13, c0, 2     @ set user r/w register
        strne   \tmp2, [\base, #TI_TP_VALUE + 4] @ save it
        .endm
@@ -43,7 +44,7 @@
 #elif defined(CONFIG_CPU_V6)
 #define tls_emu                0
 #define has_tls_reg            (elf_hwcap & HWCAP_TLS)
-#define defer_tls_reg_update   0
+#define defer_tls_reg_update   IS_ENABLED(CONFIG_SMP)
 #define switch_tls     switch_tls_v6
 #elif defined(CONFIG_CPU_32v6K)
 #define tls_emu                0
@@ -81,11 +82,11 @@ static inline void set_tls(unsigned long val)
         */
        barrier();
 
-       if (!tls_emu && !defer_tls_reg_update) {
-               if (has_tls_reg) {
+       if (!tls_emu) {
+               if (has_tls_reg && !defer_tls_reg_update) {
                        asm("mcr p15, 0, %0, c13, c0, 3"
                            : : "r" (val));
-               } else {
+               } else if (!has_tls_reg) {
 #ifdef CONFIG_KUSER_HELPERS
                        /*
                         * User space must never try to access this
index 268f7f4..cb82ff5 100644 (file)
 
 
        .macro  restore_user_regs, fast = 0, offset = 0
-#if defined(CONFIG_CPU_32v6K) && !defined(CONFIG_CPU_V6)
+#if defined(CONFIG_CPU_32v6K) || defined(CONFIG_SMP)
+#if defined(CONFIG_CPU_V6) && defined(CONFIG_SMP)
+ALT_SMP(b      .L1_\@  )
+ALT_UP( nop            )
+       ldr_va  r1, elf_hwcap
+       tst     r1, #HWCAP_TLS                  @ hardware TLS available?
+       beq     .L2_\@
+.L1_\@:
+#endif
        @ The TLS register update is deferred until return to user space so we
        @ can use it for other things while running in the kernel
        get_thread_info r1
        ldr     r1, [r1, #TI_TP_VALUE]
        mcr     p15, 0, r1, c13, c0, 3          @ set TLS register
+.L2_\@:
 #endif
 
        uaccess_enable r1, isb=0