Commit Graph

128 Commits

Author SHA1 Message Date
Andreas Kling
c3e4387c57 Kernel: Stop flushing GDT/IDT registers all the time 2020-01-18 11:10:44 +01:00
Andreas Kling
94ca55cefd Meta: Add license header to source files
As suggested by Joshua, this commit adds the 2-clause BSD license as a
comment block to the top of every source file.

For the first pass, I've just added myself for simplicity. I encourage
everyone to add themselves as copyright holders of any file they've
added or modified in some significant way. If I've added myself in
error somewhere, feel free to replace it with the appropriate copyright
holder instead.

Going forward, all new source files should include a license header.
2020-01-18 09:45:54 +01:00
Andreas Kling
8b54ba0d61 Kernel: Dispatch pending signals when returning from a syscall
It was quite easy to put the system into a heavy churn state by doing
e.g "cat /dev/zero".

It was then basically impossible to kill the "cat" process, even with
"kill -9", since signals are only delivered in two conditions:

a) The target thread is blocked in the kernel
b) The target thread is running in userspace

However, since "cat /dev/zero" command spends most of its time actively
running in the kernel, not blocked, the signal dispatch code just kept
postponing actually handling the signal indefinitely.

To fix this, we now check before returning from a syscall if there are
any pending unmasked signals, and if so, we take a dramatic pause by
blocking the current thread, knowing it will immediately be unblocked
by signal dispatch anyway. :^)
2020-01-12 15:04:33 +01:00
Andreas Kling
9e51291aed Kernel: Disable SMAP protection while capturing profile samples 2020-01-12 02:02:29 +01:00
Andreas Kling
17ef5bc0ac Kernel: Rename {ss,esp}_if_crossRing to userspace_{ss,esp}
These were always so awkwardly named.
2020-01-09 18:02:01 +01:00
Andreas Kling
76c20642f0 Kernel: Ignore closed fd's when considering select() unblock
This fixes a null RefPtr deref (which asserts) in the scheduler if a
file descriptor being select()'ed is closed by a second thread while
blocked in select().

Test: Kernel/null-deref-close-during-select.cpp
2020-01-09 12:36:42 +01:00
Andreas Kling
f598bbbb1d Kernel: Prevent executing I/O instructions in userspace
All threads were running with iomapbase=0 in their TSS, which the CPU
interprets as "there's an I/O permission bitmap starting at offset 0
into my TSS".

Because of that, any bits that were 1 inside the TSS would allow the
thread to execute I/O instructions on the port with that bit index.

Fix this by always setting the iomapbase to sizeof(TSS32), and also
setting the TSS descriptor's limit to sizeof(TSS32), effectively making
the I/O permissions bitmap zero-length.

This should make it no longer possible to do I/O from userspace. :^)
2020-01-01 17:31:41 +01:00
Andreas Kling
fd740829d1 Kernel: Switch to eagerly restoring x86 FPU state on context switch
Lazy FPU restore is well known to be vulnerable to timing attacks,
and eager restore is a lot simpler anyway, so let's just do it eagerly.
2020-01-01 16:54:21 +01:00
Andreas Kling
50677bf806 Kernel: Refactor scheduler to use dynamic thread priorities
Threads now have numeric priorities with a base priority in the 1-99
range.

Whenever a runnable thread is *not* scheduled, its effective priority
is incremented by 1. This is tracked in Thread::m_extra_priority.
The effective priority of a thread is m_priority + m_extra_priority.

When a runnable thread *is* scheduled, its m_extra_priority is reset to
zero and the effective priority returns to base.

This means that lower-priority threads will always eventually get
scheduled to run, once its effective priority becomes high enough to
exceed the base priority of threads "above" it.

The previous values for ThreadPriority (Low, Normal and High) are now
replaced as follows:

    Low -> 10
    Normal -> 30
    High -> 50

In other words, it will take 20 ticks for a "Low" priority thread to
get to "Normal" effective priority, and another 20 to reach "High".

This is not perfect, and I've used some quite naive data structures,
but I think the mechanism will allow us to build various new and
interesting optimizations, and we can figure out better data structures
later on. :^)
2019-12-30 18:46:17 +01:00
Conrad Pankoff
115b315375 Kernel: Add kernel-level timer queue (heavily based on @juliusf's work)
PR #591 defines the rationale for kernel-level timers. They're most
immediately useful for TCP retransmission, but will most likely see use
in many other areas as well.
2019-12-27 02:15:45 +01:00
Andreas Kling
abdd5aa08a Kernel: Separate runnable thread queues by priority
This patch introduces three separate thread queues, one for each thread
priority available to userspace (Low, Normal and High.)

Each queue operates in a round-robin fashion, but we now always prefer
to schedule the highest priority thread that currently wants to run.

There are tons of tweaks and improvements that we can and should make
to this mechanism, but I think this is a step in the right direction.

This makes WindowServer significantly more responsive while one of its
clients is burning CPU. :^)
2019-12-27 00:52:30 +01:00
Andreas Kling
95034fdfbd Kernel: Move PC speaker beep timing logic from scheduler to the syscall
I don't know why I put this in the scheduler to begin with.. the caller
can just block until the beeping is finished.
2019-12-26 22:31:26 +01:00
Andreas Kling
16812f0f98 Kernel: Get rid of "main thread" concept
The idea of all processes reliably having a main thread was nice in
some ways, but cumbersome in others. More importantly, it didn't match
up with POSIX thread semantics, so let's move away from it.

This thread gets rid of Process::main_thread() and you now we just have
a bunch of Thread objects floating around each Process.

When the finalizer nukes the last Thread in a Process, it will also
tear down the Process.

There's a bunch of more things to fix around this, but this is where we
get started :^)
2019-12-22 12:37:58 +01:00
Andreas Kling
77cf607cda Kernel+LibC: Publish a "kernel info page" and use it for gettimeofday()
This patch adds a single "kernel info page" that is mappable read-only
by any process and contains the current time of day.

This is then used to implement a version of gettimeofday() that doesn't
have to make a syscall.

To protect against race condition issues, the info page also has a
serial number which is incremented whenever the kernel updates the
contents of the page. Make sure to verify that the serial number is the
same before and after reading the information you want from the page.
2019-12-15 21:29:26 +01:00
Andreas Kling
b32e961a84 Kernel: Implement a simple process time profiler
The kernel now supports basic profiling of all the threads in a process
by calling profiling_enable(pid_t). You finish the profiling by calling
profiling_disable(pid_t).

This all works by recording thread stacks when the timer interrupt
fires and the current thread is in a process being profiled.
Note that symbolication is deferred until profiling_disable() to avoid
adding more noise than necessary to the profile.

A simple "/bin/profile" command is included here that can be used to
start/stop profiling like so:

    $ profile 10 on
    ... wait ...
    $ profile 10 off

After a profile has been recorded, it can be fetched in /proc/profile

There are various limits (or "bugs") on this mechanism at the moment:

- Only one process can be profiled at a time.
- We allocate 8MB for the samples, if you use more space, things will
  not work, and probably break a bit.
- Things will probably fall apart if the profiled process dies during
  profiling, or while extracing /proc/profile
2019-12-11 20:36:56 +01:00
Andreas Kling
8bb98aa31b Kernel: Use a WaitQueue to implement finalizer wakeup
This gets rid of the special "Lurking" thread state and replaces it
with a generic WaitQueue :^)
2019-12-01 19:17:17 +01:00
Andreas Kling
5859e16e53 Kernel: Use a dedicated thread state for wait-queued threads
Instead of using the generic block mechanism, wait-queued threads now
go into the special Queued state.

This fixes an issue where signal dispatch would unblock a wait-queued
thread (because signal dispatch unblocks blocked threads) and cause
confusion since the thread only expected to be awoken by the queue.
2019-12-01 16:02:58 +01:00
Andreas Kling
f067730f6b Kernel: Add a WaitQueue for Thread queueing/waking and use it for Lock
The kernel's Lock class now uses a proper wait queue internally instead
of just having everyone wake up regularly to try to acquire the lock.

We also keep the donation mechanism, so that whenever someone tries to
take the lock and fails, that thread donates the remainder of its
timeslice to the current lock holder.

After unlocking a Lock, the unlocking thread calls WaitQueue::wake_one,
which unblocks the next thread in queue.
2019-12-01 12:07:43 +01:00
Andreas Kling
cb5021419e Kernel: Move Thread::m_joinee_exit_value into the JoinBlocker
There's no need for this to be a permanent Thread member. Just use a
reference in the JoinBlocker instead.
2019-11-14 21:04:34 +01:00
Andreas Kling
69efa3f630 Kernel+LibPthread: Implement pthread_join()
It's now possible to block until another thread in the same process has
exited. We can also retrieve its exit value, which is whatever value it
passed to pthread_exit(). :^)
2019-11-14 20:58:23 +01:00
Andreas Kling
083c5f8b89 Kernel: Rework Process::Priority into ThreadPriority
Scheduling priority is now set at the thread level instead of at the
process level.

This is a step towards allowing processes to set different priorities
for threads. There's no userspace API for that yet, since only the main
thread's priority is affected by sched_setparam().
2019-11-06 16:30:06 +01:00
Drew Stratford
7fc903b97a Kernel: Add exception_code to RegisterDump.
Added the exception_code field to RegisterDump, removing the need
for RegisterDumpWithExceptionCode. To accomplish this, I had to
push a dummy exception code during some interrupt entries to properly
pad out the RegisterDump. Note that we also needed to change some code
in sys$sigreturn to deal with the new RegisterDump layout.
2019-10-07 16:39:47 +02:00
Andreas Kling
c33ac7f170 Kernel: Don't update Thread TSS if scheduler tick reschedules it
If we didn't find anything else that wants to run, we don't need to
update the current thread's TSS since we're just gonna return to the
same thread anyway.
2019-10-02 13:47:44 +02:00
Andreas Kling
e1481dcb42 Kernel: Stop idling after handling an IRQ
If we receive an IRQ while the idle task is running, prevent it from
re-halting the CPU after the IRQ handler returns.

Instead have the idle task yield to the scheduler, so we can see if
the IRQ has unblocked something.
2019-09-14 20:21:10 +02:00
Drew Stratford
b65bedd610 Kernel: Change m_blockers to m_blocker.
Because of the way signals now work there should
not be more than one blocker per thread. This
changes the blocker and thread class to reflect
that.
2019-09-09 08:35:43 +02:00
Andreas Kling
23eafdb8d6 Kernel: waitpid() should unblock and -ECHILD if SIG_IGN reaps child 2019-09-08 14:01:00 +02:00
Andreas Kling
ec6bceaa08 Kernel: Support thread-local storage
This patch adds support for TLS according to the x86 System V ABI.
Each thread gets a thread-specific memory region, and the GS segment
register always points _to a pointer_ to the thread-specific memory.

In other words, to access thread-local variables, userspace programs
start by dereferencing the pointer at [gs:0].

The Process keeps a master copy of the TLS segment that new threads
should use, and when a new thread is created, they get a copy of it.
It's basically whatever the PT_TLS program header in the ELF says.
2019-09-07 15:55:36 +02:00
Conrad Pankoff
bd6d2c0819 Kernel: Use a more detailed state machine for socket setup 2019-08-10 09:07:11 +02:00
Andreas Kling
1f9b8f0e7c Kernel: Don't create Function objects in the scheduling code
Each Function is a heap allocation, so let's make an effort to avoid
doing that during scheduling. Because of header dependencies, I had to
put the runnables iteration helpers in Thread.h, which is a bit meh but
at least this cuts out all the kmalloc() traffic in pick_next().
2019-08-07 20:43:54 +02:00
Andreas Kling
55d6efd485 Scheduler: Fix bitrotted SCHEDULER_RUNNABLE_DEBUG code
The runnable lists have moved from Thread to Scheduler.
2019-08-01 20:00:56 +02:00
Andreas Kling
09cd3a7a07 Scheduler: Fix deadlock when first scheduling candidate being inspected
Somewhat reproducible by opening ProcessManager and trying to view the
stacks for WindowServer.

Regressed in 53262cd08b.
2019-08-01 19:33:36 +02:00
Robin Burchell
342f7a6b0f Move runnable/non-runnable list control entirely over to Scheduler
This way, we can change how the scheduler works without having to change Thread too.
2019-07-22 09:42:39 +02:00
Robin Burchell
dea7f937bf Scheduler: Allow reentry into block()
With the presence of signal handlers, it is possible that a thread might
be blocked multiple times. Picture for instance a signal handler using
read(), or wait() while the thread is already blocked elsewhere before
the handler is invoked.

To fix this, we turn m_blocker into a chain of handlers. Each block()
call now prepends to the list, and unblocking will only consider the
most recent (first) blocker in the chain.

Fixes #309
2019-07-21 12:42:22 +02:00
Robin Burchell
d48c73b10a Thread: Cleanup m_blocker handling
The only two places we set m_blocker now are Thread::set_state(), and
Thread::block(). set_state is mostly just an issue of clarity: we don't
want to end up with state() != Blocked with an m_blocker, because that's
weird. It's also possible: if we yield, someone else may set_state() us.

We also now set_state() and set m_blocker under lock in block(), rather
than unlocking which might allow someone else to mess with our internals
while we're in the process of trying to block.

This seems to fix sending STOP & CONT causing a panic.

My guess as to what was happening is this:

    thread A blocks in select(): Blocking & m_blocker != nullptr
    thread B sends SIGSTOP: Stopped & m_blocker != nullptr
    thread B sends SIGCONT: we continue execution. Runnable & m_blocker != nullptr
    thread A tries to block in select() again:
        * sets m_blocker
        * unlocks (in block_helper)
        * someone else tries to unblock us? maybe from the old m_blocker? unclear -- clears m_blocker
        * sets Blocked (while unlocked!)

So, thread A is left with state Blocked & m_blocker == nullptr, leading
to the scheduler assert (m_blocker != nullptr) failing.

Long story short, let's do all our data management with the lock _held_.
2019-07-20 19:31:52 +02:00
Robin Burchell
833d444cd8 Thread: Return a result from block() indicating why the block terminated
And use this to return EINTR in various places; some of which we were
not handling properly before.

This might expose a few bugs in userspace, but should be more compatible
with other POSIX systems, and is certainly a little cleaner.
2019-07-20 12:15:24 +02:00
Robin Burchell
53262cd08b AK: Introduce IntrusiveList
And use it in the scheduler.

IntrusiveList is similar to InlineLinkedList, except that rather than
making assertions about the type (and requiring inheritance), it
provides an IntrusiveListNode type that can be used to put an instance
into many different lists at once.

As a proof of concept, port the scheduler over to use it. The only
downside here is that the "list" global needs to know the position of
the IntrusiveListNode member, so we have to position things a little
awkwardly to make that happen. We also move the runnable lists to
Thread, to avoid having to publicize the node.
2019-07-19 15:42:30 +02:00
Andreas Kling
705cd2491c Kernel: Some small refinements to the thread blockers.
Committing some things my hands did while browsing through this code.

- Mark all leaf classes "final".
- FileDescriptionBlocker now stores a NonnullRefPtr<FileDescription>.
- FileDescriptionBlocker::blocked_description() now returns a reference.
- ConditionBlocker takes a Function&&.
2019-07-19 13:19:47 +02:00
Robin Burchell
d092855c72 Scheduler: Remove some raw use of the runnable lists 2019-07-19 13:19:02 +02:00
Robin Burchell
e74dce65e6 Thread: Normalize all for_each constructs to use IterationDecision
This way a caller can abort the for_each early if they want.
2019-07-19 13:19:02 +02:00
Robin Burchell
762333ba95 Kernel: Restore state strings for block states
"Blocking" is not terribly informative, but now that everything is
ported over, we can force the blocker to provide us with a reason.

This does mean that to_string(State) needed to become a member, but
that's OK.
2019-07-19 11:03:22 +02:00
Robin Burchell
b13f1699fc Kernel: Rename Condition state to Blocked now we only have one blocking mechanism :) 2019-07-19 11:03:22 +02:00
Robin Burchell
d2ca91c024 Kernel: Convert BlockedSignal and BlockedLurking to the new Blocker mechanism
The last two of the old block states gone :)
2019-07-19 11:03:22 +02:00
Robin Burchell
750dbe986d Kernel: Avoid allocations for Select vectors by using inline capacity
Good tip by Andreas :)
2019-07-19 11:03:22 +02:00
Robin Burchell
52743f9eec Kernel: Rename ThreadBlocker classes to avoid stutter
Thread::ThreadBlockerFoo is a lot less nice to read than Thread::FooBlocker
2019-07-19 11:03:22 +02:00
Robin Burchell
782e4ee6e1 Kernel: Port wait to ThreadBlocker 2019-07-19 11:03:22 +02:00
Robin Burchell
4f9ae9b970 Kernel: Port select to ThreadBlocker 2019-07-19 11:03:22 +02:00
Robin Burchell
32fcfb79e9 Kernel: Port sleep to ThreadBlocker 2019-07-19 11:03:22 +02:00
Robin Burchell
0c8813e6d9 Kernel: Introduce ThreadBlocker as a way to make unblocking neater :)
And port all the descriptor-based blocks over to it as a proof of concept.
2019-07-19 11:03:22 +02:00
Robin Burchell
4da2521606 Scheduler: Move thread unblocking out of a lambda to help make things more readable
We also use a switch to explicitly make sure we handle all cases properly.
2019-07-18 16:14:09 +02:00
Robin Burchell
f2fdac789c Kernel: Add a new block state for accept() on a blocking socket
Rather than asserting, which really ruins everyone's day.
2019-07-18 10:56:49 +02:00