Skip to content

Commit

Permalink
Kernel: Allow passing a thread argument for new kernel threads
Browse files Browse the repository at this point in the history
This adds the ability to pass a pointer to kernel thread/process.
Also add the ability to use a closure as thread function, which
allows passing information to a kernel thread more easily.
  • Loading branch information
tomuta authored and awesomekling committed Nov 30, 2020
1 parent 6cb640e commit 6a62056
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 24 deletions.
17 changes: 15 additions & 2 deletions Kernel/Arch/i386/CPU.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ static GenericInterruptHandler* s_interrupt_handler[GENERIC_INTERRUPT_HANDLERS_C
extern "C" void enter_thread_context(Thread* from_thread, Thread* to_thread);
extern "C" void context_first_init(Thread* from_thread, Thread* to_thread, TrapFrame* trap);
extern "C" u32 do_init_context(Thread* thread, u32 flags);
extern "C" void exit_kernel_thread(void);
extern "C" void pre_init_finished(void);
extern "C" void post_init_finished(void);
extern "C" void handle_interrupt(TrapFrame*);
Expand Down Expand Up @@ -1457,6 +1458,11 @@ asm(
" jmp common_trap_exit \n"
);

void exit_kernel_thread(void)
{
Thread::current()->exit();
}

u32 Processor::init_context(Thread& thread, bool leave_crit)
{
ASSERT(is_kernel_mode());
Expand All @@ -1468,7 +1474,7 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
ASSERT(in_critical() == 1);
}

const u32 kernel_stack_top = thread.kernel_stack_top();
u32 kernel_stack_top = thread.kernel_stack_top();
u32 stack_top = kernel_stack_top;

// TODO: handle NT?
Expand All @@ -1482,13 +1488,20 @@ u32 Processor::init_context(Thread& thread, bool leave_crit)
// userspace_esp and userspace_ss are not popped off by iret
// unless we're switching back to user mode
stack_top -= sizeof(RegisterState) - 2 * sizeof(u32);

// For kernel threads we'll push the thread function argument
// which should be in tss.esp and exit_kernel_thread as return
// address.
stack_top -= 2 * sizeof(u32);
*reinterpret_cast<u32*>(kernel_stack_top - 2 * sizeof(u32)) = tss.esp;
*reinterpret_cast<u32*>(kernel_stack_top - 3 * sizeof(u32)) = FlatPtr(&exit_kernel_thread);
} else {
stack_top -= sizeof(RegisterState);
}

// we want to end up 16-byte aligned, %esp + 4 should be aligned
stack_top -= sizeof(u32);
*reinterpret_cast<u32*>(kernel_stack_top - 4) = 0;
*reinterpret_cast<u32*>(kernel_stack_top - sizeof(u32)) = 0;

// set up the stack so that after returning from thread_context_first_enter()
// we will end up either in kernel mode or user mode, depending on how the thread is set up
Expand Down
6 changes: 3 additions & 3 deletions Kernel/Net/NetworkTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,15 @@ static void handle_icmp(const EthernetFrameHeader&, const IPv4Packet&, const tim
static void handle_udp(const IPv4Packet&, const timeval& packet_timestamp);
static void handle_tcp(const IPv4Packet&, const timeval& packet_timestamp);

[[noreturn]] static void NetworkTask_main();
[[noreturn]] static void NetworkTask_main(void*);

void NetworkTask::spawn()
{
RefPtr<Thread> thread;
Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main);
Process::create_kernel_process(thread, "NetworkTask", NetworkTask_main, nullptr);
}

void NetworkTask_main()
void NetworkTask_main(void*)
{
WaitQueue packet_wait_queue;
u8 octet = 15;
Expand Down
8 changes: 5 additions & 3 deletions Kernel/Process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,10 +311,11 @@ RefPtr<Process> Process::create_user_process(RefPtr<Thread>& first_thread, const
return process;
}

NonnullRefPtr<Process> Process::create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*e)(), u32 affinity)
NonnullRefPtr<Process> Process::create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void *entry_data, u32 affinity)
{
auto process = adopt(*new Process(first_thread, move(name), (uid_t)0, (gid_t)0, ProcessID(0), true));
first_thread->tss().eip = (FlatPtr)e;
first_thread->tss().eip = (FlatPtr)entry;
first_thread->tss().esp = FlatPtr(entry_data); // entry function argument is expected to be in tss.esp

if (process->pid() != 0) {
ScopedSpinLock lock(g_processes_lock);
Expand Down Expand Up @@ -765,7 +766,7 @@ KResult Process::send_signal(u8 signal, Process* sender)
return KResult(-ESRCH);
}

RefPtr<Thread> Process::create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity, bool joinable)
RefPtr<Thread> Process::create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, const String& name, u32 affinity, bool joinable)
{
ASSERT((priority >= THREAD_PRIORITY_MIN) && (priority <= THREAD_PRIORITY_MAX));

Expand All @@ -781,6 +782,7 @@ RefPtr<Thread> Process::create_kernel_thread(void (*entry)(), u32 priority, cons

auto& tss = thread->tss();
tss.eip = (FlatPtr)entry;
tss.esp = FlatPtr(entry_data); // entry function argument is expected to be in tss.esp

ScopedSpinLock lock(g_scheduler_lock);
thread->set_state(Thread::State::Runnable);
Expand Down
26 changes: 24 additions & 2 deletions Kernel/Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,14 +126,35 @@ class Process
return current_thread ? &current_thread->process() : nullptr;
}

static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(), u32 affinity = THREAD_AFFINITY_DEFAULT);
template<typename EntryFunction>
static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, EntryFunction entry, u32 affinity = THREAD_AFFINITY_DEFAULT)
{
auto* entry_func = new EntryFunction(move(entry));
return create_kernel_process(first_thread, move(name), [](void* data) {
EntryFunction* func = reinterpret_cast<EntryFunction*>(data);
(*func)();
delete func;
}, entry_func, affinity);
}

static NonnullRefPtr<Process> create_kernel_process(RefPtr<Thread>& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT);
static RefPtr<Process> create_user_process(RefPtr<Thread>& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector<String>&& arguments = Vector<String>(), Vector<String>&& environment = Vector<String>(), TTY* = nullptr);
~Process();

static Vector<ProcessID> all_pids();
static AK::NonnullRefPtrVector<Process> all_processes();

RefPtr<Thread> create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true);
template<typename EntryFunction>
RefPtr<Thread> create_kernel_thread(EntryFunction entry, u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true)
{
auto* entry_func = new EntryFunction(move(entry));
return create_kernel_thread([](void* data) {
EntryFunction* func = reinterpret_cast<EntryFunction*>(data);
(*func)();
delete func;
}, priority, name, affinity, joinable);
}
RefPtr<Thread> create_kernel_thread(void (*entry)(void*), void* entry_data, u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true);

bool is_profiling() const { return m_profiling; }
void set_profiling(bool profiling) { m_profiling = profiling; }
Expand Down Expand Up @@ -689,6 +710,7 @@ inline bool InodeMetadata::may_write(const Process& process) const
return may_write(process.euid(), process.egid(), process.extra_gids());
}


inline bool InodeMetadata::may_execute(const Process& process) const
{
return may_execute(process.euid(), process.egid(), process.extra_gids());
Expand Down
6 changes: 3 additions & 3 deletions Kernel/Scheduler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -754,7 +754,7 @@ void Scheduler::initialize()
g_finalizer_wait_queue = new WaitQueue;

g_finalizer_has_work.store(false, AK::MemoryOrder::memory_order_release);
s_colonel_process = &Process::create_kernel_process(idle_thread, "colonel", idle_loop, 1).leak_ref();
s_colonel_process = &Process::create_kernel_process(idle_thread, "colonel", idle_loop, nullptr, 1).leak_ref();
ASSERT(s_colonel_process);
ASSERT(idle_thread);
idle_thread->set_priority(THREAD_PRIORITY_MIN);
Expand All @@ -776,7 +776,7 @@ Thread* Scheduler::create_ap_idle_thread(u32 cpu)
ASSERT(Processor::current().id() == 0);

ASSERT(s_colonel_process);
Thread* idle_thread = s_colonel_process->create_kernel_thread(idle_loop, THREAD_PRIORITY_MIN, String::format("idle thread #%u", cpu), 1 << cpu, false);
Thread* idle_thread = s_colonel_process->create_kernel_thread(idle_loop, nullptr, THREAD_PRIORITY_MIN, String::format("idle thread #%u", cpu), 1 << cpu, false);
ASSERT(idle_thread);
return idle_thread;
}
Expand Down Expand Up @@ -832,7 +832,7 @@ void Scheduler::notify_finalizer()
g_finalizer_wait_queue->wake_all();
}

void Scheduler::idle_loop()
void Scheduler::idle_loop(void*)
{
dbg() << "Scheduler[" << Processor::current().id() << "]: idle loop running";
ASSERT(are_interrupts_enabled());
Expand Down
2 changes: 1 addition & 1 deletion Kernel/Scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ class Scheduler {
static void prepare_for_idle_loop();
static Process* colonel();
static void beep();
static void idle_loop();
static void idle_loop(void*);
static void invoke_async();
static void notify_finalizer();

Expand Down
6 changes: 1 addition & 5 deletions Kernel/Syscalls/thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,11 +93,7 @@ void Process::sys$exit_thread(Userspace<void*> exit_value)
{
REQUIRE_PROMISE(thread);
cli();
auto current_thread = Thread::current();
current_thread->m_exit_value = reinterpret_cast<void*>(exit_value.ptr());
current_thread->set_should_die();
big_lock().force_unlock_if_locked();
current_thread->die_if_needed();
Thread::current()->exit(reinterpret_cast<void*>(exit_value.ptr()));
ASSERT_NOT_REACHED();
}

Expand Down
4 changes: 2 additions & 2 deletions Kernel/Tasks/FinalizerTask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ namespace Kernel {
void FinalizerTask::spawn()
{
RefPtr<Thread> finalizer_thread;
Process::create_kernel_process(finalizer_thread, "FinalizerTask", [] {
Process::create_kernel_process(finalizer_thread, "FinalizerTask", [](void*) {
Thread::current()->set_priority(THREAD_PRIORITY_LOW);
for (;;) {
Thread::current()->wait_on(*g_finalizer_wait_queue, "FinalizerTask");
Expand All @@ -41,7 +41,7 @@ void FinalizerTask::spawn()
if (g_finalizer_has_work.compare_exchange_strong(expected, false, AK::MemoryOrder::memory_order_acq_rel))
Thread::finalize_dying_threads();
}
});
}, nullptr);
g_finalizer = finalizer_thread;
}

Expand Down
9 changes: 9 additions & 0 deletions Kernel/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,15 @@ void Thread::die_if_needed()
ASSERT_NOT_REACHED();
}

void Thread::exit(void* exit_value)
{
ASSERT(Thread::current() == this);
m_exit_value = exit_value;
set_should_die();
unlock_process_if_locked();
die_if_needed();
}

void Thread::yield_without_holding_big_lock()
{
bool did_unlock = unlock_process_if_locked();
Expand Down
2 changes: 2 additions & 0 deletions Kernel/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -572,6 +572,8 @@ class Thread
bool should_die() const { return m_should_die; }
void die_if_needed();

void exit(void* = nullptr);

bool tick();
void set_ticks_left(u32 t) { m_ticks_left = t; }
u32 ticks_left() const { return m_ticks_left; }
Expand Down
6 changes: 3 additions & 3 deletions Kernel/init.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ u32 __stack_chk_guard;

namespace Kernel {

[[noreturn]] static void init_stage2();
[[noreturn]] static void init_stage2(void*);
static void setup_serial_debug();

// boot.S expects these functions precisely this this. We declare them here
Expand Down Expand Up @@ -168,7 +168,7 @@ extern "C" [[noreturn]] void init()

{
RefPtr<Thread> init_stage2_thread;
Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2);
Process::create_kernel_process(init_stage2_thread, "init_stage2", init_stage2, nullptr);
// We need to make sure we drop the reference for init_stage2_thread
// before calling into Scheduler::start, otherwise we will have a
// dangling Thread that never gets cleaned up
Expand Down Expand Up @@ -210,7 +210,7 @@ extern "C" void init_finished(u32 cpu)
}
}

void init_stage2()
void init_stage2(void*)
{
if (APIC::initialized() && APIC::the().enabled_processor_count() > 1) {
// We can't start the APs until we have a scheduler up and running.
Expand Down

0 comments on commit 6a62056

Please sign in to comment.