k_scheduler.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. // Copyright 2020 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. // This file references various implementation details from Atmosphere, an open-source firmware for
  5. // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX.
  6. #include <bit>
  7. #include "common/assert.h"
  8. #include "common/bit_util.h"
  9. #include "common/fiber.h"
  10. #include "common/logging/log.h"
  11. #include "core/arm/arm_interface.h"
  12. #include "core/core.h"
  13. #include "core/core_timing.h"
  14. #include "core/cpu_manager.h"
  15. #include "core/hle/kernel/k_interrupt_manager.h"
  16. #include "core/hle/kernel/k_process.h"
  17. #include "core/hle/kernel/k_scheduler.h"
  18. #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h"
  19. #include "core/hle/kernel/k_thread.h"
  20. #include "core/hle/kernel/kernel.h"
  21. #include "core/hle/kernel/physical_core.h"
  22. #include "core/hle/kernel/time_manager.h"
  23. namespace Kernel {
  24. static void IncrementScheduledCount(Kernel::KThread* thread) {
  25. if (auto process = thread->GetOwnerProcess(); process) {
  26. process->IncrementScheduledCount();
  27. }
  28. }
  29. void KScheduler::RescheduleCores(KernelCore& kernel, u64 cores_pending_reschedule) {
  30. auto scheduler = kernel.CurrentScheduler();
  31. u32 current_core{0xF};
  32. bool must_context_switch{};
  33. if (scheduler) {
  34. current_core = scheduler->core_id;
  35. // TODO(bunnei): Should be set to true when we deprecate single core
  36. must_context_switch = !kernel.IsPhantomModeForSingleCore();
  37. }
  38. while (cores_pending_reschedule != 0) {
  39. const auto core = static_cast<u32>(std::countr_zero(cores_pending_reschedule));
  40. ASSERT(core < Core::Hardware::NUM_CPU_CORES);
  41. if (!must_context_switch || core != current_core) {
  42. auto& phys_core = kernel.PhysicalCore(core);
  43. phys_core.Interrupt();
  44. }
  45. cores_pending_reschedule &= ~(1ULL << core);
  46. }
  47. for (std::size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; ++core_id) {
  48. if (kernel.PhysicalCore(core_id).IsInterrupted()) {
  49. KInterruptManager::HandleInterrupt(kernel, static_cast<s32>(core_id));
  50. }
  51. }
  52. if (must_context_switch) {
  53. auto core_scheduler = kernel.CurrentScheduler();
  54. kernel.ExitSVCProfile();
  55. core_scheduler->RescheduleCurrentCore();
  56. kernel.EnterSVCProfile();
  57. }
  58. }
  59. u64 KScheduler::UpdateHighestPriorityThread(KThread* highest_thread) {
  60. KScopedSpinLock lk{guard};
  61. if (KThread* prev_highest_thread = state.highest_priority_thread;
  62. prev_highest_thread != highest_thread) {
  63. if (prev_highest_thread != nullptr) {
  64. IncrementScheduledCount(prev_highest_thread);
  65. prev_highest_thread->SetLastScheduledTick(system.CoreTiming().GetCPUTicks());
  66. }
  67. if (state.should_count_idle) {
  68. if (highest_thread != nullptr) {
  69. if (KProcess* process = highest_thread->GetOwnerProcess(); process != nullptr) {
  70. process->SetRunningThread(core_id, highest_thread, state.idle_count);
  71. }
  72. } else {
  73. state.idle_count++;
  74. }
  75. }
  76. state.highest_priority_thread = highest_thread;
  77. state.needs_scheduling.store(true);
  78. return (1ULL << core_id);
  79. } else {
  80. return 0;
  81. }
  82. }
  83. u64 KScheduler::UpdateHighestPriorityThreadsImpl(KernelCore& kernel) {
  84. ASSERT(kernel.GlobalSchedulerContext().IsLocked());
  85. // Clear that we need to update.
  86. ClearSchedulerUpdateNeeded(kernel);
  87. u64 cores_needing_scheduling = 0, idle_cores = 0;
  88. KThread* top_threads[Core::Hardware::NUM_CPU_CORES];
  89. auto& priority_queue = GetPriorityQueue(kernel);
  90. /// We want to go over all cores, finding the highest priority thread and determining if
  91. /// scheduling is needed for that core.
  92. for (size_t core_id = 0; core_id < Core::Hardware::NUM_CPU_CORES; core_id++) {
  93. KThread* top_thread = priority_queue.GetScheduledFront(static_cast<s32>(core_id));
  94. if (top_thread != nullptr) {
  95. // If the thread has no waiters, we need to check if the process has a thread pinned.
  96. if (top_thread->GetNumKernelWaiters() == 0) {
  97. if (KProcess* parent = top_thread->GetOwnerProcess(); parent != nullptr) {
  98. if (KThread* pinned = parent->GetPinnedThread(static_cast<s32>(core_id));
  99. pinned != nullptr && pinned != top_thread) {
  100. // We prefer our parent's pinned thread if possible. However, we also don't
  101. // want to schedule un-runnable threads.
  102. if (pinned->GetRawState() == ThreadState::Runnable) {
  103. top_thread = pinned;
  104. } else {
  105. top_thread = nullptr;
  106. }
  107. }
  108. }
  109. }
  110. } else {
  111. idle_cores |= (1ULL << core_id);
  112. }
  113. top_threads[core_id] = top_thread;
  114. cores_needing_scheduling |=
  115. kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
  116. }
  117. // Idle cores are bad. We're going to try to migrate threads to each idle core in turn.
  118. while (idle_cores != 0) {
  119. const auto core_id = static_cast<u32>(std::countr_zero(idle_cores));
  120. if (KThread* suggested = priority_queue.GetSuggestedFront(core_id); suggested != nullptr) {
  121. s32 migration_candidates[Core::Hardware::NUM_CPU_CORES];
  122. size_t num_candidates = 0;
  123. // While we have a suggested thread, try to migrate it!
  124. while (suggested != nullptr) {
  125. // Check if the suggested thread is the top thread on its core.
  126. const s32 suggested_core = suggested->GetActiveCore();
  127. if (KThread* top_thread =
  128. (suggested_core >= 0) ? top_threads[suggested_core] : nullptr;
  129. top_thread != suggested) {
  130. // Make sure we're not dealing with threads too high priority for migration.
  131. if (top_thread != nullptr &&
  132. top_thread->GetPriority() < HighestCoreMigrationAllowedPriority) {
  133. break;
  134. }
  135. // The suggested thread isn't bound to its core, so we can migrate it!
  136. suggested->SetActiveCore(core_id);
  137. priority_queue.ChangeCore(suggested_core, suggested);
  138. top_threads[core_id] = suggested;
  139. cores_needing_scheduling |=
  140. kernel.Scheduler(core_id).UpdateHighestPriorityThread(top_threads[core_id]);
  141. break;
  142. }
  143. // Note this core as a candidate for migration.
  144. ASSERT(num_candidates < Core::Hardware::NUM_CPU_CORES);
  145. migration_candidates[num_candidates++] = suggested_core;
  146. suggested = priority_queue.GetSuggestedNext(core_id, suggested);
  147. }
  148. // If suggested is nullptr, we failed to migrate a specific thread. So let's try all our
  149. // candidate cores' top threads.
  150. if (suggested == nullptr) {
  151. for (size_t i = 0; i < num_candidates; i++) {
  152. // Check if there's some other thread that can run on the candidate core.
  153. const s32 candidate_core = migration_candidates[i];
  154. suggested = top_threads[candidate_core];
  155. if (KThread* next_on_candidate_core =
  156. priority_queue.GetScheduledNext(candidate_core, suggested);
  157. next_on_candidate_core != nullptr) {
  158. // The candidate core can run some other thread! We'll migrate its current
  159. // top thread to us.
  160. top_threads[candidate_core] = next_on_candidate_core;
  161. cores_needing_scheduling |=
  162. kernel.Scheduler(candidate_core)
  163. .UpdateHighestPriorityThread(top_threads[candidate_core]);
  164. // Perform the migration.
  165. suggested->SetActiveCore(core_id);
  166. priority_queue.ChangeCore(candidate_core, suggested);
  167. top_threads[core_id] = suggested;
  168. cores_needing_scheduling |=
  169. kernel.Scheduler(core_id).UpdateHighestPriorityThread(
  170. top_threads[core_id]);
  171. break;
  172. }
  173. }
  174. }
  175. }
  176. idle_cores &= ~(1ULL << core_id);
  177. }
  178. return cores_needing_scheduling;
  179. }
  180. void KScheduler::ClearPreviousThread(KernelCore& kernel, KThread* thread) {
  181. ASSERT(kernel.GlobalSchedulerContext().IsLocked());
  182. for (size_t i = 0; i < Core::Hardware::NUM_CPU_CORES; ++i) {
  183. // Get an atomic reference to the core scheduler's previous thread.
  184. std::atomic_ref<KThread*> prev_thread(kernel.Scheduler(static_cast<s32>(i)).prev_thread);
  185. static_assert(std::atomic_ref<KThread*>::is_always_lock_free);
  186. // Atomically clear the previous thread if it's our target.
  187. KThread* compare = thread;
  188. prev_thread.compare_exchange_strong(compare, nullptr);
  189. }
  190. }
  191. void KScheduler::OnThreadStateChanged(KernelCore& kernel, KThread* thread, ThreadState old_state) {
  192. ASSERT(kernel.GlobalSchedulerContext().IsLocked());
  193. // Check if the state has changed, because if it hasn't there's nothing to do.
  194. const auto cur_state = thread->GetRawState();
  195. if (cur_state == old_state) {
  196. return;
  197. }
  198. // Update the priority queues.
  199. if (old_state == ThreadState::Runnable) {
  200. // If we were previously runnable, then we're not runnable now, and we should remove.
  201. GetPriorityQueue(kernel).Remove(thread);
  202. IncrementScheduledCount(thread);
  203. SetSchedulerUpdateNeeded(kernel);
  204. } else if (cur_state == ThreadState::Runnable) {
  205. // If we're now runnable, then we weren't previously, and we should add.
  206. GetPriorityQueue(kernel).PushBack(thread);
  207. IncrementScheduledCount(thread);
  208. SetSchedulerUpdateNeeded(kernel);
  209. }
  210. }
  211. void KScheduler::OnThreadPriorityChanged(KernelCore& kernel, KThread* thread, s32 old_priority) {
  212. ASSERT(kernel.GlobalSchedulerContext().IsLocked());
  213. // If the thread is runnable, we want to change its priority in the queue.
  214. if (thread->GetRawState() == ThreadState::Runnable) {
  215. GetPriorityQueue(kernel).ChangePriority(old_priority,
  216. thread == kernel.GetCurrentEmuThread(), thread);
  217. IncrementScheduledCount(thread);
  218. SetSchedulerUpdateNeeded(kernel);
  219. }
  220. }
  221. void KScheduler::OnThreadAffinityMaskChanged(KernelCore& kernel, KThread* thread,
  222. const KAffinityMask& old_affinity, s32 old_core) {
  223. ASSERT(kernel.GlobalSchedulerContext().IsLocked());
  224. // If the thread is runnable, we want to change its affinity in the queue.
  225. if (thread->GetRawState() == ThreadState::Runnable) {
  226. GetPriorityQueue(kernel).ChangeAffinityMask(old_core, old_affinity, thread);
  227. IncrementScheduledCount(thread);
  228. SetSchedulerUpdateNeeded(kernel);
  229. }
  230. }
  231. void KScheduler::RotateScheduledQueue(s32 cpu_core_id, s32 priority) {
  232. ASSERT(system.GlobalSchedulerContext().IsLocked());
  233. // Get a reference to the priority queue.
  234. auto& kernel = system.Kernel();
  235. auto& priority_queue = GetPriorityQueue(kernel);
  236. // Rotate the front of the queue to the end.
  237. KThread* top_thread = priority_queue.GetScheduledFront(cpu_core_id, priority);
  238. KThread* next_thread = nullptr;
  239. if (top_thread != nullptr) {
  240. next_thread = priority_queue.MoveToScheduledBack(top_thread);
  241. if (next_thread != top_thread) {
  242. IncrementScheduledCount(top_thread);
  243. IncrementScheduledCount(next_thread);
  244. }
  245. }
  246. // While we have a suggested thread, try to migrate it!
  247. {
  248. KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id, priority);
  249. while (suggested != nullptr) {
  250. // Check if the suggested thread is the top thread on its core.
  251. const s32 suggested_core = suggested->GetActiveCore();
  252. if (KThread* top_on_suggested_core =
  253. (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
  254. : nullptr;
  255. top_on_suggested_core != suggested) {
  256. // If the next thread is a new thread that has been waiting longer than our
  257. // suggestion, we prefer it to our suggestion.
  258. if (top_thread != next_thread && next_thread != nullptr &&
  259. next_thread->GetLastScheduledTick() < suggested->GetLastScheduledTick()) {
  260. suggested = nullptr;
  261. break;
  262. }
  263. // If we're allowed to do a migration, do one.
  264. // NOTE: Unlike migrations in UpdateHighestPriorityThread, this moves the suggestion
  265. // to the front of the queue.
  266. if (top_on_suggested_core == nullptr ||
  267. top_on_suggested_core->GetPriority() >= HighestCoreMigrationAllowedPriority) {
  268. suggested->SetActiveCore(cpu_core_id);
  269. priority_queue.ChangeCore(suggested_core, suggested, true);
  270. IncrementScheduledCount(suggested);
  271. break;
  272. }
  273. }
  274. // Get the next suggestion.
  275. suggested = priority_queue.GetSamePriorityNext(cpu_core_id, suggested);
  276. }
  277. }
  278. // Now that we might have migrated a thread with the same priority, check if we can do better.
  279. {
  280. KThread* best_thread = priority_queue.GetScheduledFront(cpu_core_id);
  281. if (best_thread == GetCurrentThread()) {
  282. best_thread = priority_queue.GetScheduledNext(cpu_core_id, best_thread);
  283. }
  284. // If the best thread we can choose has a priority the same or worse than ours, try to
  285. // migrate a higher priority thread.
  286. if (best_thread != nullptr && best_thread->GetPriority() >= priority) {
  287. KThread* suggested = priority_queue.GetSuggestedFront(cpu_core_id);
  288. while (suggested != nullptr) {
  289. // If the suggestion's priority is the same as ours, don't bother.
  290. if (suggested->GetPriority() >= best_thread->GetPriority()) {
  291. break;
  292. }
  293. // Check if the suggested thread is the top thread on its core.
  294. const s32 suggested_core = suggested->GetActiveCore();
  295. if (KThread* top_on_suggested_core =
  296. (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
  297. : nullptr;
  298. top_on_suggested_core != suggested) {
  299. // If we're allowed to do a migration, do one.
  300. // NOTE: Unlike migrations in UpdateHighestPriorityThread, this moves the
  301. // suggestion to the front of the queue.
  302. if (top_on_suggested_core == nullptr ||
  303. top_on_suggested_core->GetPriority() >=
  304. HighestCoreMigrationAllowedPriority) {
  305. suggested->SetActiveCore(cpu_core_id);
  306. priority_queue.ChangeCore(suggested_core, suggested, true);
  307. IncrementScheduledCount(suggested);
  308. break;
  309. }
  310. }
  311. // Get the next suggestion.
  312. suggested = priority_queue.GetSuggestedNext(cpu_core_id, suggested);
  313. }
  314. }
  315. }
  316. // After a rotation, we need a scheduler update.
  317. SetSchedulerUpdateNeeded(kernel);
  318. }
  319. bool KScheduler::CanSchedule(KernelCore& kernel) {
  320. return kernel.GetCurrentEmuThread()->GetDisableDispatchCount() <= 1;
  321. }
  322. bool KScheduler::IsSchedulerUpdateNeeded(const KernelCore& kernel) {
  323. return kernel.GlobalSchedulerContext().scheduler_update_needed.load(std::memory_order_acquire);
  324. }
  325. void KScheduler::SetSchedulerUpdateNeeded(KernelCore& kernel) {
  326. kernel.GlobalSchedulerContext().scheduler_update_needed.store(true, std::memory_order_release);
  327. }
  328. void KScheduler::ClearSchedulerUpdateNeeded(KernelCore& kernel) {
  329. kernel.GlobalSchedulerContext().scheduler_update_needed.store(false, std::memory_order_release);
  330. }
  331. void KScheduler::DisableScheduling(KernelCore& kernel) {
  332. // If we are shutting down the kernel, none of this is relevant anymore.
  333. if (kernel.IsShuttingDown()) {
  334. return;
  335. }
  336. ASSERT(GetCurrentThreadPointer(kernel)->GetDisableDispatchCount() >= 0);
  337. GetCurrentThreadPointer(kernel)->DisableDispatch();
  338. }
  339. void KScheduler::EnableScheduling(KernelCore& kernel, u64 cores_needing_scheduling) {
  340. // If we are shutting down the kernel, none of this is relevant anymore.
  341. if (kernel.IsShuttingDown()) {
  342. return;
  343. }
  344. auto* current_thread = GetCurrentThreadPointer(kernel);
  345. ASSERT(current_thread->GetDisableDispatchCount() >= 1);
  346. if (current_thread->GetDisableDispatchCount() > 1) {
  347. current_thread->EnableDispatch();
  348. } else {
  349. RescheduleCores(kernel, cores_needing_scheduling);
  350. }
  351. }
  352. u64 KScheduler::UpdateHighestPriorityThreads(KernelCore& kernel) {
  353. if (IsSchedulerUpdateNeeded(kernel)) {
  354. return UpdateHighestPriorityThreadsImpl(kernel);
  355. } else {
  356. return 0;
  357. }
  358. }
  359. KSchedulerPriorityQueue& KScheduler::GetPriorityQueue(KernelCore& kernel) {
  360. return kernel.GlobalSchedulerContext().priority_queue;
  361. }
  362. void KScheduler::YieldWithoutCoreMigration(KernelCore& kernel) {
  363. // Validate preconditions.
  364. ASSERT(CanSchedule(kernel));
  365. ASSERT(kernel.CurrentProcess() != nullptr);
  366. // Get the current thread and process.
  367. KThread& cur_thread = Kernel::GetCurrentThread(kernel);
  368. KProcess& cur_process = *kernel.CurrentProcess();
  369. // If the thread's yield count matches, there's nothing for us to do.
  370. if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
  371. return;
  372. }
  373. // Get a reference to the priority queue.
  374. auto& priority_queue = GetPriorityQueue(kernel);
  375. // Perform the yield.
  376. {
  377. KScopedSchedulerLock lock(kernel);
  378. const auto cur_state = cur_thread.GetRawState();
  379. if (cur_state == ThreadState::Runnable) {
  380. // Put the current thread at the back of the queue.
  381. KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
  382. IncrementScheduledCount(std::addressof(cur_thread));
  383. // If the next thread is different, we have an update to perform.
  384. if (next_thread != std::addressof(cur_thread)) {
  385. SetSchedulerUpdateNeeded(kernel);
  386. } else {
  387. // Otherwise, set the thread's yield count so that we won't waste work until the
  388. // process is scheduled again.
  389. cur_thread.SetYieldScheduleCount(cur_process.GetScheduledCount());
  390. }
  391. }
  392. }
  393. }
  394. void KScheduler::YieldWithCoreMigration(KernelCore& kernel) {
  395. // Validate preconditions.
  396. ASSERT(CanSchedule(kernel));
  397. ASSERT(kernel.CurrentProcess() != nullptr);
  398. // Get the current thread and process.
  399. KThread& cur_thread = Kernel::GetCurrentThread(kernel);
  400. KProcess& cur_process = *kernel.CurrentProcess();
  401. // If the thread's yield count matches, there's nothing for us to do.
  402. if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
  403. return;
  404. }
  405. // Get a reference to the priority queue.
  406. auto& priority_queue = GetPriorityQueue(kernel);
  407. // Perform the yield.
  408. {
  409. KScopedSchedulerLock lock(kernel);
  410. const auto cur_state = cur_thread.GetRawState();
  411. if (cur_state == ThreadState::Runnable) {
  412. // Get the current active core.
  413. const s32 core_id = cur_thread.GetActiveCore();
  414. // Put the current thread at the back of the queue.
  415. KThread* next_thread = priority_queue.MoveToScheduledBack(std::addressof(cur_thread));
  416. IncrementScheduledCount(std::addressof(cur_thread));
  417. // While we have a suggested thread, try to migrate it!
  418. bool recheck = false;
  419. KThread* suggested = priority_queue.GetSuggestedFront(core_id);
  420. while (suggested != nullptr) {
  421. // Check if the suggested thread is the thread running on its core.
  422. const s32 suggested_core = suggested->GetActiveCore();
  423. if (KThread* running_on_suggested_core =
  424. (suggested_core >= 0)
  425. ? kernel.Scheduler(suggested_core).state.highest_priority_thread
  426. : nullptr;
  427. running_on_suggested_core != suggested) {
  428. // If the current thread's priority is higher than our suggestion's we prefer
  429. // the next thread to the suggestion. We also prefer the next thread when the
  430. // current thread's priority is equal to the suggestions, but the next thread
  431. // has been waiting longer.
  432. if ((suggested->GetPriority() > cur_thread.GetPriority()) ||
  433. (suggested->GetPriority() == cur_thread.GetPriority() &&
  434. next_thread != std::addressof(cur_thread) &&
  435. next_thread->GetLastScheduledTick() < suggested->GetLastScheduledTick())) {
  436. suggested = nullptr;
  437. break;
  438. }
  439. // If we're allowed to do a migration, do one.
  440. // NOTE: Unlike migrations in UpdateHighestPriorityThread, this moves the
  441. // suggestion to the front of the queue.
  442. if (running_on_suggested_core == nullptr ||
  443. running_on_suggested_core->GetPriority() >=
  444. HighestCoreMigrationAllowedPriority) {
  445. suggested->SetActiveCore(core_id);
  446. priority_queue.ChangeCore(suggested_core, suggested, true);
  447. IncrementScheduledCount(suggested);
  448. break;
  449. } else {
  450. // We couldn't perform a migration, but we should check again on a future
  451. // yield.
  452. recheck = true;
  453. }
  454. }
  455. // Get the next suggestion.
  456. suggested = priority_queue.GetSuggestedNext(core_id, suggested);
  457. }
  458. // If we still have a suggestion or the next thread is different, we have an update to
  459. // perform.
  460. if (suggested != nullptr || next_thread != std::addressof(cur_thread)) {
  461. SetSchedulerUpdateNeeded(kernel);
  462. } else if (!recheck) {
  463. // Otherwise if we don't need to re-check, set the thread's yield count so that we
  464. // won't waste work until the process is scheduled again.
  465. cur_thread.SetYieldScheduleCount(cur_process.GetScheduledCount());
  466. }
  467. }
  468. }
  469. }
  470. void KScheduler::YieldToAnyThread(KernelCore& kernel) {
  471. // Validate preconditions.
  472. ASSERT(CanSchedule(kernel));
  473. ASSERT(kernel.CurrentProcess() != nullptr);
  474. // Get the current thread and process.
  475. KThread& cur_thread = Kernel::GetCurrentThread(kernel);
  476. KProcess& cur_process = *kernel.CurrentProcess();
  477. // If the thread's yield count matches, there's nothing for us to do.
  478. if (cur_thread.GetYieldScheduleCount() == cur_process.GetScheduledCount()) {
  479. return;
  480. }
  481. // Get a reference to the priority queue.
  482. auto& priority_queue = GetPriorityQueue(kernel);
  483. // Perform the yield.
  484. {
  485. KScopedSchedulerLock lock(kernel);
  486. const auto cur_state = cur_thread.GetRawState();
  487. if (cur_state == ThreadState::Runnable) {
  488. // Get the current active core.
  489. const s32 core_id = cur_thread.GetActiveCore();
  490. // Migrate the current thread to core -1.
  491. cur_thread.SetActiveCore(-1);
  492. priority_queue.ChangeCore(core_id, std::addressof(cur_thread));
  493. IncrementScheduledCount(std::addressof(cur_thread));
  494. // If there's nothing scheduled, we can try to perform a migration.
  495. if (priority_queue.GetScheduledFront(core_id) == nullptr) {
  496. // While we have a suggested thread, try to migrate it!
  497. KThread* suggested = priority_queue.GetSuggestedFront(core_id);
  498. while (suggested != nullptr) {
  499. // Check if the suggested thread is the top thread on its core.
  500. const s32 suggested_core = suggested->GetActiveCore();
  501. if (KThread* top_on_suggested_core =
  502. (suggested_core >= 0) ? priority_queue.GetScheduledFront(suggested_core)
  503. : nullptr;
  504. top_on_suggested_core != suggested) {
  505. // If we're allowed to do a migration, do one.
  506. if (top_on_suggested_core == nullptr ||
  507. top_on_suggested_core->GetPriority() >=
  508. HighestCoreMigrationAllowedPriority) {
  509. suggested->SetActiveCore(core_id);
  510. priority_queue.ChangeCore(suggested_core, suggested);
  511. IncrementScheduledCount(suggested);
  512. }
  513. // Regardless of whether we migrated, we had a candidate, so we're done.
  514. break;
  515. }
  516. // Get the next suggestion.
  517. suggested = priority_queue.GetSuggestedNext(core_id, suggested);
  518. }
  519. // If the suggestion is different from the current thread, we need to perform an
  520. // update.
  521. if (suggested != std::addressof(cur_thread)) {
  522. SetSchedulerUpdateNeeded(kernel);
  523. } else {
  524. // Otherwise, set the thread's yield count so that we won't waste work until the
  525. // process is scheduled again.
  526. cur_thread.SetYieldScheduleCount(cur_process.GetScheduledCount());
  527. }
  528. } else {
  529. // Otherwise, we have an update to perform.
  530. SetSchedulerUpdateNeeded(kernel);
  531. }
  532. }
  533. }
  534. }
  535. KScheduler::KScheduler(Core::System& system_, s32 core_id_) : system{system_}, core_id{core_id_} {
  536. switch_fiber = std::make_shared<Common::Fiber>(OnSwitch, this);
  537. state.needs_scheduling.store(true);
  538. state.interrupt_task_thread_runnable = false;
  539. state.should_count_idle = false;
  540. state.idle_count = 0;
  541. state.idle_thread_stack = nullptr;
  542. state.highest_priority_thread = nullptr;
  543. }
  544. void KScheduler::Finalize() {
  545. if (idle_thread) {
  546. idle_thread->Close();
  547. idle_thread = nullptr;
  548. }
  549. }
  550. KScheduler::~KScheduler() {
  551. ASSERT(!idle_thread);
  552. }
  553. KThread* KScheduler::GetCurrentThread() const {
  554. if (auto result = current_thread.load(); result) {
  555. return result;
  556. }
  557. return idle_thread;
  558. }
  559. u64 KScheduler::GetLastContextSwitchTicks() const {
  560. return last_context_switch_time;
  561. }
  562. void KScheduler::RescheduleCurrentCore() {
  563. ASSERT(GetCurrentThread()->GetDisableDispatchCount() == 1);
  564. auto& phys_core = system.Kernel().PhysicalCore(core_id);
  565. if (phys_core.IsInterrupted()) {
  566. phys_core.ClearInterrupt();
  567. }
  568. guard.Lock();
  569. if (state.needs_scheduling.load()) {
  570. Schedule();
  571. } else {
  572. GetCurrentThread()->EnableDispatch();
  573. guard.Unlock();
  574. }
  575. }
  576. void KScheduler::OnThreadStart() {
  577. SwitchContextStep2();
  578. }
  579. void KScheduler::Unload(KThread* thread) {
  580. ASSERT(thread);
  581. LOG_TRACE(Kernel, "core {}, unload thread {}", core_id, thread ? thread->GetName() : "nullptr");
  582. if (thread->IsCallingSvc()) {
  583. thread->ClearIsCallingSvc();
  584. }
  585. auto& physical_core = system.Kernel().PhysicalCore(core_id);
  586. if (!physical_core.IsInitialized()) {
  587. return;
  588. }
  589. Core::ARM_Interface& cpu_core = physical_core.ArmInterface();
  590. cpu_core.SaveContext(thread->GetContext32());
  591. cpu_core.SaveContext(thread->GetContext64());
  592. // Save the TPIDR_EL0 system register in case it was modified.
  593. thread->SetTPIDR_EL0(cpu_core.GetTPIDR_EL0());
  594. cpu_core.ClearExclusiveState();
  595. if (!thread->IsTerminationRequested() && thread->GetActiveCore() == core_id) {
  596. prev_thread = thread;
  597. } else {
  598. prev_thread = nullptr;
  599. }
  600. thread->context_guard.Unlock();
  601. }
  602. void KScheduler::Reload(KThread* thread) {
  603. LOG_TRACE(Kernel, "core {}, reload thread {}", core_id, thread ? thread->GetName() : "nullptr");
  604. if (thread) {
  605. ASSERT_MSG(thread->GetState() == ThreadState::Runnable, "Thread must be runnable.");
  606. Core::ARM_Interface& cpu_core = system.ArmInterface(core_id);
  607. cpu_core.LoadContext(thread->GetContext32());
  608. cpu_core.LoadContext(thread->GetContext64());
  609. cpu_core.SetTlsAddress(thread->GetTLSAddress());
  610. cpu_core.SetTPIDR_EL0(thread->GetTPIDR_EL0());
  611. cpu_core.ClearExclusiveState();
  612. }
  613. }
  614. void KScheduler::SwitchContextStep2() {
  615. // Load context of new thread
  616. Reload(current_thread.load());
  617. RescheduleCurrentCore();
  618. }
  619. void KScheduler::ScheduleImpl() {
  620. KThread* previous_thread = GetCurrentThread();
  621. KThread* next_thread = state.highest_priority_thread;
  622. state.needs_scheduling = false;
  623. // We never want to schedule a null thread, so use the idle thread if we don't have a next.
  624. if (next_thread == nullptr) {
  625. next_thread = idle_thread;
  626. }
  627. // We never want to schedule a dummy thread, as these are only used by host threads for locking.
  628. if (next_thread->GetThreadType() == ThreadType::Dummy) {
  629. next_thread = idle_thread;
  630. }
  631. // If we're not actually switching thread, there's nothing to do.
  632. if (next_thread == current_thread.load()) {
  633. previous_thread->EnableDispatch();
  634. guard.Unlock();
  635. return;
  636. }
  637. if (next_thread->GetCurrentCore() != core_id) {
  638. next_thread->SetCurrentCore(core_id);
  639. }
  640. current_thread.store(next_thread);
  641. KProcess* const previous_process = system.Kernel().CurrentProcess();
  642. UpdateLastContextSwitchTime(previous_thread, previous_process);
  643. // Save context for previous thread
  644. Unload(previous_thread);
  645. std::shared_ptr<Common::Fiber>* old_context;
  646. old_context = &previous_thread->GetHostContext();
  647. guard.Unlock();
  648. Common::Fiber::YieldTo(*old_context, *switch_fiber);
  649. /// When a thread wakes up, the scheduler may have changed to other in another core.
  650. auto& next_scheduler = *system.Kernel().CurrentScheduler();
  651. next_scheduler.SwitchContextStep2();
  652. }
  653. void KScheduler::OnSwitch(void* this_scheduler) {
  654. KScheduler* sched = static_cast<KScheduler*>(this_scheduler);
  655. sched->SwitchToCurrent();
  656. }
  657. void KScheduler::SwitchToCurrent() {
  658. while (true) {
  659. {
  660. KScopedSpinLock lk{guard};
  661. current_thread.store(state.highest_priority_thread);
  662. state.needs_scheduling.store(false);
  663. }
  664. const auto is_switch_pending = [this] {
  665. KScopedSpinLock lk{guard};
  666. return state.needs_scheduling.load();
  667. };
  668. do {
  669. auto next_thread = current_thread.load();
  670. if (next_thread != nullptr) {
  671. next_thread->context_guard.Lock();
  672. if (next_thread->GetRawState() != ThreadState::Runnable) {
  673. next_thread->context_guard.Unlock();
  674. break;
  675. }
  676. if (next_thread->GetActiveCore() != core_id) {
  677. next_thread->context_guard.Unlock();
  678. break;
  679. }
  680. }
  681. auto thread = next_thread ? next_thread : idle_thread;
  682. Common::Fiber::YieldTo(switch_fiber, *thread->GetHostContext());
  683. } while (!is_switch_pending());
  684. }
  685. }
  686. void KScheduler::UpdateLastContextSwitchTime(KThread* thread, KProcess* process) {
  687. const u64 prev_switch_ticks = last_context_switch_time;
  688. const u64 most_recent_switch_ticks = system.CoreTiming().GetCPUTicks();
  689. const u64 update_ticks = most_recent_switch_ticks - prev_switch_ticks;
  690. if (thread != nullptr) {
  691. thread->AddCpuTime(core_id, update_ticks);
  692. }
  693. if (process != nullptr) {
  694. process->UpdateCPUTimeTicks(update_ticks);
  695. }
  696. last_context_switch_time = most_recent_switch_ticks;
  697. }
  698. void KScheduler::Initialize() {
  699. idle_thread = KThread::Create(system.Kernel());
  700. ASSERT(KThread::InitializeIdleThread(system, idle_thread, core_id).IsSuccess());
  701. idle_thread->SetName(fmt::format("IdleThread:{}", core_id));
  702. }
  703. KScopedSchedulerLock::KScopedSchedulerLock(KernelCore& kernel)
  704. : KScopedLock(kernel.GlobalSchedulerContext().SchedulerLock()) {}
  705. KScopedSchedulerLock::~KScopedSchedulerLock() = default;
  706. } // namespace Kernel