diff --git a/kernel-src/arch/x86-64/smp.c b/kernel-src/arch/x86-64/smp.c index 4f7c82e4..6e1acdad 100644 --- a/kernel-src/arch/x86-64/smp.c +++ b/kernel-src/arch/x86-64/smp.c @@ -11,7 +11,7 @@ static volatile struct limine_smp_request smprequest = { }; // set to 1 because of the bsp -size_t cpusawake = 1; +size_t arch_smp_cpusawake = 1; static void cpuwakeup(struct limine_smp_info *info) { cpu_set((cpu_t *)info->extra_argument); @@ -20,13 +20,14 @@ static void cpuwakeup(struct limine_smp_info *info) { arch_idt_reload(); dpc_init(); arch_mmu_apswitch(); + vmm_apinit(); cpu_initstate(); arch_apic_timerinit(); - __atomic_add_fetch(&cpusawake, 1, __ATOMIC_SEQ_CST); - // TODO let other processors run threads and applications - for (;;) CPU_HALT(); + __atomic_add_fetch(&arch_smp_cpusawake, 1, __ATOMIC_SEQ_CST); + + sched_apentry(); } // if the nosmp argument is given to the kernel, this is what the APs will jump to @@ -34,6 +35,10 @@ static void cpuwakeuphalt(struct limine_smp_info *info) { for (;;) CPU_HALT(); } +void arch_smp_sendipi(cpu_t *targcpu, isr_t *isr, int target) { + arch_apic_sendipi(targcpu ? targcpu->id : 0, INTERRUPT_IDTOVECTOR(isr->id), target, 0, 1); +} + void arch_smp_wakeup() { struct limine_smp_response *response = smprequest.response; if (response == NULL) { @@ -64,7 +69,7 @@ void arch_smp_wakeup() { // wait for other cpus to boot up if (wakeupfn == cpuwakeup) - while (__atomic_load_n(&cpusawake, __ATOMIC_SEQ_CST) != response->cpu_count) asm("pause"); + while (__atomic_load_n(&arch_smp_cpusawake, __ATOMIC_SEQ_CST) != response->cpu_count) asm("pause"); printf("smp: awoke other processors\n"); } diff --git a/kernel-src/include/kernel/pgcache.h b/kernel-src/include/kernel/pgcache.h new file mode 100644 index 00000000..00fc7c76 --- /dev/null +++ b/kernel-src/include/kernel/pgcache.h @@ -0,0 +1,8 @@ +#ifndef _PGCACHE_H +#define _PGCACHE_H + +void pgcache_init(); +int pgcache_getpage(vnode_t *node, uintmax_t offset, void **ret); +int pgcache_setdirty(void *addr); + +#endif diff --git a/kernel-src/include/kernel/scheduler.h b/kernel-src/include/kernel/scheduler.h index 088f6ce6..d5c12a0e 100644 --- a/kernel-src/include/kernel/scheduler.h +++ b/kernel-src/include/kernel/scheduler.h @@ -11,6 +11,7 @@ #define SCHED_THREAD_FLAGS_RUNNING 2 #define SCHED_THREAD_FLAGS_SLEEP 4 #define SCHED_THREAD_FLAGS_INTERRUPTIBLE 8 +#define SCHED_THREAD_FLAGS_PREEMPTED 16 #define SCHED_PROC_STATE_NORMAL 0 #define SCHED_PROC_STATE_ZOMBIE 1 @@ -91,5 +92,6 @@ void sched_destroyproc(proc_t *); void sched_destroythread(thread_t *); void sched_targetcpu(struct cpu_t *cpu); void sched_sleepus(size_t us); +void sched_apentry(); #endif diff --git a/kernel-src/include/x86-64/arch/smp.h b/kernel-src/include/x86-64/arch/smp.h index 8b75b9c5..904ba9cd 100644 --- a/kernel-src/include/x86-64/arch/smp.h +++ b/kernel-src/include/x86-64/arch/smp.h @@ -1,6 +1,14 @@ #ifndef _SMP_H #define _SMP_H +#define ARCH_SMP_IPI_TARGET 0 +#define ARCH_SMP_IPI_SELF 1 +#define ARCH_SMP_IPI_ALL 2 +#define ARCH_SMP_IPI_OTHERCPUS 3 + +extern size_t arch_smp_cpusawake; + void arch_smp_wakeup(); +void arch_smp_sendipi(cpu_t *targcpu, isr_t *isr, int target); #endif diff --git a/kernel-src/lib/panic.c b/kernel-src/lib/panic.c index 9ab03226..9072df9e 100644 --- a/kernel-src/lib/panic.c +++ b/kernel-src/lib/panic.c @@ -4,7 +4,7 @@ #include __attribute__((noreturn)) void _panic(char *msg, context_t *ctx) { - printf("Oops.\n"); + printf("cpu%lu: Oops.\n", _cpu()->id); if (msg) printf("%s\n", msg); diff --git a/kernel-src/mm/vmm.c b/kernel-src/mm/vmm.c index 188bfc27..2e3f9560 100644 --- a/kernel-src/mm/vmm.c +++ b/kernel-src/mm/vmm.c @@ -635,3 +635,7 @@ void vmm_init() { printspace(&kernelspace); } + +void vmm_apinit() { + vmm_switchcontext(&vmm_kernelctx); +} diff --git a/kernel-src/sys/scheduler.c b/kernel-src/sys/scheduler.c index f1a02744..30dfd9df 100644 --- a/kernel-src/sys/scheduler.c +++ b/kernel-src/sys/scheduler.c @@ -12,7 +12,7 @@ #include #define QUANTUM_US 100000 -#define SCHEDULER_STACK_SIZE 4096 +#define SCHEDULER_STACK_SIZE PAGE_SIZE static scache_t *threadcache; static scache_t *processcache; @@ -151,7 +151,6 @@ static thread_t *runqueuenext(int minprio) { runqueuebitmap &= ~((uint64_t)1 << i); } - if (thread) thread->flags &= ~SCHED_THREAD_FLAGS_QUEUED; @@ -173,6 +172,14 @@ static __attribute__((noreturn)) void switchthread(thread_t *thread) { thread->cpu = _cpu(); if (current) current->flags &= ~SCHED_THREAD_FLAGS_RUNNING; + + __assert((thread->flags & SCHED_THREAD_FLAGS_RUNNING) == 0); + + if (current && current->flags & SCHED_THREAD_FLAGS_SLEEP) + spinlock_release(¤t->sleeplock); + + thread->flags |= SCHED_THREAD_FLAGS_RUNNING; + ARCH_CONTEXT_SWITCHTHREAD(thread); __builtin_unreachable(); } @@ -258,9 +265,7 @@ static void yield(context_t *context) { if (next || sleeping) { ARCH_CONTEXT_THREADSAVE(thread, context); - if (sleeping) - spinlock_release(&thread->sleeplock); - else + if (sleeping == false) runqueueinsert(thread); if (next == NULL) @@ -348,32 +353,44 @@ void sched_setroot(vnode_t *new) { setnodeslock(&proc->root, new); } -static void timerhook(context_t *context, dpcarg_t arg) { - thread_t* current = _cpu()->thread; - - interrupt_set(false); +// once a scheduler dpc gets run, the return context is set to this function using the scheduler stack +static void dopreempt() { + // interrupts are disabled, the thread context is already saved spinlock_acquire(&runqueuelock); + thread_t *current = _cpu()->thread; thread_t *next = runqueuenext(current->priority); + current->flags &= ~SCHED_THREAD_FLAGS_PREEMPTED; if (next) { current->flags &= ~SCHED_THREAD_FLAGS_RUNNING; - ARCH_CONTEXT_THREADSAVE(current, context); - runqueueinsert(current); - - ARCH_CONTEXT_THREADLOAD(next, context); - _cpu()->thread = next; - next->flags |= SCHED_THREAD_FLAGS_RUNNING; - next->cpu = _cpu(); - if (next->vmmctx != current->vmmctx) - vmm_switchcontext(next->vmmctx); + } else { + next = current; } spinlock_release(&runqueuelock); + switchthread(next); +} + +static void timerhook(context_t *context, dpcarg_t arg) { + thread_t* current = _cpu()->thread; + interrupt_set(false); + + // no need to preempt it again + if (current->flags & SCHED_THREAD_FLAGS_PREEMPTED) + return; + + current->flags |= SCHED_THREAD_FLAGS_PREEMPTED; + ARCH_CONTEXT_THREADSAVE(current, context); + + CTX_INIT(context, false); + CTX_SP(context) = (uintptr_t)_cpu()->schedulerstack; + CTX_IP(context) = (uintptr_t)dopreempt; } static void cpuidlethread() { + sched_targetcpu(_cpu()); interrupt_set(true); while (1) { CPU_HALT(); @@ -400,13 +417,26 @@ void sched_sleepus(size_t us) { sched_yield(); } +void sched_apentry() { + _cpu()->schedulerstack = vmm_map(NULL, SCHEDULER_STACK_SIZE, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL); + __assert(_cpu()->schedulerstack); + _cpu()->schedulerstack = (void *)((uintptr_t)_cpu()->schedulerstack + SCHEDULER_STACK_SIZE); + + _cpu()->idlethread = sched_newthread(cpuidlethread, PAGE_SIZE * 4, 3, NULL, NULL); + __assert(_cpu()->idlethread); + + timer_insert(_cpu()->timer, &_cpu()->schedtimerentry, timerhook, NULL, QUANTUM_US, true); + timer_resume(_cpu()->timer); + sched_stopcurrentthread(); +} + void sched_init() { threadcache = slab_newcache(sizeof(thread_t), 0, NULL, NULL); __assert(threadcache); processcache = slab_newcache(sizeof(proc_t), 0, NULL, NULL); __assert(processcache); - _cpu()->schedulerstack = alloc(SCHEDULER_STACK_SIZE); + _cpu()->schedulerstack = vmm_map(NULL, SCHEDULER_STACK_SIZE, VMM_FLAGS_ALLOCATE, ARCH_MMU_FLAGS_READ | ARCH_MMU_FLAGS_WRITE | ARCH_MMU_FLAGS_NOEXEC, NULL); __assert(_cpu()->schedulerstack); _cpu()->schedulerstack = (void *)((uintptr_t)_cpu()->schedulerstack + SCHEDULER_STACK_SIZE);