kernel/x86_64: improve context switch implementation

The goal of this patch is to amortize the cost of context switch by making
the compiler aware that context switch clobbers all registers. Because all
register need to be saved anyway there is no additional cost of using
callee saved register in the function that does the context switch.
This commit is contained in:
Pawel Dziepak
2014-05-06 21:15:55 +02:00
parent 9db5b975f9
commit 88e8e24c84
5 changed files with 62 additions and 54 deletions
+32
View File
@@ -0,0 +1,32 @@
/*
* Copyright 2014, Paweł Dziepak, [email protected].
* Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_X86_64_CPU_H
#define _KERNEL_ARCH_X86_64_CPU_H
#include <arch_thread_types.h>
static inline void
x86_context_switch(arch_thread* oldState, arch_thread* newState)
{
asm volatile(
"pushq %%rbp;"
"movq $1f, %c[rip](%0);"
"movq %%rsp, %c[rsp](%0);"
"movq %c[rsp](%1), %%rsp;"
"jmp *%c[rip](%1);"
"1:"
"popq %%rbp;"
:
: "a" (oldState), "d" (newState),
[rsp] "i" (offsetof(arch_thread, current_stack)),
[rip] "i" (offsetof(arch_thread, instruction_pointer))
: "rbx", "rcx", "rdi", "rsi", "r8", "r9", "r10", "r11", "r12", "r13",
"r14", "r15", "memory");
}
#endif // _KERNEL_ARCH_X86_64_CPU_H
+6 -5
View File
@@ -13,12 +13,13 @@
#ifndef _ASSEMBLER
#include <module.h>
#include <arch_thread_types.h>
#include <arch/x86/descriptors.h>
#ifdef __x86_64__
# include <arch/x86/64/iframe.h>
#else
# include <arch/x86/32/iframe.h>
# include <arch/x86/64/cpu.h>
#endif
#endif // !_ASSEMBLER
@@ -452,8 +453,6 @@ void __x86_setup_system_time(uint32 conversionFactor,
uint32 conversionFactorNsecs, bool conversionFactorNsecsShift);
#endif
void x86_context_switch(struct arch_thread* oldState,
struct arch_thread* newState);
void x86_userspace_thread_exit(void);
void x86_end_userspace_thread_exit(void);
void x86_swap_pgdir(addr_t newPageDir);
@@ -482,6 +481,8 @@ void x86_hardware_interrupt(iframe* frame);
void x86_page_fault_exception(iframe* iframe);
#ifndef __x86_64__
void x86_context_switch(struct arch_thread* oldState,
struct arch_thread* newState);
void x86_fnsave(void* fpuState);
void x86_frstor(const void* fpuState);
@@ -9,7 +9,13 @@
#define _KERNEL_ARCH_x86_THREAD_TYPES_H
#include <arch_cpu.h>
#include <SupportDefs.h>
#ifdef __x86_64__
# include <arch/x86/64/iframe.h>
#else
# include <arch/x86/32/iframe.h>
#endif
namespace BKernel {
@@ -40,7 +46,8 @@ struct arch_thread {
uint64* syscall_rsp;
uint64* user_rsp;
uint64* current_stack;
uintptr_t* current_stack;
uintptr_t instruction_pointer;
#else
struct farcall current_stack;
struct farcall interrupt_stack;
@@ -72,7 +79,7 @@ struct arch_fork_arg {
inline addr_t
arch_thread::GetFramePointer() const
{
return current_stack[1];
return current_stack[0];
}
+9 -29
View File
@@ -79,38 +79,18 @@ FUNCTION_END(x86_write_msr)
/* void x86_64_thread_entry(); */
FUNCTION(x86_64_thread_entry):
movq %r15, %rdi
jmp *%r14
xorq %rbp, %rbp
movq %rsp, %rax
addq $16, %rsp
andq $0xfffffffffffffff0, %rsp
subq $8, %rsp
movq 8(%rax), %rdi
jmp *(%rax)
FUNCTION_END(x86_64_thread_entry)
/* void x86_context_switch(struct arch_thread* oldState,
struct arch_thread* newState); */
FUNCTION(x86_context_switch):
// Just need to save callee-save registers: RBP, RBX, R12-15.
push %r15
push %r14
push %r13
push %r12
push %rbp
push %rbx
// Swap the stack pointers.
movq %rsp, ARCH_THREAD_current_stack(%rdi)
movq ARCH_THREAD_current_stack(%rsi), %rsp
// Restore callee-save registers.
pop %rbx
pop %rbp
pop %r12
pop %r13
pop %r14
pop %r15
ret
FUNCTION_END(x86_context_switch)
/* void x86_swap_pgdir(uint64 newPageDir); */
FUNCTION(x86_swap_pgdir):
movq %rdi, %cr3
+5 -17
View File
@@ -165,7 +165,7 @@ void
arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop,
void (*function)(void*), const void* data)
{
addr_t* stackTop = (addr_t*)_stackTop;
uintptr_t* stackTop = static_cast<uintptr_t*>(_stackTop);
TRACE("arch_thread_init_kthread_stack: stack top %p, function %p, data: "
"%p\n", _stackTop, function, data);
@@ -173,23 +173,11 @@ arch_thread_init_kthread_stack(Thread* thread, void* _stack, void* _stackTop,
// Save the stack top for system call entry.
thread->arch_info.syscall_rsp = (uint64*)thread->kernel_stack_top;
// x86_64 uses registers for argument passing, first argument in RDI,
// however we don't save RDI on every context switch (there is no need
// for us to: it is not callee-save, and only contains the first argument
// to x86_context_switch). However, this presents a problem since we
// cannot store the argument for the entry function here. Therefore, we
// save the function address in R14 and the argument in R15 (which are
// restored), and then set up the stack to initially call a wrapper
// function which passes the argument correctly.
thread->arch_info.instruction_pointer
= reinterpret_cast<uintptr_t>(x86_64_thread_entry);
*--stackTop = 0; // Dummy return address.
*--stackTop = (addr_t)x86_64_thread_entry; // Wrapper function.
*--stackTop = (addr_t)data; // R15: argument.
*--stackTop = (addr_t)function; // R14: entry function.
*--stackTop = 0; // R13.
*--stackTop = 0; // R12.
*--stackTop = 0; // RBP.
*--stackTop = 0; // RBX.
*--stackTop = uintptr_t(data);
*--stackTop = uintptr_t(function);
// Save the stack position.
thread->arch_info.current_stack = stackTop;