diff options
| author | 2020-12-03 16:43:18 -0800 | |
|---|---|---|
| committer | 2020-12-06 00:03:24 -0800 | |
| commit | 8d3e06349e12e7de17c334619f1f986792d1de4b (patch) | |
| tree | 926e34570e5e51d5d7dc03c13f45b1401f5ae829 /src/core | |
| parent | hle: kernel: Rewrite scheduler implementation based on Mesopshere. (diff) | |
| download | yuzu-8d3e06349e12e7de17c334619f1f986792d1de4b.tar.gz yuzu-8d3e06349e12e7de17c334619f1f986792d1de4b.tar.xz yuzu-8d3e06349e12e7de17c334619f1f986792d1de4b.zip | |
hle: kernel: Separate KScheduler from GlobalSchedulerContext class.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/global_scheduler_context.cpp | 55 | ||||
| -rw-r--r-- | src/core/hle/kernel/global_scheduler_context.h | 79 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scheduler.cpp | 48 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_scheduler.h | 74 |
5 files changed, 140 insertions, 118 deletions
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 662839ff8..ee61f22c0 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -148,6 +148,8 @@ add_library(core STATIC | |||
| 148 | hle/kernel/code_set.cpp | 148 | hle/kernel/code_set.cpp |
| 149 | hle/kernel/code_set.h | 149 | hle/kernel/code_set.h |
| 150 | hle/kernel/errors.h | 150 | hle/kernel/errors.h |
| 151 | hle/kernel/global_scheduler_context.cpp | ||
| 152 | hle/kernel/global_scheduler_context.h | ||
| 151 | hle/kernel/handle_table.cpp | 153 | hle/kernel/handle_table.cpp |
| 152 | hle/kernel/handle_table.h | 154 | hle/kernel/handle_table.h |
| 153 | hle/kernel/hle_ipc.cpp | 155 | hle/kernel/hle_ipc.cpp |
diff --git a/src/core/hle/kernel/global_scheduler_context.cpp b/src/core/hle/kernel/global_scheduler_context.cpp new file mode 100644 index 000000000..40e9adf47 --- /dev/null +++ b/src/core/hle/kernel/global_scheduler_context.cpp | |||
| @@ -0,0 +1,55 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <mutex> | ||
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/hle/kernel/global_scheduler_context.h" | ||
| 10 | #include "core/hle/kernel/k_scheduler.h" | ||
| 11 | #include "core/hle/kernel/kernel.h" | ||
| 12 | |||
| 13 | namespace Kernel { | ||
| 14 | |||
| 15 | GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel) | ||
| 16 | : kernel{kernel}, scheduler_lock{kernel} {} | ||
| 17 | |||
| 18 | GlobalSchedulerContext::~GlobalSchedulerContext() = default; | ||
| 19 | |||
| 20 | void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) { | ||
| 21 | std::scoped_lock lock{global_list_guard}; | ||
| 22 | thread_list.push_back(std::move(thread)); | ||
| 23 | } | ||
| 24 | |||
| 25 | void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) { | ||
| 26 | std::scoped_lock lock{global_list_guard}; | ||
| 27 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | ||
| 28 | thread_list.end()); | ||
| 29 | } | ||
| 30 | |||
| 31 | void GlobalSchedulerContext::PreemptThreads() { | ||
| 32 | // The priority levels at which the global scheduler preempts threads every 10 ms. They are | ||
| 33 | // ordered from Core 0 to Core 3. | ||
| 34 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 63}; | ||
| 35 | |||
| 36 | ASSERT(IsLocked()); | ||
| 37 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 38 | const u32 priority = preemption_priorities[core_id]; | ||
| 39 | kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); | ||
| 40 | } | ||
| 41 | } | ||
| 42 | |||
| 43 | bool GlobalSchedulerContext::IsLocked() const { | ||
| 44 | return scheduler_lock.IsLockedByCurrentThread(); | ||
| 45 | } | ||
| 46 | |||
| 47 | void GlobalSchedulerContext::Lock() { | ||
| 48 | scheduler_lock.Lock(); | ||
| 49 | } | ||
| 50 | |||
| 51 | void GlobalSchedulerContext::Unlock() { | ||
| 52 | scheduler_lock.Unlock(); | ||
| 53 | } | ||
| 54 | |||
| 55 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/global_scheduler_context.h b/src/core/hle/kernel/global_scheduler_context.h new file mode 100644 index 000000000..40fe44cc0 --- /dev/null +++ b/src/core/hle/kernel/global_scheduler_context.h | |||
| @@ -0,0 +1,79 @@ | |||
| 1 | // Copyright 2020 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <atomic> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/spin_lock.h" | ||
| 12 | #include "core/hardware_properties.h" | ||
| 13 | #include "core/hle/kernel/k_priority_queue.h" | ||
| 14 | #include "core/hle/kernel/k_scheduler_lock.h" | ||
| 15 | #include "core/hle/kernel/thread.h" | ||
| 16 | |||
| 17 | namespace Kernel { | ||
| 18 | |||
| 19 | class KernelCore; | ||
| 20 | class SchedulerLock; | ||
| 21 | |||
| 22 | using KSchedulerPriorityQueue = | ||
| 23 | KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>; | ||
| 24 | static constexpr s32 HighestCoreMigrationAllowedPriority = 2; | ||
| 25 | |||
| 26 | class GlobalSchedulerContext final { | ||
| 27 | friend class KScheduler; | ||
| 28 | |||
| 29 | public: | ||
| 30 | explicit GlobalSchedulerContext(KernelCore& kernel); | ||
| 31 | ~GlobalSchedulerContext(); | ||
| 32 | |||
| 33 | /// Adds a new thread to the scheduler | ||
| 34 | void AddThread(std::shared_ptr<Thread> thread); | ||
| 35 | |||
| 36 | /// Removes a thread from the scheduler | ||
| 37 | void RemoveThread(std::shared_ptr<Thread> thread); | ||
| 38 | |||
| 39 | /// Returns a list of all threads managed by the scheduler | ||
| 40 | const std::vector<std::shared_ptr<Thread>>& GetThreadList() const { | ||
| 41 | return thread_list; | ||
| 42 | } | ||
| 43 | |||
| 44 | /** | ||
| 45 | * Rotates the scheduling queues of threads at a preemption priority and then does | ||
| 46 | * some core rebalancing. Preemption priorities can be found in the array | ||
| 47 | * 'preemption_priorities'. | ||
| 48 | * | ||
| 49 | * @note This operation happens every 10ms. | ||
| 50 | */ | ||
| 51 | void PreemptThreads(); | ||
| 52 | |||
| 53 | /// Returns true if the global scheduler lock is acquired | ||
| 54 | bool IsLocked() const; | ||
| 55 | |||
| 56 | private: | ||
| 57 | friend class SchedulerLock; | ||
| 58 | |||
| 59 | /// Lock the scheduler to the current thread. | ||
| 60 | void Lock(); | ||
| 61 | |||
| 62 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | ||
| 63 | /// and reschedules current core if needed. | ||
| 64 | void Unlock(); | ||
| 65 | |||
| 66 | using LockType = KAbstractSchedulerLock<KScheduler>; | ||
| 67 | |||
| 68 | KernelCore& kernel; | ||
| 69 | |||
| 70 | std::atomic_bool scheduler_update_needed{}; | ||
| 71 | KSchedulerPriorityQueue priority_queue; | ||
| 72 | LockType scheduler_lock; | ||
| 73 | |||
| 74 | /// Lists all thread ids that aren't deleted/etc. | ||
| 75 | std::vector<std::shared_ptr<Thread>> thread_list; | ||
| 76 | Common::SpinLock global_list_guard{}; | ||
| 77 | }; | ||
| 78 | |||
| 79 | } // namespace Kernel | ||
diff --git a/src/core/hle/kernel/k_scheduler.cpp b/src/core/hle/kernel/k_scheduler.cpp index 7f7da610d..c7e2eabd4 100644 --- a/src/core/hle/kernel/k_scheduler.cpp +++ b/src/core/hle/kernel/k_scheduler.cpp | |||
| @@ -5,12 +5,6 @@ | |||
| 5 | // This file references various implementation details from Atmosphere, an open-source firmware for | 5 | // This file references various implementation details from Atmosphere, an open-source firmware for |
| 6 | // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | 6 | // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. |
| 7 | 7 | ||
| 8 | #include <algorithm> | ||
| 9 | #include <mutex> | ||
| 10 | #include <set> | ||
| 11 | #include <unordered_set> | ||
| 12 | #include <utility> | ||
| 13 | |||
| 14 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 15 | #include "common/bit_util.h" | 9 | #include "common/bit_util.h" |
| 16 | #include "common/fiber.h" | 10 | #include "common/fiber.h" |
| @@ -19,10 +13,10 @@ | |||
| 19 | #include "core/core.h" | 13 | #include "core/core.h" |
| 20 | #include "core/core_timing.h" | 14 | #include "core/core_timing.h" |
| 21 | #include "core/cpu_manager.h" | 15 | #include "core/cpu_manager.h" |
| 16 | #include "core/hle/kernel/k_scheduler.h" | ||
| 22 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 23 | #include "core/hle/kernel/physical_core.h" | 18 | #include "core/hle/kernel/physical_core.h" |
| 24 | #include "core/hle/kernel/process.h" | 19 | #include "core/hle/kernel/process.h" |
| 25 | #include "core/hle/kernel/k_scheduler.h" | ||
| 26 | #include "core/hle/kernel/thread.h" | 20 | #include "core/hle/kernel/thread.h" |
| 27 | #include "core/hle/kernel/time_manager.h" | 21 | #include "core/hle/kernel/time_manager.h" |
| 28 | 22 | ||
| @@ -34,11 +28,6 @@ static void IncrementScheduledCount(Kernel::Thread* thread) { | |||
| 34 | } | 28 | } |
| 35 | } | 29 | } |
| 36 | 30 | ||
| 37 | GlobalSchedulerContext::GlobalSchedulerContext(KernelCore& kernel) | ||
| 38 | : kernel{kernel}, scheduler_lock{kernel} {} | ||
| 39 | |||
| 40 | GlobalSchedulerContext::~GlobalSchedulerContext() = default; | ||
| 41 | |||
| 42 | /*static*/ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, | 31 | /*static*/ void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule, |
| 43 | Core::EmuThreadHandle global_thread) { | 32 | Core::EmuThreadHandle global_thread) { |
| 44 | u32 current_core = global_thread.host_handle; | 33 | u32 current_core = global_thread.host_handle; |
| @@ -205,33 +194,6 @@ u64 KScheduler::UpdateHighestPriorityThread(Thread* highest_thread) { | |||
| 205 | return cores_needing_scheduling; | 194 | return cores_needing_scheduling; |
| 206 | } | 195 | } |
| 207 | 196 | ||
| 208 | void GlobalSchedulerContext::AddThread(std::shared_ptr<Thread> thread) { | ||
| 209 | std::scoped_lock lock{global_list_guard}; | ||
| 210 | thread_list.push_back(std::move(thread)); | ||
| 211 | } | ||
| 212 | |||
| 213 | void GlobalSchedulerContext::RemoveThread(std::shared_ptr<Thread> thread) { | ||
| 214 | std::scoped_lock lock{global_list_guard}; | ||
| 215 | thread_list.erase(std::remove(thread_list.begin(), thread_list.end(), thread), | ||
| 216 | thread_list.end()); | ||
| 217 | } | ||
| 218 | |||
| 219 | void GlobalSchedulerContext::PreemptThreads() { | ||
| 220 | // The priority levels at which the global scheduler preempts threads every 10 ms. They are | ||
| 221 | // ordered from Core 0 to Core 3. | ||
| 222 | std::array<u32, Core::Hardware::NUM_CPU_CORES> preemption_priorities = {59, 59, 59, 63}; | ||
| 223 | |||
| 224 | ASSERT(IsLocked()); | ||
| 225 | for (u32 core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) { | ||
| 226 | const u32 priority = preemption_priorities[core_id]; | ||
| 227 | kernel.Scheduler(core_id).RotateScheduledQueue(core_id, priority); | ||
| 228 | } | ||
| 229 | } | ||
| 230 | |||
| 231 | bool GlobalSchedulerContext::IsLocked() const { | ||
| 232 | return scheduler_lock.IsLockedByCurrentThread(); | ||
| 233 | } | ||
| 234 | |||
| 235 | /*static*/ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, | 197 | /*static*/ void KScheduler::OnThreadStateChanged(KernelCore& kernel, Thread* thread, |
| 236 | u32 old_state) { | 198 | u32 old_state) { |
| 237 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 199 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| @@ -635,14 +597,6 @@ void KScheduler::YieldToAnyThread() { | |||
| 635 | } | 597 | } |
| 636 | } | 598 | } |
| 637 | 599 | ||
| 638 | void GlobalSchedulerContext::Lock() { | ||
| 639 | scheduler_lock.Lock(); | ||
| 640 | } | ||
| 641 | |||
| 642 | void GlobalSchedulerContext::Unlock() { | ||
| 643 | scheduler_lock.Unlock(); | ||
| 644 | } | ||
| 645 | |||
| 646 | KScheduler::KScheduler(Core::System& system, std::size_t core_id) | 600 | KScheduler::KScheduler(Core::System& system, std::size_t core_id) |
| 647 | : system(system), core_id(core_id) { | 601 | : system(system), core_id(core_id) { |
| 648 | switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this); | 602 | switch_fiber = std::make_shared<Common::Fiber>(std::function<void(void*)>(OnSwitch), this); |
diff --git a/src/core/hle/kernel/k_scheduler.h b/src/core/hle/kernel/k_scheduler.h index 535ee34b9..7f020d96e 100644 --- a/src/core/hle/kernel/k_scheduler.h +++ b/src/core/hle/kernel/k_scheduler.h | |||
| @@ -8,94 +8,27 @@ | |||
| 8 | #pragma once | 8 | #pragma once |
| 9 | 9 | ||
| 10 | #include <atomic> | 10 | #include <atomic> |
| 11 | #include <memory> | ||
| 12 | #include <mutex> | ||
| 13 | #include <vector> | ||
| 14 | 11 | ||
| 15 | #include "common/common_types.h" | 12 | #include "common/common_types.h" |
| 16 | #include "common/multi_level_queue.h" | ||
| 17 | #include "common/scope_exit.h" | ||
| 18 | #include "common/spin_lock.h" | 13 | #include "common/spin_lock.h" |
| 19 | #include "core/core_timing.h" | 14 | #include "core/hle/kernel/global_scheduler_context.h" |
| 20 | #include "core/hardware_properties.h" | ||
| 21 | #include "core/hle/kernel/k_priority_queue.h" | 15 | #include "core/hle/kernel/k_priority_queue.h" |
| 22 | #include "core/hle/kernel/k_scheduler_lock.h" | 16 | #include "core/hle/kernel/k_scheduler_lock.h" |
| 23 | #include "core/hle/kernel/thread.h" | ||
| 24 | 17 | ||
| 25 | namespace Common { | 18 | namespace Common { |
| 26 | class Fiber; | 19 | class Fiber; |
| 27 | } | 20 | } |
| 28 | 21 | ||
| 29 | namespace Core { | 22 | namespace Core { |
| 30 | class ARM_Interface; | ||
| 31 | class System; | 23 | class System; |
| 32 | } // namespace Core | 24 | } |
| 33 | 25 | ||
| 34 | namespace Kernel { | 26 | namespace Kernel { |
| 35 | 27 | ||
| 36 | class KernelCore; | 28 | class KernelCore; |
| 37 | class Process; | 29 | class Process; |
| 38 | class SchedulerLock; | 30 | class SchedulerLock; |
| 39 | 31 | class Thread; | |
| 40 | using KSchedulerPriorityQueue = | ||
| 41 | KPriorityQueue<Thread, Core::Hardware::NUM_CPU_CORES, THREADPRIO_LOWEST, THREADPRIO_HIGHEST>; | ||
| 42 | static constexpr s32 HighestCoreMigrationAllowedPriority = 2; | ||
| 43 | |||
| 44 | class GlobalSchedulerContext final { | ||
| 45 | friend class KScheduler; | ||
| 46 | |||
| 47 | public: | ||
| 48 | explicit GlobalSchedulerContext(KernelCore& kernel); | ||
| 49 | ~GlobalSchedulerContext(); | ||
| 50 | |||
| 51 | /// Adds a new thread to the scheduler | ||
| 52 | void AddThread(std::shared_ptr<Thread> thread); | ||
| 53 | |||
| 54 | /// Removes a thread from the scheduler | ||
| 55 | void RemoveThread(std::shared_ptr<Thread> thread); | ||
| 56 | |||
| 57 | /// Returns a list of all threads managed by the scheduler | ||
| 58 | const std::vector<std::shared_ptr<Thread>>& GetThreadList() const { | ||
| 59 | return thread_list; | ||
| 60 | } | ||
| 61 | |||
| 62 | /** | ||
| 63 | * Rotates the scheduling queues of threads at a preemption priority and then does | ||
| 64 | * some core rebalancing. Preemption priorities can be found in the array | ||
| 65 | * 'preemption_priorities'. | ||
| 66 | * | ||
| 67 | * @note This operation happens every 10ms. | ||
| 68 | */ | ||
| 69 | void PreemptThreads(); | ||
| 70 | |||
| 71 | u32 CpuCoresCount() const { | ||
| 72 | return Core::Hardware::NUM_CPU_CORES; | ||
| 73 | } | ||
| 74 | |||
| 75 | bool IsLocked() const; | ||
| 76 | |||
| 77 | private: | ||
| 78 | friend class SchedulerLock; | ||
| 79 | |||
| 80 | /// Lock the scheduler to the current thread. | ||
| 81 | void Lock(); | ||
| 82 | |||
| 83 | /// Unlocks the scheduler, reselects threads, interrupts cores for rescheduling | ||
| 84 | /// and reschedules current core if needed. | ||
| 85 | void Unlock(); | ||
| 86 | |||
| 87 | using LockType = KAbstractSchedulerLock<KScheduler>; | ||
| 88 | |||
| 89 | KernelCore& kernel; | ||
| 90 | |||
| 91 | std::atomic_bool scheduler_update_needed{}; | ||
| 92 | KSchedulerPriorityQueue priority_queue; | ||
| 93 | LockType scheduler_lock; | ||
| 94 | |||
| 95 | /// Lists all thread ids that aren't deleted/etc. | ||
| 96 | std::vector<std::shared_ptr<Thread>> thread_list; | ||
| 97 | Common::SpinLock global_list_guard{}; | ||
| 98 | }; | ||
| 99 | 32 | ||
| 100 | class KScheduler final { | 33 | class KScheduler final { |
| 101 | public: | 34 | public: |
| @@ -221,7 +154,6 @@ private: | |||
| 221 | 154 | ||
| 222 | /// Switches the CPU's active thread context to that of the specified thread | 155 | /// Switches the CPU's active thread context to that of the specified thread |
| 223 | void ScheduleImpl(); | 156 | void ScheduleImpl(); |
| 224 | void SwitchThread(Thread* next_thread); | ||
| 225 | 157 | ||
| 226 | /// When a thread wakes up, it must run this through it's new scheduler | 158 | /// When a thread wakes up, it must run this through it's new scheduler |
| 227 | void SwitchContextStep2(); | 159 | void SwitchContextStep2(); |