Merge remote-tracking branch 'origin/ctx_switch' into x64_new

This commit is contained in:
Stefan Lankes 2012-05-17 14:45:34 +02:00
commit b39a84e07f
6 changed files with 226 additions and 6 deletions

View file

@ -413,6 +413,69 @@ static int pi(void* arg)
return 0;
}
#define REPS 10000
volatile uint64_t t1, t2;
volatile int stop = !!0;
volatile int sid = 0;
static int measure_ctx_switch(void* arg)
{
int id = !!(int)arg;
int oid = !id;
uint64_t freq = get_cpu_frequency() *1000 *1000;
uint64_t diff, min = (uint64_t)-1, max = 0, avg = 0;
int i;
uint32_t a=0,b,c,d;
// Size of a timeslice in ticks
uint64_t timeslice = freq / TIMER_FREQ;
kprintf("ID: %d, ", id);
#ifdef SW_TASK_SWITCH
kprintf("Measuring SW task switch.\n");
#else
kprintf("Measuring HW task switch.\n");
#endif
for (i=0; i < REPS && stop == 0; i++) {
while(id == sid && stop == 0) {
t2 = rdtsc();
cpuid(0,&a,&b,&c,&d);
}
cpuid(0,&a,&b,&c,&d);
diff = rdtsc() -t2;
// The last measurement is garbage
if (stop) break;
// The first ones are garbage, too
if (i < 5) goto next_try;
if (diff >= timeslice) {
i--;
goto next_try;
}
kprintf("%i: diff= %llu, i= %i\n", id, diff, i);
if (diff > max) max = diff;
if (diff < min) min = diff;
avg += diff;
next_try:
sid = id;
}
avg /= i-5;
stop = 1;
kprintf("maximum gap: %llu ticks\n", max);
kprintf("minimum gap: %llu ticks\n", min);
kprintf("average gap: %llu ticks\n", avg);
kprintf("Timeslice size: %llu ticks\n", timeslice);
return 0;
}
int test_init(void)
{
// char* argv[] = {"/bin/mshell", NULL};
@ -424,8 +487,11 @@ int test_init(void)
//sem_init(&consuming, 0);
//mailbox_int32_init(&mbox);
create_kernel_task(NULL, foo, "Hello from foo1", NORMAL_PRIO);
create_kernel_task(NULL, join_test, NULL, NORMAL_PRIO);
create_kernel_task(NULL, measure_ctx_switch, (int)0, NORMAL_PRIO);
create_kernel_task(NULL, measure_ctx_switch, (int)1, NORMAL_PRIO);
//create_kernel_task(NULL, foo, "Hello from foo1", NORMAL_PRIO);
//create_kernel_task(NULL, foo, "Hello from foo2", NORMAL_PRIO);
//create_kernel_task(NULL, join_test, NULL, NORMAL_PRIO);
//create_kernel_task(NULL, producer, , NORMAL_PRIO);
//create_kernel_task(NULL, consumer, NULL, NORMAL_PRIO);
//create_kernel_task(NULL, mail_ping, NULL, NORMAL_PRIO);
@ -436,7 +502,7 @@ int test_init(void)
//create_kernel_task(NULL, laplace, NULL, NORMAL_PRIO);
//create_kernel_task(NULL, jacobi, NULL, NORMAL_PRIO);
//create_user_task(NULL, "/bin/hello", argv);
create_user_task(NULL, "/bin/tests", argv);
//create_user_task(NULL, "/bin/tests", argv);
//create_user_task(NULL, "/bin/jacobi", argv);
//create_user_task(NULL, "/bin/mshell", argv);
//create_user_task(NULL, "/bin/jacobi", argv);

View file

@ -507,6 +507,40 @@ hack:
jmp 0x00 : 0xDEADBEAF
ret
; This procedure is used by scheduler() to switch tasks.
; It is the software-equivalent to the hw-procedure switch_task from above.
; Call it in C with the following arguments:
; sw_switch_context(&old_tasks_stack_pointer, &new_tasks_stack_pointer)
global sw_switch_context
sw_switch_context:
; The stack layout looks like this:
; [new stack pointer]
; [old stack pointer]
;pushf ; [this procedure's return address] overwritten by: EFLAGS (*1)
push DWORD 0x8 ; CS
push DWORD [esp+4] ; EIP
push DWORD 0 ; Interrupt number
push DWORD 0xc0edbabe ; Error code
pusha ; Registers...
; ---- This will be popped off by iret later.
pushf
pop eax
mov [esp+48], eax ; Move EFLAGS to position (*1) by overwriting
; the return address of sw_switch_context()
mov ecx, [esp+52]
mov [ecx], esp ; Save stack position in old task structure
mov ecx, [esp+56]
mov esp, [ecx] ; Load new stack
sw_rollback:
popa
add esp, 8
iret
; 32: IRQ0
irq0:
; irq0 - irq15 are registered as "Interrupt Gate"

View file

@ -27,7 +27,11 @@
#include <asm/page.h>
gdt_ptr_t gp;
#ifdef SW_TASK_SWITCH
static tss_t task_state_segments[MAX_CORES] __attribute__ ((aligned (PAGE_SIZE)));
#else
static tss_t task_state_segments[MAX_TASKS] __attribute__ ((aligned (PAGE_SIZE)));
#endif
static unsigned char kstacks[MAX_TASKS][KERNEL_STACK_SIZE] __attribute__ ((aligned (PAGE_SIZE))) = {[0 ... MAX_TASKS-1][0 ... KERNEL_STACK_SIZE-1] = 0xCD};
uint32_t default_stack_pointer = (uint32_t) kstacks[0] + KERNEL_STACK_SIZE - sizeof(size_t);
// currently, our kernel has full access to the ioports
@ -71,6 +75,7 @@ int register_task(task_t* task) {
int arch_fork(task_t* task)
{
#ifndef SW_TASK_SWITCH
uint16_t cs = 0x08;
uint16_t ds = 0x10;
uint32_t id;
@ -120,6 +125,7 @@ int arch_fork(task_t* task)
asm volatile ("pushf; pop %%eax" : "=a"(task_state_segments[id].eflags));
// This will be the entry point for the new task.
asm volatile ("call read_eip" : "=a"(task_state_segments[id].eip));
#endif
return 0;
}
@ -130,10 +136,60 @@ int create_default_frame(task_t* task, internal_entry_point_t ep, void* arg)
uint16_t ds = 0x10;
uint32_t id;
#ifdef SW_TASK_SWITCH
uint32_t *stack;
struct state *stptr;
uint32_t short_state_size = sizeof(struct state)/sizeof(uint32_t) -2;
#endif
if (BUILTIN_EXPECT(!task, 0))
return -EINVAL;
id = task->id;
#ifdef SW_TASK_SWITCH
memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE);
/* The difference between setting up a task for SW-task-switching
* and not for HW-task-switching is setting up a stack and not a TSS.
* This is the stack which will be activated and popped off for iret later.
*/
stack = kstacks[id] +KERNEL_STACK_SIZE -sizeof(uint32_t);
/* The next three things on the stack are a marker for debugging purposes, ... */
*stack-- = 0xDEADBEEF;
/* the first-function-to-be-called's arguments, ... */
*stack-- = arg;
/* and the "caller" we shall return to.
* This procedure cleans the task after exit. */
*stack = leave_kernel_task;
/* Next bunch on the stack is the initial register state.
* The stack must look like the stack of a task which was
* scheduled away previously. */
/* short_state_size was introduced because the convenient "struct state"
* is used for filling the stack with initial values. But the problem is that
* "iret" will not remove the last two entries from the stack, since we're
* "returning" from kernel space to kernel space. Therefore it is shortened
* by its last two entries. */
stack -= short_state_size;
stptr = stack;
memset(stptr, 0x00, short_state_size*sizeof(uint32_t));
stptr->esp = stack +short_state_size;
stptr->int_no = 0xB16B00B5;
stptr->error = 0xC03DB4B3;
/* The instruction pointer shall be set on the first function to be called
* after IRETing */
stptr->eip = ep;
stptr->cs = cs;
stptr->eflags = 0x1002;
/* Set the task's stack pointer entry to the stack we have crafted right now.
* This is the pointer which will be used by sw_switch_task(old_task, new_task) later.*/
task->stack = stack;
#else
/* reset buffers */
memset(task_state_segments+id, 0x00, sizeof(tss_t));
memset(kstacks[id], 0xCD, KERNEL_STACK_SIZE);
@ -161,9 +217,48 @@ int create_default_frame(task_t* task, internal_entry_point_t ep, void* arg)
/* setup for the kernel stack frame */
task_state_segments[id].ss0 = 0x10;
task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
#endif
return 0;
}
#ifdef SW_TASK_SWITCH
int create_default_tss(int id)
{
uint16_t cs = 0x08;
uint16_t ds = 0x10;
/* reset buffers */
memset(task_state_segments+id, 0x00, sizeof(tss_t));
/* set default values of all registers */
task_state_segments[id].cs = cs;
task_state_segments[id].ss = ds;
task_state_segments[id].ds = ds;
task_state_segments[id].fs = ds;
task_state_segments[id].gs = ds;
task_state_segments[id].es = ds;
task_state_segments[id].eflags = 0x1002; // 0x1202;
//task_state_segments[id].cr3 = (uint32_t) (virt_to_phys((size_t)task->pgd));
//task_state_segments[id].eip = (uint32_t) ep;
task_state_segments[id].esp = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
/* build default stack frame */
*((size_t*)task_state_segments[id].esp) = 0xDEADBEAF; /* dead-end */
/*
task_state_segments[id].ebp = task_state_segments[id].esp;
task_state_segments[id].esp -= sizeof(size_t);
*((size_t*)task_state_segments[id].esp) = (size_t) arg;
task_state_segments[id].esp -= sizeof(size_t);
*((size_t*)task_state_segments[id].esp) = (size_t) leave_kernel_task;
*/
/* setup for the kernel stack frame */
task_state_segments[id].ss0 = 0x10;
task_state_segments[id].esp0 = (uint32_t) kstacks[id] + KERNEL_STACK_SIZE - sizeof(size_t);
return 0;
}
#endif
/* Setup a descriptor in the Global Descriptor Table */
static void gdt_set_gate(int num, unsigned long base, unsigned long limit,
@ -203,7 +298,11 @@ void gdt_install(void)
{
unsigned int i;
#ifdef SW_TASK_SWITCH
memset(task_state_segments, 0x00, MAX_CORES*sizeof(tss_t));
#else
memset(task_state_segments, 0x00, MAX_TASKS*sizeof(tss_t));
#endif
/* Setup the GDT pointer and limit */
gp.limit = (sizeof(gdt_entry_t) * GDT_ENTRIES) - 1;
@ -247,7 +346,12 @@ void gdt_install(void)
/*
* Create TSS for each task at ring0 (we use these segments for task switching)
*/
#ifdef SW_TASK_SWITCH
for(i=0; i<MAX_CORES; i++) {
create_default_tss(i);
#else
for(i=0; i<MAX_TASKS; i++) {
#endif
gdt_set_gate(5+i, (unsigned long) (task_state_segments+i), sizeof(tss_t)-1,
GDT_FLAG_PRESENT | GDT_FLAG_TSS | GDT_FLAG_RING0,
GDT_FLAG_32_BIT);

View file

@ -42,6 +42,8 @@ extern "C" {
#define VIDEO_MEM_ADDR 0xB8000 // the video memora address
#define SMP_SETUP_ADDR 0x07000
#define SW_TASK_SWITCH
#define BYTE_ORDER LITTLE_ENDIAN
/*

View file

@ -67,6 +67,9 @@ struct page_dir;
/** @brief The task_t structure */
typedef struct task {
#ifdef SW_TASK_SWITCH
uint32_t stack;
#endif
/// Task id = position in the task table
tid_t id;
/// Task status (INVALID, READY, RUNNING, ...)

View file

@ -46,9 +46,15 @@
*
* A task's id will be its position in this array.
*/
#ifdef SW_TASK_SWITCH
static task_t task_table[MAX_TASKS] = { \
[0] = {0, 0, TASK_IDLE, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}, \
[1 ... MAX_TASKS-1] = {0, 0, TASK_INVALID, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}};
#else
static task_t task_table[MAX_TASKS] = { \
[0] = {0, TASK_IDLE, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}, \
[1 ... MAX_TASKS-1] = {0, TASK_INVALID, 0, 0, 0, NULL, NULL, 0, ATOMIC_INIT(0), SPINLOCK_INIT, NULL, SPINLOCK_INIT, NULL, NULL, 0, 0, 0, 0}};
#endif
static spinlock_irqsave_t table_lock = SPINLOCK_IRQSAVE_INIT;
#if MAX_CORES > 1
@ -1372,16 +1378,21 @@ get_task_out:
orig_task->flags &= ~TASK_FPU_USED;
}
//kprintf("schedule from %u to %u with prio %u on core %u\n",
// orig_task->id, curr_task->id, (uint32_t)curr_task->prio, CORE_ID);
//kprintf("schedule from %u to %u with prio %u on core %u\n", orig_task->id, curr_task->id, (uint32_t)curr_task->prio, CORE_ID);
#ifndef SW_TASK_SWITCH
switch_task(curr_task->id);
#endif
finish_task_switch(0);
#ifdef SW_TASK_SWITCH
write_cr3(virt_to_phys((size_t)curr_task->pgd));
sw_switch_context(&orig_task->stack, &curr_task->stack);
#endif
}
}
void reschedule(void)
{
uint32_t flags = irq_nested_disable();
scheduler();
scheduler();
irq_nested_enable(flags);
}