diff options
author | Brian Gerst <brgerst@gmail.com> | 2016-08-13 12:38:19 -0400 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-08-24 06:31:41 -0400 |
commit | 0100301bfdf56a2a370c7157b5ab0fbf9313e1cd (patch) | |
tree | f8a339f20fb486a1ebe029d1c409e94c9bf8d67a /arch/x86/kernel | |
parent | 7b32aeadbc95d4a41402c1c0da6aa3ab51af4c10 (diff) |
sched/x86: Rewrite the switch_to() code
Move the low-level context switch code to an out-of-line asm stub instead of
using complex inline asm. This allows constructing a new stack frame for the
child process to make it seamlessly flow to ret_from_fork without an extra
test and branch in __switch_to(). It also improves code generation for
__schedule() by using the C calling convention instead of clobbering all
registers.
Signed-off-by: Brian Gerst <brgerst@gmail.com>
Reviewed-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/1471106302-10159-5-git-send-email-brgerst@gmail.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Diffstat (limited to 'arch/x86/kernel')
-rw-r--r-- | arch/x86/kernel/asm-offsets.c | 6 | ||||
-rw-r--r-- | arch/x86/kernel/asm-offsets_32.c | 5 | ||||
-rw-r--r-- | arch/x86/kernel/asm-offsets_64.c | 5 | ||||
-rw-r--r-- | arch/x86/kernel/process_32.c | 9 | ||||
-rw-r--r-- | arch/x86/kernel/process_64.c | 9 | ||||
-rw-r--r-- | arch/x86/kernel/smpboot.c | 1 |
6 files changed, 29 insertions, 6 deletions
diff --git a/arch/x86/kernel/asm-offsets.c b/arch/x86/kernel/asm-offsets.c index 2bd5c6ff7ee7..db3a0af9b9ec 100644 --- a/arch/x86/kernel/asm-offsets.c +++ b/arch/x86/kernel/asm-offsets.c | |||
@@ -29,6 +29,12 @@ | |||
29 | 29 | ||
30 | void common(void) { | 30 | void common(void) { |
31 | BLANK(); | 31 | BLANK(); |
32 | OFFSET(TASK_threadsp, task_struct, thread.sp); | ||
33 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
34 | OFFSET(TASK_stack_canary, task_struct, stack_canary); | ||
35 | #endif | ||
36 | |||
37 | BLANK(); | ||
32 | OFFSET(TI_flags, thread_info, flags); | 38 | OFFSET(TI_flags, thread_info, flags); |
33 | OFFSET(TI_status, thread_info, status); | 39 | OFFSET(TI_status, thread_info, status); |
34 | 40 | ||
diff --git a/arch/x86/kernel/asm-offsets_32.c b/arch/x86/kernel/asm-offsets_32.c index ecdc1d217dc0..880aa093268d 100644 --- a/arch/x86/kernel/asm-offsets_32.c +++ b/arch/x86/kernel/asm-offsets_32.c | |||
@@ -57,6 +57,11 @@ void foo(void) | |||
57 | /* Size of SYSENTER_stack */ | 57 | /* Size of SYSENTER_stack */ |
58 | DEFINE(SIZEOF_SYSENTER_stack, sizeof(((struct tss_struct *)0)->SYSENTER_stack)); | 58 | DEFINE(SIZEOF_SYSENTER_stack, sizeof(((struct tss_struct *)0)->SYSENTER_stack)); |
59 | 59 | ||
60 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
61 | BLANK(); | ||
62 | OFFSET(stack_canary_offset, stack_canary, canary); | ||
63 | #endif | ||
64 | |||
60 | #if defined(CONFIG_LGUEST) || defined(CONFIG_LGUEST_GUEST) || defined(CONFIG_LGUEST_MODULE) | 65 | #if defined(CONFIG_LGUEST) || defined(CONFIG_LGUEST_GUEST) || defined(CONFIG_LGUEST_MODULE) |
61 | BLANK(); | 66 | BLANK(); |
62 | OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); | 67 | OFFSET(LGUEST_DATA_irq_enabled, lguest_data, irq_enabled); |
diff --git a/arch/x86/kernel/asm-offsets_64.c b/arch/x86/kernel/asm-offsets_64.c index d875f97d4e0b..210927ee2e74 100644 --- a/arch/x86/kernel/asm-offsets_64.c +++ b/arch/x86/kernel/asm-offsets_64.c | |||
@@ -56,6 +56,11 @@ int main(void) | |||
56 | OFFSET(TSS_sp0, tss_struct, x86_tss.sp0); | 56 | OFFSET(TSS_sp0, tss_struct, x86_tss.sp0); |
57 | BLANK(); | 57 | BLANK(); |
58 | 58 | ||
59 | #ifdef CONFIG_CC_STACKPROTECTOR | ||
60 | DEFINE(stack_canary_offset, offsetof(union irq_stack_union, stack_canary)); | ||
61 | BLANK(); | ||
62 | #endif | ||
63 | |||
59 | DEFINE(__NR_syscall_max, sizeof(syscalls_64) - 1); | 64 | DEFINE(__NR_syscall_max, sizeof(syscalls_64) - 1); |
60 | DEFINE(NR_syscalls, sizeof(syscalls_64)); | 65 | DEFINE(NR_syscalls, sizeof(syscalls_64)); |
61 | 66 | ||
diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c index d86be29c38c7..4bedbc08e53c 100644 --- a/arch/x86/kernel/process_32.c +++ b/arch/x86/kernel/process_32.c | |||
@@ -133,17 +133,20 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, | |||
133 | unsigned long arg, struct task_struct *p, unsigned long tls) | 133 | unsigned long arg, struct task_struct *p, unsigned long tls) |
134 | { | 134 | { |
135 | struct pt_regs *childregs = task_pt_regs(p); | 135 | struct pt_regs *childregs = task_pt_regs(p); |
136 | struct fork_frame *fork_frame = container_of(childregs, struct fork_frame, regs); | ||
137 | struct inactive_task_frame *frame = &fork_frame->frame; | ||
136 | struct task_struct *tsk; | 138 | struct task_struct *tsk; |
137 | int err; | 139 | int err; |
138 | 140 | ||
139 | p->thread.sp = (unsigned long) childregs; | 141 | frame->bp = 0; |
142 | p->thread.sp = (unsigned long) fork_frame; | ||
140 | p->thread.sp0 = (unsigned long) (childregs+1); | 143 | p->thread.sp0 = (unsigned long) (childregs+1); |
141 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); | 144 | memset(p->thread.ptrace_bps, 0, sizeof(p->thread.ptrace_bps)); |
142 | 145 | ||
143 | if (unlikely(p->flags & PF_KTHREAD)) { | 146 | if (unlikely(p->flags & PF_KTHREAD)) { |
144 | /* kernel thread */ | 147 | /* kernel thread */ |
145 | memset(childregs, 0, sizeof(struct pt_regs)); | 148 | memset(childregs, 0, sizeof(struct pt_regs)); |
146 | p->thread.ip = (unsigned long) ret_from_kernel_thread; | 149 | frame->ret_addr = (unsigned long) ret_from_kernel_thread; |
147 | task_user_gs(p) = __KERNEL_STACK_CANARY; | 150 | task_user_gs(p) = __KERNEL_STACK_CANARY; |
148 | childregs->ds = __USER_DS; | 151 | childregs->ds = __USER_DS; |
149 | childregs->es = __USER_DS; | 152 | childregs->es = __USER_DS; |
@@ -161,7 +164,7 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, | |||
161 | if (sp) | 164 | if (sp) |
162 | childregs->sp = sp; | 165 | childregs->sp = sp; |
163 | 166 | ||
164 | p->thread.ip = (unsigned long) ret_from_fork; | 167 | frame->ret_addr = (unsigned long) ret_from_fork; |
165 | task_user_gs(p) = get_user_gs(current_pt_regs()); | 168 | task_user_gs(p) = get_user_gs(current_pt_regs()); |
166 | 169 | ||
167 | p->thread.io_bitmap_ptr = NULL; | 170 | p->thread.io_bitmap_ptr = NULL; |
diff --git a/arch/x86/kernel/process_64.c b/arch/x86/kernel/process_64.c index 63236d8f84bf..827eeed03e16 100644 --- a/arch/x86/kernel/process_64.c +++ b/arch/x86/kernel/process_64.c | |||
@@ -141,12 +141,17 @@ int copy_thread_tls(unsigned long clone_flags, unsigned long sp, | |||
141 | { | 141 | { |
142 | int err; | 142 | int err; |
143 | struct pt_regs *childregs; | 143 | struct pt_regs *childregs; |
144 | struct fork_frame *fork_frame; | ||
145 | struct inactive_task_frame *frame; | ||
144 | struct task_struct *me = current; | 146 | struct task_struct *me = current; |
145 | 147 | ||
146 | p->thread.sp0 = (unsigned long)task_stack_page(p) + THREAD_SIZE; | 148 | p->thread.sp0 = (unsigned long)task_stack_page(p) + THREAD_SIZE; |
147 | childregs = task_pt_regs(p); | 149 | childregs = task_pt_regs(p); |
148 | p->thread.sp = (unsigned long) childregs; | 150 | fork_frame = container_of(childregs, struct fork_frame, regs); |
149 | set_tsk_thread_flag(p, TIF_FORK); | 151 | frame = &fork_frame->frame; |
152 | frame->bp = 0; | ||
153 | frame->ret_addr = (unsigned long) ret_from_fork; | ||
154 | p->thread.sp = (unsigned long) fork_frame; | ||
150 | p->thread.io_bitmap_ptr = NULL; | 155 | p->thread.io_bitmap_ptr = NULL; |
151 | 156 | ||
152 | savesegment(gs, p->thread.gsindex); | 157 | savesegment(gs, p->thread.gsindex); |
diff --git a/arch/x86/kernel/smpboot.c b/arch/x86/kernel/smpboot.c index c85d2c636092..7e52f83d3a4b 100644 --- a/arch/x86/kernel/smpboot.c +++ b/arch/x86/kernel/smpboot.c | |||
@@ -942,7 +942,6 @@ void common_cpu_up(unsigned int cpu, struct task_struct *idle) | |||
942 | per_cpu(cpu_current_top_of_stack, cpu) = | 942 | per_cpu(cpu_current_top_of_stack, cpu) = |
943 | (unsigned long)task_stack_page(idle) + THREAD_SIZE; | 943 | (unsigned long)task_stack_page(idle) + THREAD_SIZE; |
944 | #else | 944 | #else |
945 | clear_tsk_thread_flag(idle, TIF_FORK); | ||
946 | initial_gs = per_cpu_offset(cpu); | 945 | initial_gs = per_cpu_offset(cpu); |
947 | #endif | 946 | #endif |
948 | } | 947 | } |