From 6a620562cc7298c2f591a06817ff560c9ef1deac Mon Sep 17 00:00:00 2001 From: Tom Date: Mon, 16 Nov 2020 20:51:34 -0700 Subject: [PATCH] Kernel: Allow passing a thread argument for new kernel threads 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. --- Kernel/Arch/i386/CPU.cpp | 17 +++++++++++++++-- Kernel/Net/NetworkTask.cpp | 6 +++--- Kernel/Process.cpp | 8 +++++--- Kernel/Process.h | 26 ++++++++++++++++++++++++-- Kernel/Scheduler.cpp | 6 +++--- Kernel/Scheduler.h | 2 +- Kernel/Syscalls/thread.cpp | 6 +----- Kernel/Tasks/FinalizerTask.cpp | 4 ++-- Kernel/Thread.cpp | 9 +++++++++ Kernel/Thread.h | 2 ++ Kernel/init.cpp | 6 +++--- 11 files changed, 68 insertions(+), 24 deletions(-) diff --git a/Kernel/Arch/i386/CPU.cpp b/Kernel/Arch/i386/CPU.cpp index 6df49f7acc..0de723d461 100644 --- a/Kernel/Arch/i386/CPU.cpp +++ b/Kernel/Arch/i386/CPU.cpp @@ -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*); @@ -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()); @@ -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? @@ -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(kernel_stack_top - 2 * sizeof(u32)) = tss.esp; + *reinterpret_cast(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(kernel_stack_top - 4) = 0; + *reinterpret_cast(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 diff --git a/Kernel/Net/NetworkTask.cpp b/Kernel/Net/NetworkTask.cpp index 93fe9e6e1d..cd7616f098 100644 --- a/Kernel/Net/NetworkTask.cpp +++ b/Kernel/Net/NetworkTask.cpp @@ -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; - 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; diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp index 0693c9ef61..f7cb346114 100644 --- a/Kernel/Process.cpp +++ b/Kernel/Process.cpp @@ -311,10 +311,11 @@ RefPtr Process::create_user_process(RefPtr& first_thread, const return process; } -NonnullRefPtr Process::create_kernel_process(RefPtr& first_thread, String&& name, void (*e)(), u32 affinity) +NonnullRefPtr Process::create_kernel_process(RefPtr& 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); @@ -765,7 +766,7 @@ KResult Process::send_signal(u8 signal, Process* sender) return KResult(-ESRCH); } -RefPtr Process::create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity, bool joinable) +RefPtr 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)); @@ -781,6 +782,7 @@ RefPtr 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); diff --git a/Kernel/Process.h b/Kernel/Process.h index 1700047d2c..7e85373988 100644 --- a/Kernel/Process.h +++ b/Kernel/Process.h @@ -126,14 +126,35 @@ public: return current_thread ? ¤t_thread->process() : nullptr; } - static NonnullRefPtr create_kernel_process(RefPtr& first_thread, String&& name, void (*entry)(), u32 affinity = THREAD_AFFINITY_DEFAULT); + template + static NonnullRefPtr create_kernel_process(RefPtr& 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(data); + (*func)(); + delete func; + }, entry_func, affinity); + } + + static NonnullRefPtr create_kernel_process(RefPtr& first_thread, String&& name, void (*entry)(void*), void* entry_data = nullptr, u32 affinity = THREAD_AFFINITY_DEFAULT); static RefPtr create_user_process(RefPtr& first_thread, const String& path, uid_t, gid_t, ProcessID ppid, int& error, Vector&& arguments = Vector(), Vector&& environment = Vector(), TTY* = nullptr); ~Process(); static Vector all_pids(); static AK::NonnullRefPtrVector all_processes(); - RefPtr create_kernel_thread(void (*entry)(), u32 priority, const String& name, u32 affinity = THREAD_AFFINITY_DEFAULT, bool joinable = true); + template + RefPtr 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(data); + (*func)(); + delete func; + }, priority, name, affinity, joinable); + } + RefPtr 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; } @@ -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()); diff --git a/Kernel/Scheduler.cpp b/Kernel/Scheduler.cpp index 8f4ad69602..e89bd89f77 100644 --- a/Kernel/Scheduler.cpp +++ b/Kernel/Scheduler.cpp @@ -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); @@ -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; } @@ -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()); diff --git a/Kernel/Scheduler.h b/Kernel/Scheduler.h index 2afca051d8..c26fe3ac7e 100644 --- a/Kernel/Scheduler.h +++ b/Kernel/Scheduler.h @@ -66,7 +66,7 @@ public: 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(); diff --git a/Kernel/Syscalls/thread.cpp b/Kernel/Syscalls/thread.cpp index d2250d1cae..9781ab0e72 100644 --- a/Kernel/Syscalls/thread.cpp +++ b/Kernel/Syscalls/thread.cpp @@ -93,11 +93,7 @@ void Process::sys$exit_thread(Userspace exit_value) { REQUIRE_PROMISE(thread); cli(); - auto current_thread = Thread::current(); - current_thread->m_exit_value = reinterpret_cast(exit_value.ptr()); - current_thread->set_should_die(); - big_lock().force_unlock_if_locked(); - current_thread->die_if_needed(); + Thread::current()->exit(reinterpret_cast(exit_value.ptr())); ASSERT_NOT_REACHED(); } diff --git a/Kernel/Tasks/FinalizerTask.cpp b/Kernel/Tasks/FinalizerTask.cpp index fe82f84ab9..f05d573a7f 100644 --- a/Kernel/Tasks/FinalizerTask.cpp +++ b/Kernel/Tasks/FinalizerTask.cpp @@ -32,7 +32,7 @@ namespace Kernel { void FinalizerTask::spawn() { RefPtr 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"); @@ -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; } diff --git a/Kernel/Thread.cpp b/Kernel/Thread.cpp index fa433fdfbd..65e441b41b 100644 --- a/Kernel/Thread.cpp +++ b/Kernel/Thread.cpp @@ -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(); diff --git a/Kernel/Thread.h b/Kernel/Thread.h index 1235de6e42..07e98cd046 100644 --- a/Kernel/Thread.h +++ b/Kernel/Thread.h @@ -572,6 +572,8 @@ public: 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; } diff --git a/Kernel/init.cpp b/Kernel/init.cpp index f24da6513f..f4e2e613ab 100644 --- a/Kernel/init.cpp +++ b/Kernel/init.cpp @@ -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 @@ -168,7 +168,7 @@ extern "C" [[noreturn]] void init() { RefPtr 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 @@ -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.