Skip to content

Commit 13a695a

Browse files
author
Russell King (Oracle)
committed
Merge tag 'pr-arm32-ti-in-task' of git://git.kernel.org/pub/scm/linux/kernel/git/ardb/linux into devel-stable
ARM: support THREAD_INFO_IN_TASK Move thread_info off the stack and into the task struct, as is done by many other architectures. This requires a method to find the task struct of the task currently running on the CPU, which is provided in this case by the user space TLS (Thread Local Storage) register. This implies that the feature is only supported on CPUs that implement this register, i.e., ARM v6k or later. Kindly tested by Amit and reviewed by Linus. The first patch is against the GCC plugins subsystem, but was reviewed by the maintainer and can be taken through the ARM tree.
2 parents 6880fa6 + 18ed1c0 commit 13a695a

20 files changed

+174
-50
lines changed

arch/arm/Kconfig

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ config ARM
125125
select PERF_USE_VMALLOC
126126
select RTC_LIB
127127
select SYS_SUPPORTS_APM_EMULATION
128+
select THREAD_INFO_IN_TASK if CURRENT_POINTER_IN_TPIDRURO
128129
select TRACE_IRQFLAGS_SUPPORT if !CPU_V7M
129130
# Above selects are sorted alphabetically; please add new ones
130131
# according to that. Thanks.
@@ -1157,6 +1158,11 @@ config SMP_ON_UP
11571158

11581159
If you don't know what to do here, say Y.
11591160

1161+
1162+
config CURRENT_POINTER_IN_TPIDRURO
1163+
def_bool y
1164+
depends on SMP && CPU_32v6K && !CPU_V6
1165+
11601166
config ARM_CPU_TOPOLOGY
11611167
bool "Support cpu topology definition"
11621168
depends on SMP && CPU_V7
@@ -1600,7 +1606,7 @@ config XEN
16001606

16011607
config STACKPROTECTOR_PER_TASK
16021608
bool "Use a unique stack canary value for each task"
1603-
depends on GCC_PLUGINS && STACKPROTECTOR && SMP && !XIP_DEFLATED_DATA
1609+
depends on GCC_PLUGINS && STACKPROTECTOR && THREAD_INFO_IN_TASK && !XIP_DEFLATED_DATA
16041610
select GCC_PLUGIN_ARM_SSP_PER_TASK
16051611
default y
16061612
help

arch/arm/Makefile

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,10 @@ ifeq ($(CONFIG_CC_IS_CLANG),y)
113113
CFLAGS_ABI += -meabi gnu
114114
endif
115115

116+
ifeq ($(CONFIG_CURRENT_POINTER_IN_TPIDRURO),y)
117+
CFLAGS_ABI += -mtp=cp15
118+
endif
119+
116120
# Accept old syntax despite ".syntax unified"
117121
AFLAGS_NOWARN :=$(call as-option,-Wa$(comma)-mno-warn-deprecated,-Wa$(comma)-W)
118122

@@ -273,11 +277,8 @@ ifeq ($(CONFIG_STACKPROTECTOR_PER_TASK),y)
273277
prepare: stack_protector_prepare
274278
stack_protector_prepare: prepare0
275279
$(eval SSP_PLUGIN_CFLAGS := \
276-
-fplugin-arg-arm_ssp_per_task_plugin-tso=$(shell \
277-
awk '{if ($$2 == "THREAD_SZ_ORDER") print $$3;}'\
278-
include/generated/asm-offsets.h) \
279280
-fplugin-arg-arm_ssp_per_task_plugin-offset=$(shell \
280-
awk '{if ($$2 == "TI_STACK_CANARY") print $$3;}'\
281+
awk '{if ($$2 == "TSK_STACK_CANARY") print $$3;}'\
281282
include/generated/asm-offsets.h))
282283
$(eval KBUILD_CFLAGS += $(SSP_PLUGIN_CFLAGS))
283284
$(eval GCC_PLUGINS_CFLAGS += $(SSP_PLUGIN_CFLAGS))

arch/arm/include/asm/assembler.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,14 +199,43 @@
199199
.endm
200200
.endr
201201

202+
.macro get_current, rd
203+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
204+
mrc p15, 0, \rd, c13, c0, 3 @ get TPIDRURO register
205+
#else
206+
get_thread_info \rd
207+
ldr \rd, [\rd, #TI_TASK]
208+
#endif
209+
.endm
210+
211+
.macro set_current, rn
212+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
213+
mcr p15, 0, \rn, c13, c0, 3 @ set TPIDRURO register
214+
#endif
215+
.endm
216+
217+
.macro reload_current, t1:req, t2:req
218+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
219+
adr_l \t1, __entry_task @ get __entry_task base address
220+
mrc p15, 0, \t2, c13, c0, 4 @ get per-CPU offset
221+
ldr \t1, [\t1, \t2] @ load variable
222+
mcr p15, 0, \t1, c13, c0, 3 @ store in TPIDRURO
223+
#endif
224+
.endm
225+
202226
/*
203227
* Get current thread_info.
204228
*/
205229
.macro get_thread_info, rd
230+
#ifdef CONFIG_THREAD_INFO_IN_TASK
231+
/* thread_info is the first member of struct task_struct */
232+
get_current \rd
233+
#else
206234
ARM( mov \rd, sp, lsr #THREAD_SIZE_ORDER + PAGE_SHIFT )
207235
THUMB( mov \rd, sp )
208236
THUMB( lsr \rd, \rd, #THREAD_SIZE_ORDER + PAGE_SHIFT )
209237
mov \rd, \rd, lsl #THREAD_SIZE_ORDER + PAGE_SHIFT
238+
#endif
210239
.endm
211240

212241
/*

arch/arm/include/asm/current.h

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/* SPDX-License-Identifier: GPL-2.0-only */
2+
/*
3+
* Copyright (c) 2021 Keith Packard <[email protected]>
4+
* Copyright (c) 2021 Google, LLC <[email protected]>
5+
*/
6+
7+
#ifndef _ASM_ARM_CURRENT_H
8+
#define _ASM_ARM_CURRENT_H
9+
10+
#ifndef __ASSEMBLY__
11+
12+
struct task_struct;
13+
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+
}
22+
23+
#ifdef CONFIG_CURRENT_POINTER_IN_TPIDRURO
24+
25+
static inline struct task_struct *get_current(void)
26+
{
27+
struct task_struct *cur;
28+
29+
#if __has_builtin(__builtin_thread_pointer)
30+
/*
31+
* Use the __builtin helper when available - this results in better
32+
* code, especially when using GCC in combination with the per-task
33+
* stack protector, as the compiler will recognize that it needs to
34+
* load the TLS register only once in every function.
35+
*/
36+
cur = __builtin_thread_pointer();
37+
#else
38+
asm("mrc p15, 0, %0, c13, c0, 3" : "=r"(cur));
39+
#endif
40+
return cur;
41+
}
42+
43+
#define current get_current()
44+
#else
45+
#include <asm-generic/current.h>
46+
#endif /* CONFIG_CURRENT_POINTER_IN_TPIDRURO */
47+
48+
#endif /* __ASSEMBLY__ */
49+
50+
#endif /* _ASM_ARM_CURRENT_H */

arch/arm/include/asm/smp.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ extern void set_smp_ipi_range(int ipi_base, int nr_ipi);
4848
* Called from platform specific assembly code, this is the
4949
* secondary CPU entry point.
5050
*/
51-
asmlinkage void secondary_start_kernel(void);
51+
asmlinkage void secondary_start_kernel(struct task_struct *task);
5252

5353

5454
/*
@@ -61,6 +61,7 @@ struct secondary_data {
6161
};
6262
unsigned long swapper_pg_dir;
6363
void *stack;
64+
struct task_struct *task;
6465
};
6566
extern struct secondary_data secondary_data;
6667
extern void secondary_startup(void);

arch/arm/include/asm/stackprotector.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ static __always_inline void boot_init_stack_canary(void)
3939
current->stack_canary = canary;
4040
#ifndef CONFIG_STACKPROTECTOR_PER_TASK
4141
__stack_chk_guard = current->stack_canary;
42-
#else
43-
current_thread_info()->stack_canary = current->stack_canary;
4442
#endif
4543
}
4644

arch/arm/include/asm/switch_to.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,25 @@
2323
*/
2424
extern struct task_struct *__switch_to(struct task_struct *, struct thread_info *, struct thread_info *);
2525

26+
static inline void set_ti_cpu(struct task_struct *p)
27+
{
28+
#ifdef CONFIG_THREAD_INFO_IN_TASK
29+
/*
30+
* The core code no longer maintains the thread_info::cpu field once
31+
* CONFIG_THREAD_INFO_IN_TASK is in effect, but we rely on it for
32+
* raw_smp_processor_id(), which cannot access struct task_struct*
33+
* directly for reasons of circular #inclusion hell.
34+
*/
35+
task_thread_info(p)->cpu = task_cpu(p);
36+
#endif
37+
}
38+
2639
#define switch_to(prev,next,last) \
2740
do { \
2841
__complete_pending_tlbi(); \
42+
set_ti_cpu(next); \
43+
if (IS_ENABLED(CONFIG_CURRENT_POINTER_IN_TPIDRURO)) \
44+
__this_cpu_write(__entry_task, next); \
2945
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); \
3046
} while (0)
3147

arch/arm/include/asm/thread_info.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929

3030
struct task_struct;
3131

32+
DECLARE_PER_CPU(struct task_struct *, __entry_task);
33+
3234
#include <asm/types.h>
3335

3436
struct cpu_context_save {
@@ -52,12 +54,11 @@ struct cpu_context_save {
5254
struct thread_info {
5355
unsigned long flags; /* low level flags */
5456
int preempt_count; /* 0 => preemptable, <0 => bug */
57+
#ifndef CONFIG_THREAD_INFO_IN_TASK
5558
struct task_struct *task; /* main task structure */
59+
#endif
5660
__u32 cpu; /* cpu */
5761
__u32 cpu_domain; /* cpu domain */
58-
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
59-
unsigned long stack_canary;
60-
#endif
6162
struct cpu_context_save cpu_context; /* cpu context */
6263
__u32 abi_syscall; /* ABI type and syscall nr */
6364
__u8 used_cp[16]; /* thread used copro */
@@ -71,11 +72,16 @@ struct thread_info {
7172

7273
#define INIT_THREAD_INFO(tsk) \
7374
{ \
74-
.task = &tsk, \
75+
INIT_THREAD_INFO_TASK(tsk) \
7576
.flags = 0, \
7677
.preempt_count = INIT_PREEMPT_COUNT, \
7778
}
7879

80+
#ifdef CONFIG_THREAD_INFO_IN_TASK
81+
#define INIT_THREAD_INFO_TASK(tsk)
82+
#else
83+
#define INIT_THREAD_INFO_TASK(tsk) .task = &(tsk),
84+
7985
/*
8086
* how to get the thread information struct from C
8187
*/
@@ -86,6 +92,7 @@ static inline struct thread_info *current_thread_info(void)
8692
return (struct thread_info *)
8793
(current_stack_pointer & ~(THREAD_SIZE - 1));
8894
}
95+
#endif
8996

9097
#define thread_saved_pc(tsk) \
9198
((unsigned long)(task_thread_info(tsk)->cpu_context.pc))

arch/arm/include/asm/tls.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
.macro switch_tls_v6k, base, tp, tpuser, tmp1, tmp2
1414
mrc p15, 0, \tmp2, c13, c0, 2 @ get the user r/w register
15-
mcr p15, 0, \tp, c13, c0, 3 @ set TLS register
16-
mcr p15, 0, \tpuser, c13, c0, 2 @ and the user r/w register
15+
@ TLS register update is deferred until return to user space
16+
mcr p15, 0, \tpuser, c13, c0, 2 @ set the user r/w register
1717
str \tmp2, [\base, #TI_TP_VALUE + 4] @ save it
1818
.endm
1919

@@ -38,18 +38,22 @@
3838
#ifdef CONFIG_TLS_REG_EMUL
3939
#define tls_emu 1
4040
#define has_tls_reg 1
41+
#define defer_tls_reg_update 0
4142
#define switch_tls switch_tls_none
4243
#elif defined(CONFIG_CPU_V6)
4344
#define tls_emu 0
4445
#define has_tls_reg (elf_hwcap & HWCAP_TLS)
46+
#define defer_tls_reg_update 0
4547
#define switch_tls switch_tls_v6
4648
#elif defined(CONFIG_CPU_32v6K)
4749
#define tls_emu 0
4850
#define has_tls_reg 1
51+
#define defer_tls_reg_update 1
4952
#define switch_tls switch_tls_v6k
5053
#else
5154
#define tls_emu 0
5255
#define has_tls_reg 0
56+
#define defer_tls_reg_update 0
5357
#define switch_tls switch_tls_software
5458
#endif
5559

@@ -77,7 +81,7 @@ static inline void set_tls(unsigned long val)
7781
*/
7882
barrier();
7983

80-
if (!tls_emu) {
84+
if (!tls_emu && !defer_tls_reg_update) {
8185
if (has_tls_reg) {
8286
asm("mcr p15, 0, %0, c13, c0, 3"
8387
: : "r" (val));

arch/arm/kernel/asm-offsets.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ 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
4647
DEFINE(TI_TASK, offsetof(struct thread_info, task));
48+
#endif
4749
DEFINE(TI_CPU, offsetof(struct thread_info, cpu));
4850
DEFINE(TI_CPU_DOMAIN, offsetof(struct thread_info, cpu_domain));
4951
DEFINE(TI_CPU_SAVE, offsetof(struct thread_info, cpu_context));
@@ -63,10 +65,6 @@ int main(void)
6365
#ifdef CONFIG_IWMMXT
6466
DEFINE(TI_IWMMXT_STATE, offsetof(struct thread_info, fpstate.iwmmxt));
6567
#endif
66-
#ifdef CONFIG_STACKPROTECTOR_PER_TASK
67-
DEFINE(TI_STACK_CANARY, offsetof(struct thread_info, stack_canary));
68-
#endif
69-
DEFINE(THREAD_SZ_ORDER, THREAD_SIZE_ORDER);
7068
BLANK();
7169
DEFINE(S_R0, offsetof(struct pt_regs, ARM_r0));
7270
DEFINE(S_R1, offsetof(struct pt_regs, ARM_r1));

0 commit comments

Comments
 (0)