Commit 9c46929e authored by Ard Biesheuvel's avatar Ard Biesheuvel
Browse files

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: default avatarLinus Walleij <linus.walleij@linaro.org>
Acked-by: default avatarNicolas Pitre <nico@fluxnic.net>
Signed-off-by: default avatarArd Biesheuvel <ardb@kernel.org>
Tested-by: default avatarMarc Zyngier <maz@kernel.org>
Tested-by: Vladimir Murzin <vladimir.murzin@arm.com> # ARMv7M
parent c2755910
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -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
+54 −29
Original line number Diff line number Diff line
@@ -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
 */
+22 −15
Original line number Diff line number Diff line
@@ -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__ */

+2 −1
Original line number Diff line number Diff line
@@ -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)
+0 −27
Original line number Diff line number Diff line
@@ -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)	\
Loading