Kernel: Workaround to mutex corruption

# Conflicts:
#	src/core/hle/kernel/mutex.cpp
This commit is contained in:
bunnei
2019-11-28 11:48:42 -05:00
parent e3ee017e91
commit 657a4ce07c
3 changed files with 33 additions and 16 deletions

View File

@@ -7,6 +7,7 @@
#include "common/assert.h" #include "common/assert.h"
#include "core/core.h" #include "core/core.h"
#include "core/core_cpu.h"
#include "core/hle/kernel/errors.h" #include "core/hle/kernel/errors.h"
#include "core/hle/kernel/handle_table.h" #include "core/hle/kernel/handle_table.h"
#include "core/hle/kernel/kernel.h" #include "core/hle/kernel/kernel.h"
@@ -23,7 +24,7 @@ namespace Kernel {
/// Returns the number of threads that are waiting for a mutex, and the highest priority one among /// Returns the number of threads that are waiting for a mutex, and the highest priority one among
/// those. /// those.
static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread( static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
const std::shared_ptr<Thread>& current_thread, VAddr mutex_addr) { Thread* current_thread, VAddr mutex_addr) {
std::shared_ptr<Thread> highest_priority_thread; std::shared_ptr<Thread> highest_priority_thread;
u32 num_waiters = 0; u32 num_waiters = 0;
@@ -45,16 +46,15 @@ static std::pair<std::shared_ptr<Thread>, u32> GetHighestPriorityMutexWaitingThr
} }
/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner. /// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
static void TransferMutexOwnership(VAddr mutex_addr, std::shared_ptr<Thread> current_thread, static void TransferMutexOwnership(VAddr mutex_addr, Thread* current_thread, Thread* new_owner) {
std::shared_ptr<Thread> new_owner) {
const auto threads = current_thread->GetMutexWaitingThreads(); const auto threads = current_thread->GetMutexWaitingThreads();
for (const auto& thread : threads) { for (const auto& thread : threads) {
if (thread->GetMutexWaitAddress() != mutex_addr) if (thread->GetMutexWaitAddress() != mutex_addr)
continue; continue;
ASSERT(thread->GetLockOwner() == current_thread.get()); ASSERT(thread->GetLockOwner() == current_thread);
current_thread->RemoveMutexWaiter(thread); current_thread->RemoveMutexWaiter(thread);
if (new_owner != thread) if (new_owner != thread.get())
new_owner->AddMutexWaiter(thread); new_owner->AddMutexWaiter(thread);
} }
} }
@@ -79,7 +79,7 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
// thread. // thread.
ASSERT(requesting_thread == current_thread); ASSERT(requesting_thread == current_thread);
const u32 addr_value = system.Memory().Read32(address); u32 addr_value = system.Memory().Read32(address);
// If the mutex isn't being held, just return success. // If the mutex isn't being held, just return success.
if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) { if (addr_value != (holding_thread_handle | Mutex::MutexHasWaitersFlag)) {
@@ -90,6 +90,20 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
return ERR_INVALID_HANDLE; return ERR_INVALID_HANDLE;
} }
// This a workaround where an unknown bug writes the mutex value to give ownership to a cond var
// waiting thread.
if (holding_thread->GetStatus() == ThreadStatus::WaitCondVar) {
if (holding_thread->GetMutexWaitAddress() == address) {
Release(address, holding_thread.get());
addr_value = system.Memory().Read32(address);
if (addr_value == 0)
return RESULT_SUCCESS;
else {
holding_thread = handle_table.Get<Thread>(addr_value & Mutex::MutexOwnerMask);
}
}
}
// Wait until the mutex is released // Wait until the mutex is released
current_thread->SetMutexWaitAddress(address); current_thread->SetMutexWaitAddress(address);
current_thread->SetWaitHandle(requesting_thread_handle); current_thread->SetWaitHandle(requesting_thread_handle);
@@ -105,15 +119,13 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }
ResultCode Mutex::Release(VAddr address) { ResultCode Mutex::Release(VAddr address, Thread* holding_thread) {
// The mutex address must be 4-byte aligned // The mutex address must be 4-byte aligned
if ((address % sizeof(u32)) != 0) { if ((address % sizeof(u32)) != 0) {
return ERR_INVALID_ADDRESS; return ERR_INVALID_ADDRESS;
} }
std::shared_ptr<Thread> current_thread = auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(holding_thread, address);
SharedFrom(system.CurrentScheduler().GetCurrentThread());
auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(current_thread, address);
// There are no more threads waiting for the mutex, release it completely. // There are no more threads waiting for the mutex, release it completely.
if (thread == nullptr) { if (thread == nullptr) {
@@ -122,7 +134,7 @@ ResultCode Mutex::Release(VAddr address) {
} }
// Transfer the ownership of the mutex from the previous owner to the new one. // Transfer the ownership of the mutex from the previous owner to the new one.
TransferMutexOwnership(address, current_thread, thread); TransferMutexOwnership(address, holding_thread, thread.get());
u32 mutex_value = thread->GetWaitHandle(); u32 mutex_value = thread->GetWaitHandle();
@@ -143,7 +155,10 @@ ResultCode Mutex::Release(VAddr address) {
thread->SetWaitHandle(0); thread->SetWaitHandle(0);
thread->SetWaitSynchronizationResult(RESULT_SUCCESS); thread->SetWaitSynchronizationResult(RESULT_SUCCESS);
system.PrepareReschedule(); if (thread->GetProcessorID() >= 0)
system.CpuCore(thread->GetProcessorID()).PrepareReschedule();
if (holding_thread->GetProcessorID() >= 0)
system.CpuCore(holding_thread->GetProcessorID()).PrepareReschedule();
return RESULT_SUCCESS; return RESULT_SUCCESS;
} }

View File

@@ -29,7 +29,7 @@ public:
Handle requesting_thread_handle); Handle requesting_thread_handle);
/// Releases the mutex at the specified address. /// Releases the mutex at the specified address.
ResultCode Release(VAddr address); ResultCode Release(VAddr address, Thread* holding_thread);
private: private:
Core::System& system; Core::System& system;

View File

@@ -587,7 +587,8 @@ static ResultCode ArbitrateUnlock(Core::System& system, VAddr mutex_addr) {
} }
auto* const current_process = system.Kernel().CurrentProcess(); auto* const current_process = system.Kernel().CurrentProcess();
return current_process->GetMutex().Release(mutex_addr); return current_process->GetMutex().Release(mutex_addr,
system.CurrentScheduler().GetCurrentThread());
} }
enum class BreakType : u32 { enum class BreakType : u32 {
@@ -1627,12 +1628,13 @@ static ResultCode WaitProcessWideKeyAtomic(Core::System& system, VAddr mutex_add
std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle); std::shared_ptr<Thread> thread = handle_table.Get<Thread>(thread_handle);
ASSERT(thread); ASSERT(thread);
const auto release_result = current_process->GetMutex().Release(mutex_addr); Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
const auto release_result = current_process->GetMutex().Release(mutex_addr, current_thread);
if (release_result.IsError()) { if (release_result.IsError()) {
return release_result; return release_result;
} }
Thread* current_thread = system.CurrentScheduler().GetCurrentThread();
current_thread->SetCondVarWaitAddress(condition_variable_addr); current_thread->SetCondVarWaitAddress(condition_variable_addr);
current_thread->SetMutexWaitAddress(mutex_addr); current_thread->SetMutexWaitAddress(mutex_addr);
current_thread->SetWaitHandle(thread_handle); current_thread->SetWaitHandle(thread_handle);