citra-emu
/
citra-canary
Archived
1
0
Fork 0

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.
This commit is contained in:
Sebastian Valle 2020-04-15 07:35:19 -05:00 committed by GitHub
parent 263e5be78e
commit 9ae37da292
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 17 additions and 11 deletions

View File

@ -11,6 +11,10 @@
#include "core/arm/skyeye_common/vfp/asm_vfp.h" #include "core/arm/skyeye_common/vfp/asm_vfp.h"
#include "core/core_timing.h" #include "core/core_timing.h"
namespace Memory {
struct PageTable;
};
/// Generic ARM11 CPU interface /// Generic ARM11 CPU interface
class ARM_Interface : NonCopyable { class ARM_Interface : NonCopyable {
public: public:
@ -73,7 +77,7 @@ public:
virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0; virtual void InvalidateCacheRange(u32 start_address, std::size_t length) = 0;
/// Notify CPU emulation that page tables have changed /// 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 * Set the Program Counter to an address

View File

@ -153,7 +153,7 @@ ARM_Dynarmic::ARM_Dynarmic(Core::System* system, Memory::MemorySystem& memory, u
std::shared_ptr<Core::Timing::Timer> timer) std::shared_ptr<Core::Timing::Timer> timer)
: ARM_Interface(id, timer), system(*system), memory(memory), : ARM_Interface(id, timer), system(*system), memory(memory),
cb(std::make_unique<DynarmicUserCallbacks>(*this)) { cb(std::make_unique<DynarmicUserCallbacks>(*this)) {
PageTableChanged(); PageTableChanged(memory.GetCurrentPageTable());
} }
ARM_Dynarmic::~ARM_Dynarmic() = default; 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); jit->InvalidateCacheRange(start_address, length);
} }
void ARM_Dynarmic::PageTableChanged() { void ARM_Dynarmic::PageTableChanged(Memory::PageTable* new_page_table) {
current_page_table = memory.GetCurrentPageTable(); current_page_table = new_page_table;
auto iter = jits.find(current_page_table); auto iter = jits.find(current_page_table);
if (iter != jits.end()) { if (iter != jits.end()) {

View File

@ -52,7 +52,7 @@ public:
void ClearInstructionCache() override; void ClearInstructionCache() override;
void InvalidateCacheRange(u32 start_address, std::size_t length) override; void InvalidateCacheRange(u32 start_address, std::size_t length) override;
void PageTableChanged() override; void PageTableChanged(Memory::PageTable* new_page_table) override;
private: private:
void ServeBreak(); void ServeBreak();

View File

@ -95,7 +95,7 @@ void ARM_DynCom::InvalidateCacheRange(u32, std::size_t) {
ClearInstructionCache(); ClearInstructionCache();
} }
void ARM_DynCom::PageTableChanged() { void ARM_DynCom::PageTableChanged(Memory::PageTable*) {
ClearInstructionCache(); ClearInstructionCache();
} }

View File

@ -30,7 +30,7 @@ public:
void ClearInstructionCache() override; void ClearInstructionCache() override;
void InvalidateCacheRange(u32 start_address, std::size_t length) 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; void SetPC(u32 pc) override;
u32 GetPC() const override; u32 GetPC() const override;

View File

@ -67,13 +67,15 @@ void KernelSystem::SetCurrentProcessForCPU(std::shared_ptr<Process> process, u32
SetCurrentMemoryPageTable(&process->vm_manager.page_table); SetCurrentMemoryPageTable(&process->vm_manager.page_table);
} else { } else {
stored_processes[core_id] = process; stored_processes[core_id] = process;
thread_managers[core_id]->cpu->PageTableChanged(&process->vm_manager.page_table);
} }
} }
void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) { void KernelSystem::SetCurrentMemoryPageTable(Memory::PageTable* page_table) {
memory.SetCurrentPageTable(page_table); memory.SetCurrentPageTable(page_table);
if (current_cpu != nullptr) { 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);
} }
} }

View File

@ -75,11 +75,13 @@ void Thread::Stop() {
void ThreadManager::SwitchContext(Thread* new_thread) { void ThreadManager::SwitchContext(Thread* new_thread) {
Thread* previous_thread = GetCurrentThread(); Thread* previous_thread = GetCurrentThread();
Process* previous_process = nullptr;
Core::Timing& timing = kernel.timing; Core::Timing& timing = kernel.timing;
// Save context for previous thread // Save context for previous thread
if (previous_thread) { if (previous_thread) {
previous_process = previous_thread->owner_process;
previous_thread->last_running_ticks = timing.GetGlobalTicks(); previous_thread->last_running_ticks = timing.GetGlobalTicks();
cpu->SaveContext(previous_thread->context); cpu->SaveContext(previous_thread->context);
@ -99,14 +101,12 @@ void ThreadManager::SwitchContext(Thread* new_thread) {
// Cancel any outstanding wakeup events for this thread // Cancel any outstanding wakeup events for this thread
timing.UnscheduleEvent(ThreadWakeupEventType, new_thread->thread_id); timing.UnscheduleEvent(ThreadWakeupEventType, new_thread->thread_id);
auto previous_process = kernel.GetCurrentProcess();
current_thread = SharedFrom(new_thread); current_thread = SharedFrom(new_thread);
ready_queue.remove(new_thread->current_priority, new_thread); ready_queue.remove(new_thread->current_priority, new_thread);
new_thread->status = ThreadStatus::Running; 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()); kernel.SetCurrentProcessForCPU(SharedFrom(current_thread->owner_process), cpu->GetID());
} }