Kernel: Workaround to mutex corruption
# Conflicts: # src/core/hle/kernel/mutex.cpp
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user