diff --git a/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp b/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp new file mode 100644 index 000000000..6e26d6af9 --- /dev/null +++ b/libraries/libmesosphere/include/mesosphere/kern_k_worker_task_manager.hpp @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once +#include +#include +#include + +namespace ams::kern { + + class KWorkerTaskManager { + public: + static constexpr s32 ExitWorkerPriority = 11; + + enum WorkerType { + WorkerType_Exit, + + WorkerType_Count, + }; + private: + KWorkerTask *head_task; + KWorkerTask *tail_task; + KThread *thread; + WorkerType type; + bool active; + private: + static void ThreadFunction(uintptr_t arg); + void ThreadFunctionImpl(); + + KWorkerTask *GetTask(); + void AddTask(KWorkerTask *task); + public: + constexpr KWorkerTaskManager() : head_task(), tail_task(), thread(), type(WorkerType_Count), active() { /* ... */ } + + NOINLINE void Initialize(WorkerType wt, s32 priority); + static void AddTask(WorkerType type, KWorkerTask *task); + }; + +} diff --git a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp index 2ba8599f1..8f12783c9 100644 --- a/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp +++ b/libraries/libmesosphere/include/mesosphere/kern_kernel.hpp @@ -20,6 +20,7 @@ #include #include #include +#include namespace ams::kern { @@ -69,6 +70,7 @@ namespace ams::kern { static KMemoryBlockSlabManager s_sys_memory_block_manager; static KBlockInfoManager s_block_info_manager; static KSupervisorPageTable s_supervisor_page_table; + static KWorkerTaskManager s_worker_task_managers[KWorkerTaskManager::WorkerType_Count]; private: static ALWAYS_INLINE KCoreLocalContext &GetCoreLocalContext() { return reinterpret_cast(cpu::GetCoreLocalRegionAddress())->current.context; @@ -135,6 +137,11 @@ namespace ams::kern { static ALWAYS_INLINE KSupervisorPageTable &GetKernelPageTable() { return s_supervisor_page_table; } + + static ALWAYS_INLINE KWorkerTaskManager &GetWorkerTaskManager(KWorkerTaskManager::WorkerType type) { + MESOSPHERE_ASSERT(type <= KWorkerTaskManager::WorkerType_Count); + return s_worker_task_managers[type]; + } }; } diff --git a/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp new file mode 100644 index 000000000..5a156cd53 --- /dev/null +++ b/libraries/libmesosphere/source/kern_k_worker_task_manager.cpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2018-2020 Atmosphère-NX + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#include + +namespace ams::kern { + + void KWorkerTaskManager::Initialize(WorkerType wt, s32 priority) { + /* Set type, other members already initialized in constructor. */ + this->type = wt; + + /* Reserve a thread from the system limit. */ + MESOSPHERE_ABORT_UNLESS(Kernel::GetSystemResourceLimit().Reserve(ams::svc::LimitableResource_ThreadCountMax, 1)); + + /* Create a new thread. */ + this->thread = KThread::Create(); + MESOSPHERE_ABORT_UNLESS(this->thread != nullptr); + + /* Launch the new thread. */ + MESOSPHERE_R_ABORT_UNLESS(KThread::InitializeKernelThread(this->thread, ThreadFunction, reinterpret_cast(this), priority, cpu::NumCores - 1)); + + /* Register the new thread. */ + KThread::Register(this->thread); + + /* Run the thread. */ + this->thread->Run(); + } + + void KWorkerTaskManager::AddTask(WorkerType type, KWorkerTask *task) { + MESOSPHERE_ASSERT(type <= WorkerType_Count); + Kernel::GetWorkerTaskManager(type).AddTask(task); + } + + void KWorkerTaskManager::ThreadFunction(uintptr_t arg) { + reinterpret_cast(arg)->ThreadFunctionImpl(); + } + + void KWorkerTaskManager::ThreadFunctionImpl() { + while (true) { + KWorkerTask *task = nullptr; + + /* Get a worker task. */ + { + KScopedSchedulerLock sl; + task = this->GetTask(); + + if (task == nullptr) { + /* If there's nothing to do, set ourselves as waiting. */ + this->active = false; + this->thread->SetState(KThread::ThreadState_Waiting); + continue; + } + + this->active = true; + } + + /* Do the task. */ + task->DoWorkerTask(); + } + } + + KWorkerTask *KWorkerTaskManager::GetTask() { + MESOSPHERE_ASSERT(KScheduler::IsSchedulerLockedByCurrentThread()); + KWorkerTask *next = this->head_task; + if (next) { + /* Advance the list. */ + if (this->head_task == this->tail_task) { + this->head_task = nullptr; + this->tail_task = nullptr; + } else { + this->head_task = this->head_task->GetNextTask(); + } + + /* Clear the next task's next. */ + next->SetNextTask(nullptr); + } + return next; + } + + void KWorkerTaskManager::AddTask(KWorkerTask *task) { + KScopedSchedulerLock sl; + MESOSPHERE_ASSERT(task->GetNextTask() == nullptr); + + /* Insert the task. */ + if (this->tail_task) { + this->tail_task->SetNextTask(task); + this->tail_task = task; + } else { + this->head_task = task; + this->tail_task = task; + + /* Make ourselves active if we need to. */ + if (!this->active) { + this->thread->SetState(KThread::ThreadState_Runnable); + } + } + } + +} diff --git a/libraries/libmesosphere/source/kern_main.cpp b/libraries/libmesosphere/source/kern_main.cpp index 81ab0d680..bafa88c08 100644 --- a/libraries/libmesosphere/source/kern_main.cpp +++ b/libraries/libmesosphere/source/kern_main.cpp @@ -107,7 +107,7 @@ namespace ams::kern { /* Perform more core-0 specific initialization. */ if (core_id == 0) { - MESOSPHERE_TODO("Initialize KWorkerThreadManager"); + Kernel::GetWorkerTaskManager(KWorkerTaskManager::WorkerType_Exit).Initialize(KWorkerTaskManager::WorkerType_Exit, KWorkerTaskManager::ExitWorkerPriority); MESOSPHERE_TODO("KSystemControl::InitializeSleepManagerAndAppletSecureMemory();"); diff --git a/mesosphere/kernel/source/kern_kernel_instantiations.cpp b/mesosphere/kernel/source/kern_kernel_instantiations.cpp index 6167c2726..ddd4dafde 100644 --- a/mesosphere/kernel/source/kern_kernel_instantiations.cpp +++ b/mesosphere/kernel/source/kern_kernel_instantiations.cpp @@ -26,6 +26,7 @@ namespace ams::kern { KMemoryBlockSlabManager Kernel::s_sys_memory_block_manager; KBlockInfoManager Kernel::s_block_info_manager; KSupervisorPageTable Kernel::s_supervisor_page_table; + KWorkerTaskManager Kernel::s_worker_task_managers[KWorkerTaskManager::WorkerType_Count]; namespace {