Skip to content

Commit 9c46929

Browse files
committed
ARM: implement THREAD_INFO_IN_TASK for uniprocessor systems
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 <[email protected]> Acked-by: Nicolas Pitre <[email protected]> Signed-off-by: Ard Biesheuvel <[email protected]> Tested-by: Marc Zyngier <[email protected]> Tested-by: Vladimir Murzin <[email protected]> # ARMv7M
1 parent c275591 commit 9c46929

File tree

12 files changed

+116
-92
lines changed

12 files changed

+116
-92
lines changed

arch/arm/Kconfig

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,8 +127,8 @@ config ARM
127127
select PERF_USE_VMALLOC
128128
select RTC_LIB
129129
select SYS_SUPPORTS_APM_EMULATION
130-
select THREAD_INFO_IN_TASK if CURRENT_POINTER_IN_TPIDRURO
131-
select HAVE_ARCH_VMAP_STACK if MMU && THREAD_INFO_IN_TASK && (!LD_IS_LLD || LLD_VERSION >= 140000)
130+
select THREAD_INFO_IN_TASK
131+
select HAVE_ARCH_VMAP_STACK if MMU && (!LD_IS_LLD || LLD_VERSION >= 140000)
132132
select TRACE_IRQFLAGS_SUPPORT if !CPU_V7M
133133
# Above selects are sorted alphabetically; please add new ones
134134
# according to that. Thanks.
@@ -1158,7 +1158,7 @@ config CURRENT_POINTER_IN_TPIDRURO
11581158

11591159
config IRQSTACKS
11601160
def_bool y
1161-
depends on THREAD_INFO_IN_TASK
1161+
depends on MMU
11621162
select HAVE_IRQ_EXIT_ON_IRQ_STACK
11631163
select HAVE_SOFTIRQ_ON_OWN_STACK
11641164

@@ -1608,7 +1608,7 @@ config CC_HAVE_STACKPROTECTOR_TLS
16081608

16091609
config STACKPROTECTOR_PER_TASK
16101610
bool "Use a unique stack canary value for each task"
1611-
depends on STACKPROTECTOR && THREAD_INFO_IN_TASK && !XIP_DEFLATED_DATA
1611+
depends on STACKPROTECTOR && CURRENT_POINTER_IN_TPIDRURO && !XIP_DEFLATED_DATA
16121612
depends on GCC_PLUGINS || CC_HAVE_STACKPROTECTOR_TLS
16131613
select GCC_PLUGIN_ARM_SSP_PER_TASK if !CC_HAVE_STACKPROTECTOR_TLS
16141614
default y

arch/arm/include/asm/assembler.h

Lines changed: 54 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -203,41 +203,12 @@ THUMB( fpreg .req r7 )
203203
.endm
204204
.endr
205205

206-
.macro get_current, rd
207-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
208-
mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register
209-
#else
210-
get_thread_info \rd
211-
ldr \rd, [\rd, #TI_TASK]
212-
#endif
213-
.endm
214-
215-
.macro set_current, rn
216-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
217-
mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register
218-
#endif
219-
.endm
220-
221-
.macro reload_current, t1:req, t2:req
222-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
223-
ldr_this_cpu \t1, __entry_task, \t1, \t2
224-
mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO
225-
#endif
226-
.endm
227-
228206
/*
229207
* Get current thread_info.
230208
*/
231209
.macro get_thread_info, rd
232-
#ifdef CONFIG_THREAD_INFO_IN_TASK
233210
/* thread_info is the first member of struct task_struct */
234211
get_current \rd
235-
#else
236-
ARM( mov \rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT )
237-
THUMB( mov \rd, sp )
238-
THUMB( lsr \rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT )
239-
mov \rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT
240-
#endif
241212
.endm
242213

243214
/*
@@ -330,6 +301,60 @@ ALT_UP_B(.L1_\@)
330301
#endif
331302
.endm
332303

304+
/*
305+
* set_current - store the task pointer of this CPU's current task
306+
*/
307+
.macro set_current, rn:req, tmp:req
308+
#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
309+
9998: mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register
310+
#ifdef CONFIG_CPU_V6
311+
ALT_UP_B(.L0_\@)
312+
.subsection 1
313+
.L0_\@: str_va \rn, __current, \tmp
314+
b .L1_\@
315+
.previous
316+
.L1_\@:
317+
#endif
318+
#else
319+
str_va \rn, __current, \tmp
320+
#endif
321+
.endm
322+
323+
/*
324+
* get_current - load the task pointer of this CPU's current task
325+
*/
326+
.macro get_current, rd:req
327+
#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
328+
9998: mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register
329+
#ifdef CONFIG_CPU_V6
330+
ALT_UP_B(.L0_\@)
331+
.subsection 1
332+
.L0_\@: ldr_va \rd, __current
333+
b .L1_\@
334+
.previous
335+
.L1_\@:
336+
#endif
337+
#else
338+
ldr_va \rd, __current
339+
#endif
340+
.endm
341+
342+
/*
343+
* reload_current - reload the task pointer of this CPU's current task
344+
* into the TLS register
345+
*/
346+
.macro reload_current, t1:req, t2:req
347+
#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
348+
#ifdef CONFIG_CPU_V6
349+
ALT_SMP(nop)
350+
ALT_UP_B(.L0_\@)
351+
#endif
352+
ldr_this_cpu \t1, __entry_task, \t1, \t2
353+
mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO
354+
.L0_\@:
355+
#endif
356+
.endm
357+
333358
/*
334359
* Instruction barrier
335360
*/

arch/arm/include/asm/current.h

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,18 @@
88
#define _ASM_ARM_CURRENT_H
99

1010
#ifndef __ASSEMBLY__
11+
#include <asm/insn.h>
1112

1213
struct task_struct;
1314

14-
static inline void set_current(struct task_struct *cur)
15-
{
16-
if (!IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO))
17-
return;
18-
19-
/* Set TPIDRURO */
20-
asm("mcr p15, 0, %0, c13, c0, 3" :: "r"(cur) : "memory");
21-
}
15+
extern struct task_struct *__current;
2216

23-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
24-
25-
static inline struct task_struct *get_current(void)
17+
static inline __attribute_const__ struct task_struct *get_current(void)
2618
{
2719
struct task_struct *cur;
2820

2921
#if __has_builtin(__builtin_thread_pointer) && \
22+
defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) && \
3023
!(defined(CONFIG_THUMB2_KERNEL) && \
3124
defined(CONFIG_CC_IS_CLANG) && CONFIG_CLANG_VERSION < 130001)
3225
/*
@@ -39,16 +32,30 @@ static inline struct task_struct *get_current(void)
3932
* https://github.com/ClangBuiltLinux/linux/issues/1485
4033
*/
4134
cur = __builtin_thread_pointer();
35+
#elif defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
36+
asm("0: mrc p15, 0, %0, c13, c0, 3 \n\t"
37+
#ifdef CONFIG_CPU_V6
38+
"1: \n\t"
39+
" .subsection 1 \n\t"
40+
"2: " LOAD_SYM_ARMV6(%0, __current) " \n\t"
41+
" b 1b \n\t"
42+
" .previous \n\t"
43+
" .pushsection \".alt.smp.init\", \"a\" \n\t"
44+
" .long 0b - . \n\t"
45+
" b . + (2b - 0b) \n\t"
46+
" .popsection \n\t"
47+
#endif
48+
: "=r"(cur));
49+
#elif __LINUX_ARM_ARCH__>=7 || \
50+
(defined(MODULE) && defined(CONFIG_ARM_MODULE_PLTS))
51+
cur = __current;
4252
#else
43-
asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(cur));
53+
asm(LOAD_SYM_ARMV6(%0, __current) : "=r"(cur));
4454
#endif
4555
return cur;
4656
}
4757

4858
#define current get_current()
49-
#else
50-
#include <asm-generic/current.h>
51-
#endif /* CONFIG_CURRENT_POINTER_IN_TPIDRURO */
5259

5360
#endif /* __ASSEMBLY__ */
5461

arch/arm/include/asm/switch_to.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ static inline void set_ti_cpu(struct task_struct *p)
4040
do { \
4141
__complete_pending_tlbi(); \
4242
set_ti_cpu(next); \
43-
if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO)) \
43+
if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || \
44+
IS_ENABLED(CONFIG_SMP)) \
4445
__this_cpu_write(__entry_task, next); \
4546
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \
4647
} while (0)

arch/arm/include/asm/thread_info.h

Lines changed: 0 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -62,9 +62,6 @@ struct cpu_context_save {
6262
struct thread_info {
6363
unsigned long flags; /* low level flags */
6464
int preempt_count; /* 0 => preemptable, <0 => bug */
65-
#ifndef CONFIG_THREAD_INFO_IN_TASK
66-
struct task_struct *task; /* main task structure */
67-
#endif
6865
__u32 cpu; /* cpu */
6966
__u32 cpu_domain; /* cpu domain */
7067
struct cpu_context_save cpu_context; /* cpu context */
@@ -80,39 +77,15 @@ struct thread_info {
8077

8178
#define INIT_THREAD_INFO(tsk) \
8279
{ \
83-
INIT_THREAD_INFO_TASK(tsk) \
8480
.flags = 0, \
8581
.preempt_count = INIT_PREEMPT_COUNT, \
8682
}
8783

88-
#ifdef CONFIG_THREAD_INFO_IN_TASK
89-
#define INIT_THREAD_INFO_TASK(tsk)
90-
9184
static inline struct task_struct *thread_task(struct thread_info* ti)
9285
{
9386
return (struct task_struct *)ti;
9487
}
9588

96-
#else
97-
#define INIT_THREAD_INFO_TASK(tsk) .task = &(tsk),
98-
99-
static inline struct task_struct *thread_task(struct thread_info* ti)
100-
{
101-
return ti->task;
102-
}
103-
104-
/*
105-
* how to get the thread information struct from C
106-
*/
107-
static inline struct thread_info *current_thread_info(void) __attribute_const__;
108-
109-
static inline struct thread_info *current_thread_info(void)
110-
{
111-
return (struct thread_info *)
112-
(current_stack_pointer & ~(THREAD_SIZE - 1));
113-
}
114-
#endif
115-
11689
#define thread_saved_pc(tsk) \
11790
((unsigned long)(task_thread_info(tsk)->cpu_context.pc))
11891
#define thread_saved_sp(tsk) \

arch/arm/kernel/asm-offsets.c

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ int main(void)
4343
BLANK();
4444
DEFINE(TI_FLAGS, offsetof(struct thread_info, flags));
4545
DEFINE(TI_PREEMPT, offsetof(struct thread_info, preempt_count));
46-
#ifndef CONFIG_THREAD_INFO_IN_TASK
47-
DEFINE(TI_TASK, offsetof(struct thread_info, task));
48-
#endif
4946
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
5047
DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
5148
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));

arch/arm/kernel/entry-armv.S

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -807,12 +807,13 @@ ENTRY(__switch_to)
807807
switch_tls r1, r4, r5, r3, r7
808808
#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP) && \
809809
!defined(CONFIG_STACKPROTECTOR_PER_TASK)
810-
ldr r9, [r2, #TI_TASK]
811810
ldr r8, =__stack_chk_guard
812811
.if (TSK_STACK_CANARY > IMM12_MASK)
813-
add r9, r9, #TSK_STACK_CANARY & ~IMM12_MASK
814-
.endif
812+
add r9, r2, #TSK_STACK_CANARY & ~IMM12_MASK
815813
ldr r9, [r9, #TSK_STACK_CANARY & IMM12_MASK]
814+
.else
815+
ldr r9, [r2, #TSK_STACK_CANARY & IMM12_MASK]
816+
.endif
816817
#endif
817818
mov r7, r2 @ Preserve 'next'
818819
#ifdef CONFIG_CPU_USE_DOMAINS
@@ -829,7 +830,7 @@ ENTRY(__switch_to)
829830
#endif
830831
mov r0, r5
831832
#if !defined(CONFIG_THUMB2_KERNEL) && !defined(CONFIG_VMAP_STACK)
832-
set_current r7
833+
set_current r7, r8
833834
ldmia r4, {r4 - sl, fp, sp, pc} @ Load all regs saved previously
834835
#else
835836
mov r1, r7
@@ -851,7 +852,7 @@ ENTRY(__switch_to)
851852
@ switches us to another stack, with few other side effects. In order
852853
@ to prevent this distinction from causing any inconsistencies, let's
853854
@ keep the 'set_current' call as close as we can to the update of SP.
854-
set_current r1
855+
set_current r1, r2
855856
mov sp, ip
856857
ret lr
857858
#endif

arch/arm/kernel/entry-v7m.S

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -97,15 +97,17 @@ ENTRY(__switch_to)
9797
str sp, [ip], #4
9898
str lr, [ip], #4
9999
mov r5, r0
100+
mov r6, r2 @ Preserve 'next'
100101
add r4, r2, #TI_CPU_SAVE
101102
ldr r0, =thread_notify_head
102103
mov r1, #THREAD_NOTIFY_SWITCH
103104
bl atomic_notifier_call_chain
104-
mov ip, r4
105105
mov r0, r5
106-
ldmia ip!, {r4 - r11} @ Load all regs saved previously
107-
ldr sp, [ip]
108-
ldr pc, [ip, #4]!
106+
mov r1, r6
107+
ldmia r4, {r4 - r12, lr} @ Load all regs saved previously
108+
set_current r1, r2
109+
mov sp, ip
110+
bx lr
109111
.fnend
110112
ENDPROC(__switch_to)
111113

arch/arm/kernel/head-common.S

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,8 @@ __mmap_switched:
105105
mov r1, #0
106106
bl __memset @ clear .bss
107107

108-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
109108
adr_l r0, init_task @ get swapper task_struct
110-
set_current r0
111-
#endif
109+
set_current r0, r1
112110

113111
ldmia r4, {r0, r1, r2, r3}
114112
str r9, [r0] @ Save processor ID

arch/arm/kernel/process.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636

3737
#include "signal.h"
3838

39-
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
39+
#if defined(CONFIG_CURRENT_POINTER_IN_TPIDRURO) || defined(CONFIG_SMP)
4040
DEFINE_PER_CPU(struct task_struct *, __entry_task);
4141
#endif
4242

@@ -46,6 +46,11 @@ unsigned long __stack_chk_guard __read_mostly;
4646
EXPORT_SYMBOL(__stack_chk_guard);
4747
#endif
4848

49+
#ifndef CONFIG_CURRENT_POINTER_IN_TPIDRURO
50+
asmlinkage struct task_struct *__current;
51+
EXPORT_SYMBOL(__current);
52+
#endif
53+
4954
static const char *processor_modes[] __maybe_unused = {
5055
"USER_26", "FIQ_26" , "IRQ_26" , "SVC_26" , "UK4_26" , "UK5_26" , "UK6_26" , "UK7_26" ,
5156
"UK8_26" , "UK9_26" , "UK10_26", "UK11_26", "UK12_26", "UK13_26", "UK14_26", "UK15_26",

0 commit comments

Comments
 (0)