Commit Graph
113 Commits
Author SHA1 Message Date
Bananymous 17813f3fcf Kernel: Store SchedulerThreadNode directly in Thread
Having the pointer indirection was weird and forced allocations when
adding/binding a new thread :P
2026-08-23 14:21:27 +03:00
Bananymous 2f25bfb2db Kernel: Use min heap for scheduler block list
This makes insertion to block list O(log n) instead of O(n) when
blocking with wake time. This is more heavy and currently slower than
the previous implementation, but that is just because we don't really
have too many threads running at any given time. This will be more
scalable in the future and even allows implementing priorities if I ever
wish so
2026-08-19 21:32:36 +03:00
Bananymous bf0bd19289 Kernel: Refactor includes to reduce include dependencies
Now modifying Scheduler.h or Thread.h doesnt trigger practically a full
kernel rebuild. This required moving Mutex and RWLock of of line but
that should be fine :^)
2026-08-19 21:32:36 +03:00
Bananymous 0d390e2246 Kernel: Send EOI after handling the interrupt
There is no need to send it prior to handling. This required us to not
yield from HPET/PIT irq so that the EOI actually gets sent
2026-07-20 10:43:50 +03:00
Bananymous a9cf0134b7 Kernel: Don't block thread if the wake time has already passed 2026-07-20 06:58:30 +03:00
Bananymous 2ddee7016e Kernel: Fix another scheduler bug :^) 2026-07-20 06:58:30 +03:00
Bananymous 5329ec5912 Kernel: Rework spinlock usage when blocking the current thread
The old SpinLockAsMutex was pretty confusing first of all. It also was
causing the issue thats been around for maybe a year now which is the
only consistently happening kernel panic. I've been pretty confused
about this and finally figured out what was causing this.

The main issue was that we were accidentally enabling interrupts when
blocking a thread that passed SpinLockAsMutex from normally interrupt
enabled context. This led to receiving IPI for thread unblock while
we were actively blocking the thread. I'm very suprized this had't
caused any more serious issues than occasional kernel panics :^)
2026-07-20 04:07:23 +03:00
Bananymous edc95e1c09 Kernel: Disable SMP message handling while blocking a thread
There were some race conditions on a thread getting unblocked before
being moved to the block queue
2026-07-20 04:07:23 +03:00
Bananymous 0843b8989d Kernel: Fix timer deadline issues in scheduler
Don't wake up threads when getting the next timer deadline.
If we are idling this can: remove thread from block list, get the
deadline for the next blocked thread, return to idle thread. This will
end up sleeping until the next wake up condition which in the worst
case would be rebalance, or indefinitely when not running SMP.

Make sure we always set the next timer deadline. I was not doing that if
the timer interrupt did not lead to a yield
2026-07-07 10:30:07 +03:00
Bananymous 49662aa994 Kernel: Don't use peridic interrupts for scheduling
Scheduler will now set a timer interrupt deadline for when it should get
interrupted next. This fixes a big issue I've had for a long time where
sleeping on an idle core would have to wait for the next periodic
interrupt to wake up. This could cause 1 ms sleeps to be close to 10 ms.

This currently only supports lapic timers and will still fallback to
periodic interrupts when running with PIC. I should add deadline support
to PIT/HPET but why would you run with APIC disabled ;)
2026-07-07 04:53:41 +03:00
Bananymous 71649ffe09 Kernel Rework ThreadBlocker
I don't know why I though the block chain had to be stored fully in the
ThreadBlocker, that did not even fix the problem I was trying to fix
when I last rewrote it. Roll back to doubly linked list of block chain
and now just check that the node is contained within the ThreadBlocker
before removing and after acquiring the ThreadBlocker's lock. Also there
is no need to have a separate lock the node's blocker field. We can just
perform an atomic reads and writes to it. We can still get a blocker
that the node is no longer part of, but this can be resolved with a
simple check. This patch reduces ThreadBlocker's size from over 200
bytes to just 12 bytes +4 bytes padding
2026-04-20 12:50:09 +03:00
Bananymous 9084d9305c Kernel: Change preemption condition
Instead of keeping track of the current time and rescheduling when
interval has passed, keep track of the next expected reschedule time.
This prevents theoretically missing every second pre-emption when
scheduler's timer is interrupting at same rate as the interval.
2026-04-11 03:30:52 +03:00
Bananymous 4af9699b22 Kernel: Only save/load sse state when it is used
There is no need to save and load sse state on every interrupt. Instead
we can use CR0.TS to make threads trigger an interrupt when they use sse
instructions. This can be used to only save and load sse state when
needed.

Processor now keeps track of its current "sse thread" and the scheduler
either enabled or disabled sse based on which thread it is starting up.
When a thread dies, it checks if it was the current sse thread to avoid
use after free bugs. When load balancing, processor has to save the
thread's sse state before sending it to a new processor (if it was the
current sse thread). This ensures thread's sse state will be correct
when the new processor ends up loading it.
2026-01-11 03:06:39 +02:00
Bananymous 35c97e2ff8 Kernel: optimize yielding
Doing a yield no longer raises a software interrupt. Instead it just
saves all the callee saved registers, ip, sp and return value. Because
yield is only called in the kernel, it can just restore registers and
jump to the target address. There is never a need to use iret :)
2026-01-11 01:31:09 +02:00
Bananymous 1f03d23dae Kernel: Fix load balancing
My code to find least loaded processor used processor index instead of
processor id to index the array. Most of the time this lead to wrong
processor returned as the least loaded, leaving some processors
basically idle.
2026-01-10 01:46:08 +02:00
Bananymous 2eea074473 Kernel: Remove unnecessary page table loads
loading a page table is slow as it invalidates the whole tlb
2026-01-10 01:13:48 +02:00
Bananymous a9ceab0415 Kernel: Use syscall/sysret for syscalls in x86_64 2026-01-09 15:18:58 +02:00
Bananymous 9eb3834ae5 Kernel: Add syscall-less clock_gettime
If the processor has invariant TSC it can be used to measure time. We
keep track of the last nanosecond and TSC values and offset them based
on the current TSC. This allows getting current time in userspace.

The implementation maps a single RO page to every processes' address
space. The page contains the TSC info which gets updated every 100 ms.
If the processor does not have invariant TSC, this page will not
indicate the capability for TSC based timing.

There was the problem about how does a processor know which cpu it is
running without doing syscall. TSC counters may or may not be
synchronized between cores, so we need a separate TSC info for each
processor. I ended up adding sequence of bytes 0..255 at the start of
the shared page. When a scheduler gets a new thread, it updates the
threads gs/fs segment to point to the byte corresponding to the current
cpu.

This TSC based timing is also used in kernel. With 64 bit HPET this
probably does not bring much of a benefit, but on PIT or 32 bit HPET
this removes the need to aquire a spinlock to get the current time.

This change does force the userspace to not use gs/fs themselves and
they are both now reserved. Other one is used for TLS (this can be
technically used if user does not call libc code) and the other for
the current processor index (cannot be used as kernel unconditionally
resets it after each load balance).

I was looking at how many times timer's current time was polled
(userspace and kernel combined). When idling in window manager, it was
around 8k times/s. When running doom it peaked at over 1 million times
per second when loading and settled at ~30k times/s.
2026-01-08 17:13:59 +02:00
Bananymous 706c0816dd Kernel: Move smp_initialized flag after schedulers are initialized
Before this real hardware failed to boot with smp enabled. Allocating
the idle thread does a page mapping which ends up broadcasting TLB
shootdown to other processes. This ends up failing somewhere halting the
processors never allowing them to initialize their scheduler
2026-01-03 23:39:07 +02:00
Bananymous 31a1968798 Kernel: Cleanup scheduling while idling
If there are no threads in the run queue and we are idle, attempt to
wake up threads from the sleep queue
2025-12-31 19:28:55 +02:00
Bananymous 89c0ff1a9d Kernel/LibC: Replace SYS_{GET,SET}_TLS with SYS_{SET,GET}_{FS,GS}BASE
This allows userspace to use both registers
2025-11-13 04:20:53 +02:00
Bananymous 8a0269d29e Kernel: Remove kernel processes
Kernel can just use raw threads, pretty muchs the only thing that
process provides is syscalls which kernel threads of course don't
need.

Also this makes init process have pid 1 :D
2025-07-02 01:54:03 +03:00
Bananymous 92e4078287 Kernel: Rewrite ThreadBlocker
This gets rid of a very old bug where kernel panics when thread is being
woken up and unblocked at the same time on different cores. This
required adding a new lock to SchedulerQueue::Node and adding a cap to
how many threads a threadblocker can simultaneously block. I don't think
I ever block more than five threads on the same ThreadBlocker so this
should be fine.
2025-07-02 00:17:42 +03:00
Bananymous 83c0ef3514 Kernel: Implement CLOCK_{PROCESS,THREAD}_CPUTIME_ID 2025-06-28 16:55:13 +03:00
Bananymous 6d3d41abe2 Kernel: Fix possible division by zero in scheduler 2025-06-28 16:55:13 +03:00
Bananymous eecdad50a6 Kernel: Fix most of mutex + block race conditions
All block functions now take an optional mutex parameter that is
atomically unlocked instead of having the user unlock it before hand.
This prevents a ton of race conditions everywhere in the code!
2025-06-06 03:59:22 +03:00
Bananymous 35b3c3c98c Kernel: Fix scheduler load balancing
I was not updating the current queue of the thread which lead to blocked
threads think they were in run queue
2025-04-23 08:09:51 +03:00
Bananymous 4bcd3ed86f Kernel: Start working on TLS, add SYS_{SET,GET}_TLS 2025-04-15 23:31:17 +03:00
Bananymous f0e55938c1 Kernel: Allow initial binding of threads to specific processor 2024-09-24 16:27:40 +03:00
Bananymous 23d6205659 Kernel: Move DEBUG_* macros to centralized Debug.h
This makes toggling debug info much easier
2024-09-20 11:00:07 +03:00
Bananymous 72f8138ca1 Kernel: Disable scheduler load balancing until I get it fixed
Scheduler keeps crashing all the time when running on multiple cores.
This patch disabled the load balancer, which seems to get rid of most
scheduler crashes.
2024-08-25 15:37:17 +03:00
Bananymous 09a527fb12 Kernel: Fix scheduler thread counter
I was actually never incrementing the counter :D
2024-08-10 18:18:25 +03:00
Bananymous 0a7c316ffb Kernel: Fix race condition when blocking threads
If thread was blocked, but had not reached block queue, you might
already get an unblock request which would fail on an assertion.

If blocked thread was load balanced to another processor and unblocked
simultaneously, there was a race condition.
2024-08-05 20:14:13 +03:00
Bananymous 4599e1dec5 Kernel: Make schedler not crash when unblocking active thread
This was happening when sending signals to processes that were currently
running.
2024-08-01 18:21:49 +03:00
Bananymous bb1738db8c Kernel: Make thread unblocking O(1)
This is still bit broken. VirtualBox seems to freeze sometimes, but I
could not recreate this on qemu (with and without kvm) or real hardware.
2024-07-24 00:31:01 +03:00
Bananymous 539afb329a Kernel: All processors use LAPIC timer when running with APIC
This makes scheduler preemption much cleaner as bsb does not have to
send smp messages to notify other processes about timer interrupt.

Also PIT percision is now "full" 0.8 us instead of 1 ms that I was using
before.
2024-07-23 02:28:52 +03:00
Bananymous f8261c60c0 Kernel: Rewrite the whole scheduler and re-architecture SMP handling
Change Semaphore -> ThreadBlocker
  This was not a semaphore, I just named it one because I didn't know
  what semaphore was. I have meant to change this sooner, but it was in
  no way urgent :D

Implement SMP events. Processors can now be sent SMP events through
IPIs. SMP events can be sent either to a single processor or broadcasted
to every processor.

PageTable::{map_page,map_range,unmap_page,unmap_range}() now send SMP
event to invalidate TLB caches for the changed pages.

Scheduler no longer uses a global run queue. Each processor has its own
scheduler that keeps track of the load on the processor. Once every
second schedulers do load balancing. Schedulers have no access to other
processors' schedulers, they just see approximate loads. If scheduler
decides that it has too much load, it will send a thread to another
processor through a SMP event.

Schedulers are currently run using the timer interrupt on BSB. This
should be not the case, and each processor should use its LAPIC timer
for interrupts. There is no reason to broadcast SMP event to all
processors when BSB gets timer interrupt.

Old scheduler only achieved 20% idle load on qemu. That was probably a
very inefficient implementation. This new scheduler seems to average
around 1% idle load. This is much closer to what I would expect. On my
own laptop idle load seems to be only around 0.5% on each processor.
2024-07-22 00:33:50 +03:00
Bananymous 84b3289a2a Kernel: Move Scheduler::yield() lock check after interrupts disabled
I have no idea why this solves a bug where current processor has
scheduler lock at the beginning of yield.
2024-05-31 02:56:39 +03:00
Bananymous f8c01418b1 Kernel: Fix multiprocessing on x86_64
I did not even start APs after initializing them... :D
2024-04-03 14:21:55 +03:00
Bananymous 9e073e9fa0 Kernel: Add offset for interrupt stack in Scheduler::yield()
This allows accessing (garbage) sp and ss in interrupt stack.
2024-04-03 00:45:22 +03:00
Bananymous fe386fa819 Kernel: Implement thread start trampoline for userspace
This is needed on i686 to set segment registers.
2024-04-03 00:42:39 +03:00
Bananymous 2106a9e373 Kernel: Rework scheduler/processor stacks. 2024-04-02 12:34:42 +03:00
Bananymous 5050047cef Kernel: Rewrite whole scheduler
Current context saving was very hacky and dependant on compiler
behaviour that was not consistent. Now we always use iret for
context saving. This makes everything more clean.
2024-03-29 18:02:12 +02:00
Bananymous 1b65f850ee Kernel: Rename thread stacks to more appropriate names 2024-03-27 15:06:24 +02:00
Bananymous 99e30a4d7d Kernel: Replace i386 with i686
I don't really want to be working with i386 since it doesn't support
compare exchange instruction
2024-03-26 02:48:26 +02:00
Bananymous fe17958b9f Kernel: Rename rsp->sp and rip->ip
This makes more sense if we support i386
2024-03-22 15:41:15 +02:00
Bananymous c0dff5e203 Kernel: Scheduler/Thread add inline assembly for i386 2024-03-22 14:01:27 +02:00
Bananymous 9607b4205a Kernel: Fix kernel panic on signal
Signals are now added/handled without Scheduler's lock
2024-03-18 16:05:47 +02:00
Bananymous e65bc040af Kernel: Now all active processors are used in scheduling
When a timer reschedule happens, ipi is broadcasted too all
processors for them to perform a reschedule!
2024-03-09 23:53:50 +02:00
Bananymous 45d6caa1d0 Kernel: APs now start their idle threads when scheduler is started 2024-03-09 23:51:40 +02:00