]> git.baikalelectronics.ru Git - kernel.git/commitdiff
ARM: implement THREAD_INFO_IN_TASK for uniprocessor systems
authorArd Biesheuvel <ardb@kernel.org>
Wed, 24 Nov 2021 13:08:11 +0000 (14:08 +0100)
committerArd Biesheuvel <ardb@kernel.org>
Mon, 6 Dec 2021 11:49:17 +0000 (12:49 +0100)
On UP systems, only a single task can be 'current' at the same time,
which means we can use a global variable to track it. This means we can
also enable THREAD_INFO_IN_TASK for those systems, as in that case,
thread_info is accessed via current rather than the other way around,
removing the need to store thread_info at the base of the task stack.
This, in turn, permits us to enable IRQ stacks and vmap'ed stacks on UP
systems as well.

To partially mitigate the performance overhead of this arrangement, use
a ADD/ADD/LDR sequence with the appropriate PC-relative group
relocations to load the value of current when needed. This means that
accessing current will still only require a single load as before,
avoiding the need for a literal to carry the address of the global
variable in each function. However, accessing thread_info will now
require this load as well.

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
12 files changed:
arch/arm/Kconfig
arch/arm/include/asm/assembler.h
arch/arm/include/asm/current.h
arch/arm/include/asm/switch_to.h
arch/arm/include/asm/thread_info.h
arch/arm/kernel/asm-offsets.c
arch/arm/kernel/entry-armv.S
arch/arm/kernel/entry-v7m.S
arch/arm/kernel/head-common.S
arch/arm/kernel/process.c
arch/arm/kernel/smp.c
arch/arm/kernel/traps.c

index 5ad2151c43dd04173947661c600ee5e355badc4a..50ae5286f59b5096c13ece1dd6dce191bdb0dd1a 100644 (file)
@@ -127,8 +127,8 @@ config ARM
        select PERF_USE_VMALLOC
        select RTC_LIB
        select SYS_SUPPORTS_APM_EMULATION
-       select THREAD_INFO_IN_TASK if CURRENT_POINTER_IN_TPIDRURO
-       select HAVE_ARCH_VMAP_STACK if MMU && THREAD_INFO_IN_TASK && (!LD_IS_LLD || LLD_VERSION >= 140000)
+       select THREAD_INFO_IN_TASK
+       select HAVE_ARCH_VMAP_STACK if MMU && (!LD_IS_LLD || LLD_VERSION >= 140000)
        select TRACE_IRQFLAGS_SUPPORT if !CPU_V7M
        # Above selects are sorted alphabetically; please add new ones
        # according to that.  Thanks.
@@ -1158,7 +1158,7 @@ config CURRENT_POINTER_IN_TPIDRURO
 
 config IRQSTACKS
        def_bool y
-       depends on THREAD_INFO_IN_TASK
+       depends on MMU
        select HAVE_IRQ_EXIT_ON_IRQ_STACK
        select HAVE_SOFTIRQ_ON_OWN_STACK
 
@@ -1608,7 +1608,7 @@ config CC_HAVE_STACKPROTECTOR_TLS
 
 config STACKPROTECTOR_PER_TASK
        bool "Use a unique stack canary value for each task"
-       depends on STACKPROTECTOR && THREAD_INFO_IN_TASK && !XIP_DEFLATED_DATA
+       depends on STACKPROTECTOR && CURRENT_POINTER_IN_TPIDRURO && !XIP_DEFLATED_DATA
        depends on GCC_PLUGINS || CC_HAVE_STACKPROTECTOR_TLS
        select GCC_PLUGIN_ARM_SSP_PER_TASK if !CC_HAVE_STACKPROTECTOR_TLS
        default y
index f9b3dd0e9ef556842ce5a8b8fce9b35a51887ff0..59d7b9e819346d3a55e82b3fdb6bde1a7900902a 100644 (file)
@@ -203,41 +203,12 @@ THUMB(    fpreg   .req    r7      )
        .endm
        .endr
 
-       .macro  get_current, rd
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
-       mrc     p15, 0, \rd, c13, c0, 3         @ get TPIDRURO register
-#else
-       get_thread_info \rd
-       ldr     \rd, [\rd, #TI_TASK]
-#endif
-       .endm
-
-       .macro  set_current, rn
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
-       mcr     p15, 0, \rn, c13, c0, 3         @ set TPIDRURO register
-#endif
-       .endm
-
-       .macro  reload_current, t1:req, t2:req
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
-       ldr_this_cpu \t1, __entry_task, \t1, \t2
-       mcr     p15, 0, \t1, c13, c0, 3         @ store in TPIDRURO
-#endif
-       .endm
-
 /*
  * Get current thread_info.
  */
        .macro  get_thread_info, rd
-#ifdef CONFIG_THREAD_INFO_IN_TASK
        /* thread_info is the first member of struct task_struct */
        get_current \rd
-#else
- ARM(  mov     \rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT    )
- THUMB(        mov     \rd, sp                 )
- THUMB(        lsr     \rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT       )
-       mov     \rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT
-#endif
        .endm
 
 /*
@@ -330,6 +301,60 @@ ALT_UP_B(.L1_\@)
 #endif
        .endm
 
+       /*
+        * set_current - store the task pointer of this CPU's current task
+        */
+       .macro          set_current, rn:req, tmp:req
+#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
+9998:  mcr             p15, 0, \rn, c13, c0, 3         @ set TPIDRURO register
+#ifdef CONFIG_CPU_V6
+ALT_UP_B(.L0_\@)
+       .subsection     1
+.L0_\@: str_va         \rn, __current, \tmp
+       b               .L1_\@
+       .previous
+.L1_\@:
+#endif
+#else
+       str_va          \rn, __current, \tmp
+#endif
+       .endm
+
+       /*
+        * get_current - load the task pointer of this CPU's current task
+        */
+       .macro          get_current, rd:req
+#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
+9998:  mrc             p15, 0, \rd, c13, c0, 3         @ get TPIDRURO register
+#ifdef CONFIG_CPU_V6
+ALT_UP_B(.L0_\@)
+       .subsection     1
+.L0_\@: ldr_va         \rd, __current
+       b               .L1_\@
+       .previous
+.L1_\@:
+#endif
+#else
+       ldr_va          \rd, __current
+#endif
+       .endm
+
+       /*
+        * reload_current - reload the task pointer of this CPU's current task
+        *                  into the TLS register
+        */
+       .macro          reload_current, t1:req, t2:req
+#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
+#ifdef CONFIG_CPU_V6
+ALT_SMP(nop)
+ALT_UP_B(.L0_\@)
+#endif
+       ldr_this_cpu    \t1, __entry_task, \t1, \t2
+       mcr             p15, 0, \t1, c13, c0, 3         @ store in TPIDRURO
+.L0_\@:
+#endif
+       .endm
+
 /*
  * Instruction barrier
  */
index 6bf0aad672c3743984fc85f71438869a515492d9..69ecf4c6c7255fbd6823567d78d86e3674f159f1 100644 (file)
@@ -8,25 +8,18 @@
 #define _ASM_ARM_CURRENT_H
 
 #ifndef __ASSEMBLY__
+#include <asm/insn.h>
 
 struct task_struct;
 
-static inline void set_current(struct task_struct *cur)
-{
-       if (!IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO))
-               return;
-
-       /* Set TPIDRURO */
-       asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
-}
+extern struct task_struct *__current;
 
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
-
-static inline struct task_struct *get_current(void)
+static inline __attribute_const__ struct task_struct *get_current(void)
 {
        struct task_struct *cur;
 
 #if __has_builtin(__builtin_thread_pointer) && \
+    defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) && \
     !(defined(CONFIG_THUMB2_KERNEL) && \
       defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION < 130001)
        /*
@@ -39,16 +32,30 @@ static inline struct task_struct *get_current(void)
         * https://github.com/ClangBuiltLinux/linux/issues/1485
         */
        cur = __builtin_thread_pointer();
+#elif defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
+       asm("0: mrc p15, 0, %0, c13, c0, 3                      \n\t"
+#ifdef CONFIG_CPU_V6
+           "1:                                                 \n\t"
+           "   .subsection 1                                   \n\t"
+           "2: " LOAD_SYM_ARMV6(%0, __current) "               \n\t"
+           "   b       1b                                      \n\t"
+           "   .previous                                       \n\t"
+           "   .pushsection \".alt.smp.init\", \"a\"           \n\t"
+           "   .long   0b - .                                  \n\t"
+           "   b       . + (2b - 0b)                           \n\t"
+           "   .popsection                                     \n\t"
+#endif
+           : "=r"(cur));
+#elif __LINUX_ARM_ARCH__>=7 || \
+      (defined(MODULE) && defined(CONFIG_ARM_MODULE_PLTS))
+       cur = __current;
 #else
-       asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(cur));
+       asm(LOAD_SYM_ARMV6(%0, __current) : "=r"(cur));
 #endif
        return cur;
 }
 
 #define current get_current()
-#else
-#include <asm-generic/current.h>
-#endif /* CONFIG_CURRENT_POINTER_IN_TPIDRURO */
 
 #endif /* __ASSEMBLY__ */
 
index b55c7b2755e49aa36f8e8f7104c0299fd877067b..a482c99934ff81b4adb92fa782d5c1d978310afd 100644 (file)
@@ -40,7 +40,8 @@ static inline void set_ti_cpu(struct task_struct *p)
 do {                                                                   \
        __complete_pending_tlbi();                                      \
        set_ti_cpu(next);                                               \
-       if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO))             \
+       if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO) ||           \
+           IS_ENABLED(CONFIG_SMP))                                     \
                __this_cpu_write(__entry_task, next);                   \
        last = __switch_to(prev,task_thread_info(prev), task_thread_info(next));        \
 } while (0)
index 004b89d86224b15e875556f7ff14775da5d3c447..aecc403b2880493d6925eed075bf405b77ee03cb 100644 (file)
@@ -62,9 +62,6 @@ struct cpu_context_save {
 struct thread_info {
        unsigned long           flags;          /* low level flags */
        int                     preempt_count;  /* 0 => preemptable, <0 => bug */
-#ifndef CONFIG_THREAD_INFO_IN_TASK
-       struct task_struct      *task;          /* main task structure */
-#endif
        __u32                   cpu;            /* cpu */
        __u32                   cpu_domain;     /* cpu domain */
        struct cpu_context_save cpu_context;    /* cpu context */
@@ -80,39 +77,15 @@ struct thread_info {
 
 #define INIT_THREAD_INFO(tsk)                                          \
 {                                                                      \
-       INIT_THREAD_INFO_TASK(tsk)                                      \
        .flags          = 0,                                            \
        .preempt_count  = INIT_PREEMPT_COUNT,                           \
 }
 
-#ifdef CONFIG_THREAD_INFO_IN_TASK
-#define INIT_THREAD_INFO_TASK(tsk)
-
 static inline struct task_struct *thread_task(struct thread_info* ti)
 {
        return (struct task_struct *)ti;
 }
 
-#else
-#define INIT_THREAD_INFO_TASK(tsk)     .task = &(tsk),
-
-static inline struct task_struct *thread_task(struct thread_info* ti)
-{
-       return ti->task;
-}
-
-/*
- * how to get the thread information struct from C
- */
-static inline struct thread_info *current_thread_info(void) __attribute_const__;
-
-static inline struct thread_info *current_thread_info(void)
-{
-       return (struct thread_info *)
-               (current_stack_pointer & ~(THREAD_SIZE - 1));
-}
-#endif
-
 #define thread_saved_pc(tsk)   \
        ((unsigned long)(task_thread_info(tsk)->cpu_context.pc))
 #define thread_saved_sp(tsk)   \
index 645845e4982a6564c4c8dad4990917fb137f8b14..2c8d76fd7c66298ad2fd8c10d471953fc930ad1c 100644 (file)
@@ -43,9 +43,6 @@ int main(void)
   BLANK();
   DEFINE(TI_FLAGS,             offsetof(struct thread_info, flags));
   DEFINE(TI_PREEMPT,           offsetof(struct thread_info, preempt_count));
-#ifndef CONFIG_THREAD_INFO_IN_TASK
-  DEFINE(TI_TASK,              offsetof(struct thread_info, task));
-#endif
   DEFINE(TI_CPU,               offsetof(struct thread_info, cpu));
   DEFINE(TI_CPU_DOMAIN,                offsetof(struct thread_info, cpu_domain));
   DEFINE(TI_CPU_SAVE,          offsetof(struct thread_info, cpu_context));
index 43d917f0d9a93ef7afa5bc175f30cce475c10dcf..b58bda51e4b87d6c20d699de7ee7c26af787822e 100644 (file)
@@ -807,12 +807,13 @@ ENTRY(__switch_to)
        switch_tls r1, r4, r5, r3, r7
 #if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) && \
     !defined(CONFIG_STACKPROTECTOR_PER_TASK)
-       ldr     r9, [r2, #TI_TASK]
        ldr     r8, =__stack_chk_guard
        .if (TSK_STACK_CANARY > IMM12_MASK)
-       add     r9, r9, #TSK_STACK_CANARY & ~IMM12_MASK
-       .endif
+       add     r9, r2, #TSK_STACK_CANARY & ~IMM12_MASK
        ldr     r9, [r9, #TSK_STACK_CANARY & IMM12_MASK]
+       .else
+       ldr     r9, [r2, #TSK_STACK_CANARY & IMM12_MASK]
+       .endif
 #endif
        mov     r7, r2                          @ Preserve 'next'
 #ifdef CONFIG_CPU_USE_DOMAINS
@@ -829,7 +830,7 @@ ENTRY(__switch_to)
 #endif
        mov     r0, r5
 #if !defined(CONFIG_THUMB2_KERNEL) && !defined(CONFIG_VMAP_STACK)
-       set_current r7
+       set_current r7, r8
        ldmia   r4, {r4 - sl, fp, sp, pc}       @ Load all regs saved previously
 #else
        mov     r1, r7
@@ -851,7 +852,7 @@ ENTRY(__switch_to)
        @ switches us to another stack, with few other side effects. In order
        @ to prevent this distinction from causing any inconsistencies, let's
        @ keep the 'set_current' call as close as we can to the update of SP.
-       set_current r1
+       set_current r1, r2
        mov     sp, ip
        ret     lr
 #endif
index 520dd43e7e08305cb08864be7bcc6626944b4705..4e0d318b67c6c92ebadb76ce3b140f33ec49d697 100644 (file)
@@ -97,15 +97,17 @@ ENTRY(__switch_to)
        str     sp, [ip], #4
        str     lr, [ip], #4
        mov     r5, r0
+       mov     r6, r2                  @ Preserve 'next'
        add     r4, r2, #TI_CPU_SAVE
        ldr     r0, =thread_notify_head
        mov     r1, #THREAD_NOTIFY_SWITCH
        bl      atomic_notifier_call_chain
-       mov     ip, r4
        mov     r0, r5
-       ldmia   ip!, {r4 - r11}         @ Load all regs saved previously
-       ldr     sp, [ip]
-       ldr     pc, [ip, #4]!
+       mov     r1, r6
+       ldmia   r4, {r4 - r12, lr}      @ Load all regs saved previously
+       set_current r1, r2
+       mov     sp, ip
+       bx      lr
        .fnend
 ENDPROC(__switch_to)
 
index da18e0a17dc20cdb518fc6dd98ecfafc903b7d58..42cae73fcc19d01647c4c0ae03be7db9234cc767 100644 (file)
@@ -105,10 +105,8 @@ __mmap_switched:
        mov     r1, #0
        bl      __memset                        @ clear .bss
 
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
        adr_l   r0, init_task                   @ get swapper task_struct
-       set_current r0
-#endif
+       set_current r0, r1
 
        ldmia   r4, {r0, r1, r2, r3}
        str     r9, [r0]                        @ Save processor ID
index d47159f3791c682dce4d25bb1d4a026c9494ed68..0617af11377f09c5b1446fcc538f35f7044f0afa 100644 (file)
@@ -36,7 +36,7 @@
 
 #include "signal.h"
 
-#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
+#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
 DEFINE_PER_CPU(struct task_struct *, __entry_task);
 #endif
 
@@ -46,6 +46,11 @@ unsigned long __stack_chk_guard __read_mostly;
 EXPORT_SYMBOL(__stack_chk_guard);
 #endif
 
+#ifndef CONFIG_CURRENT_POINTER_IN_TPIDRURO
+asmlinkage struct task_struct *__current;
+EXPORT_SYMBOL(__current);
+#endif
+
 static const char *processor_modes[] __maybe_unused = {
   "USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
   "UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",
index 9c55ca915ba4ab321a51a7c0eca55bac3666e5c7..951559e5bea33e62be08df6917d3fd85a3a7d4d1 100644 (file)
@@ -403,6 +403,17 @@ static void smp_store_cpu_info(unsigned int cpuid)
        check_cpu_icache_size(cpuid);
 }
 
+static void set_current(struct task_struct *cur)
+{
+       if (!IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO) && !is_smp()) {
+               __current = cur;
+               return;
+       }
+
+       /* Set TPIDRURO */
+       asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
+}
+
 /*
  * This is the secondary CPU boot entry.  We're using this CPUs
  * idle thread stack, but a set of temporary page tables.
index b28a705c49cbbb91afb706c4362c0356d60b144b..3f38357efc4627c585c8a14f52db40a85387be46 100644 (file)
@@ -865,7 +865,9 @@ early_initcall(allocate_overflow_stacks);
 asmlinkage void handle_bad_stack(struct pt_regs *regs)
 {
        unsigned long tsk_stk = (unsigned long)current->stack;
+#ifdef CONFIG_IRQSTACKS
        unsigned long irq_stk = (unsigned long)this_cpu_read(irq_stack_ptr);
+#endif
        unsigned long ovf_stk = (unsigned long)this_cpu_read(overflow_stack_ptr);
 
        console_verbose();
@@ -873,8 +875,10 @@ asmlinkage void handle_bad_stack(struct pt_regs *regs)
 
        pr_emerg("Task stack:     [0x%08lx..0x%08lx]\n",
                 tsk_stk, tsk_stk + THREAD_SIZE);
+#ifdef CONFIG_IRQSTACKS
        pr_emerg("IRQ stack:      [0x%08lx..0x%08lx]\n",
                 irq_stk - THREAD_SIZE, irq_stk);
+#endif
        pr_emerg("Overflow stack: [0x%08lx..0x%08lx]\n",
                 ovf_stk - OVERFLOW_STACK_SIZE, ovf_stk);