From 9ae37da292931547c8aa2bdf3829574ec3438542 Mon Sep 17 00:00:00 2001 From: Sebastian Valle Date: Wed, 15 Apr 2020 07:35:19 -0500 Subject: [PATCH] Kernel/Process: Fixed scheduling multiple processes in the SysCore using Dynarmic (#5193) * Kernel/Process: Notify the CPUs that a new pagetable has been set every time the process they're executing changes. Previously the page table would only be changed when the current CPU's page table was changed, this lead to stale JIT states and the PC going to 0 when context-switching a different core inside the ThreadManager::SwitchContext function because the JIT for a new pagetable is only constructed upon receiving the change notification. * Kernel/Process: Use the relevant CPU's last process to determine when to switch its current process. Previously it was checking the kernel's current_process variable, which gets overwritten every time a CPU runs its slice. The rescheduling happens after all CPUs have run their slice so the code was effectively only checking the last CPU's process. --- src/core/arm/arm_interface.h | 6 +++++- src/core/arm/dynarmic/arm_dynarmic.cpp | 6 +++--- src/core/arm/dynarmic/arm_dynarmic.h | 2 +- src/core/arm/dyncom/arm_dyncom.cpp | 2 +- src/core/arm/dyncom/arm_dyncom.h | 2 +- src/core/hle/kernel/kernel.cpp | 4 +++- src/core/hle/kernel/thread.cpp | 6 +++--- 7 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/core/arm/arm_interface.h b/src/core/arm/arm_interface.h index 22443295b..6595b53c4 100644 --- a/src/core/arm/arm_interface.h +++ b/src/core/arm/arm_interface.h @@ -11,6 +11,10 @@ #include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/core_timing.h" +namespace Memory { +struct PageTable; +}; + /// Generic ARM11 CPU interface class ARM_Interface : NonCopyable { public: @@ -73,7 +77,7 @@ public: virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0; /// Notify CPU emulation that page tables have changed - virtual void PageTableChanged() = 0; + virtual void PageTableChanged(Memory::PageTable* new_page_table) = 0; /** * Set the Program Counter to an address diff --git a/src/core/arm/dynarmic/arm_dynarmic.cpp b/src/core/arm/dynarmic/arm_dynarmic.cpp index 55da53b14..df5de7131 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.cpp +++ b/src/core/arm/dynarmic/arm_dynarmic.cpp @@ -153,7 +153,7 @@ ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u std::shared_ptr timer) : ARM_Interface(id, timer), system(*system), memory(memory), cb(std::make_unique(*this)) { - PageTableChanged(); + PageTableChanged(memory.GetCurrentPageTable()); } ARM_Dynarmic::~ARM_Dynarmic() = default; @@ -287,8 +287,8 @@ void ARM_Dynarmic::InvalidateCacheRange(u32 start_address, std::size_t length) { jit->InvalidateCacheRange(start_address, length); } -void ARM_Dynarmic::PageTableChanged() { - current_page_table = memory.GetCurrentPageTable(); +void ARM_Dynarmic::PageTableChanged(Memory::PageTable* new_page_table) { + current_page_table = new_page_table; auto iter = jits.find(current_page_table); if (iter != jits.end()) { diff --git a/src/core/arm/dynarmic/arm_dynarmic.h b/src/core/arm/dynarmic/arm_dynarmic.h index c403f438e..44f2ee375 100644 --- a/src/core/arm/dynarmic/arm_dynarmic.h +++ b/src/core/arm/dynarmic/arm_dynarmic.h @@ -52,7 +52,7 @@ public: void ClearInstructionCache() override; void InvalidateCacheRange(u32 start_address, std::size_t length) override; - void PageTableChanged() override; + void PageTableChanged(Memory::PageTable* new_page_table) override; private: void ServeBreak(); diff --git a/src/core/arm/dyncom/arm_dyncom.cpp b/src/core/arm/dyncom/arm_dyncom.cpp index fa1aa598d..c2c291df0 100644 --- a/src/core/arm/dyncom/arm_dyncom.cpp +++ b/src/core/arm/dyncom/arm_dyncom.cpp @@ -95,7 +95,7 @@ void ARM_DynCom::InvalidateCacheRange(u32, std::size_t) { ClearInstructionCache(); } -void ARM_DynCom::PageTableChanged() { +void ARM_DynCom::PageTableChanged(Memory::PageTable*) { ClearInstructionCache(); } diff --git a/src/core/arm/dyncom/arm_dyncom.h b/src/core/arm/dyncom/arm_dyncom.h index f5360b307..38659eac0 100644 --- a/src/core/arm/dyncom/arm_dyncom.h +++ b/src/core/arm/dyncom/arm_dyncom.h @@ -30,7 +30,7 @@ public: void ClearInstructionCache() override; void InvalidateCacheRange(u32 start_address, std::size_t length) override; - void PageTableChanged() override; + void PageTableChanged(Memory::PageTable* new_page_table) override; void SetPC(u32 pc) override; u32 GetPC() const override; diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 995cfc658..8b2d89ead 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp @@ -67,13 +67,15 @@ void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr process, u32 SetCurrentMemoryPageTable(&process->vm_manager.page_table); } else { stored_processes[core_id] = process; + thread_managers[core_id]->cpu->PageTableChanged(&process->vm_manager.page_table); } } void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) { memory.SetCurrentPageTable(page_table); if (current_cpu != nullptr) { - current_cpu->PageTableChanged(); // notify the CPU the page table in memory has changed + // Notify the CPU the page table in memory has changed + current_cpu->PageTableChanged(page_table); } } diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index c86a0fb86..b2255c226 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp @@ -75,11 +75,13 @@ void Thread::Stop() { void ThreadManager::SwitchContext(Thread* new_thread) { Thread* previous_thread = GetCurrentThread(); + Process* previous_process = nullptr; Core::Timing& timing = kernel.timing; // Save context for previous thread if (previous_thread) { + previous_process = previous_thread->owner_process; previous_thread->last_running_ticks = timing.GetGlobalTicks(); cpu->SaveContext(previous_thread->context); @@ -99,14 +101,12 @@ void ThreadManager::SwitchContext(Thread* new_thread) { // Cancel any outstanding wakeup events for this thread timing.UnscheduleEvent(ThreadWakeupEventType, new_thread->thread_id); - auto previous_process = kernel.GetCurrentProcess(); - current_thread = SharedFrom(new_thread); ready_queue.remove(new_thread->current_priority, new_thread); new_thread->status = ThreadStatus::Running; - if (previous_process.get() != current_thread->owner_process) { + if (previous_process != current_thread->owner_process) { kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID()); }