mirror of
				https://git.zaroz.cloud/nintendo-back-up/yuzu/yuzu-mainline.git
				synced 2025-03-21 01:53:15 +00:00 
			
		
		
		
	Kernel: Implemented mutex priority inheritance.
Verified with a hwtest and implemented based on reverse engineering. Thread A's priority will get bumped to the highest priority among all the threads that are waiting for a mutex that A holds. Once A releases the mutex and ownership is transferred to B, A's priority will return to normal and B's priority will be bumped.
This commit is contained in:
		
							parent
							
								
									a70ed9c8ae
								
							
						
					
					
						commit
						46572d027d
					
				@ -18,13 +18,13 @@ 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<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(VAddr mutex_addr) {
 | 
					static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(
 | 
				
			||||||
    auto& thread_list = Core::System::GetInstance().Scheduler().GetThreadList();
 | 
					    SharedPtr<Thread> current_thread, VAddr mutex_addr) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    SharedPtr<Thread> highest_priority_thread;
 | 
					    SharedPtr<Thread> highest_priority_thread;
 | 
				
			||||||
    u32 num_waiters = 0;
 | 
					    u32 num_waiters = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    for (auto& thread : thread_list) {
 | 
					    for (auto& thread : current_thread->wait_mutex_threads) {
 | 
				
			||||||
        if (thread->mutex_wait_address != mutex_addr)
 | 
					        if (thread->mutex_wait_address != mutex_addr)
 | 
				
			||||||
            continue;
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -40,6 +40,21 @@ static std::pair<SharedPtr<Thread>, u32> GetHighestPriorityMutexWaitingThread(VA
 | 
				
			|||||||
    return {highest_priority_thread, num_waiters};
 | 
					    return {highest_priority_thread, num_waiters};
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Update the mutex owner field of all threads waiting on the mutex to point to the new owner.
 | 
				
			||||||
 | 
					static void TransferMutexOwnership(VAddr mutex_addr, SharedPtr<Thread> current_thread,
 | 
				
			||||||
 | 
					                                   SharedPtr<Thread> new_owner) {
 | 
				
			||||||
 | 
					    auto threads = current_thread->wait_mutex_threads;
 | 
				
			||||||
 | 
					    for (auto& thread : threads) {
 | 
				
			||||||
 | 
					        if (thread->mutex_wait_address != mutex_addr)
 | 
				
			||||||
 | 
					            continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ASSERT(thread->lock_owner == current_thread);
 | 
				
			||||||
 | 
					        current_thread->RemoveMutexWaiter(thread);
 | 
				
			||||||
 | 
					        if (new_owner != thread)
 | 
				
			||||||
 | 
					            new_owner->AddMutexWaiter(thread);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
 | 
					ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
 | 
				
			||||||
                             Handle requesting_thread_handle) {
 | 
					                             Handle requesting_thread_handle) {
 | 
				
			||||||
    // The mutex address must be 4-byte aligned
 | 
					    // The mutex address must be 4-byte aligned
 | 
				
			||||||
@ -65,11 +80,14 @@ ResultCode Mutex::TryAcquire(VAddr address, Handle holding_thread_handle,
 | 
				
			|||||||
        return ERR_INVALID_HANDLE;
 | 
					        return ERR_INVALID_HANDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Wait until the mutex is released
 | 
					    // Wait until the mutex is released
 | 
				
			||||||
    requesting_thread->mutex_wait_address = address;
 | 
					    GetCurrentThread()->mutex_wait_address = address;
 | 
				
			||||||
    requesting_thread->wait_handle = requesting_thread_handle;
 | 
					    GetCurrentThread()->wait_handle = requesting_thread_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    requesting_thread->status = THREADSTATUS_WAIT_MUTEX;
 | 
					    GetCurrentThread()->status = THREADSTATUS_WAIT_MUTEX;
 | 
				
			||||||
    requesting_thread->wakeup_callback = nullptr;
 | 
					    GetCurrentThread()->wakeup_callback = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Update the lock holder thread's priority to prevent priority inversion.
 | 
				
			||||||
 | 
					    holding_thread->AddMutexWaiter(GetCurrentThread());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Core::System::GetInstance().PrepareReschedule();
 | 
					    Core::System::GetInstance().PrepareReschedule();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -82,14 +100,18 @@ ResultCode Mutex::Release(VAddr address) {
 | 
				
			|||||||
        return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
 | 
					        return ResultCode(ErrorModule::Kernel, ErrCodes::MisalignedAddress);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(address);
 | 
					    auto [thread, num_waiters] = GetHighestPriorityMutexWaitingThread(GetCurrentThread(), 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) {
 | 
				
			||||||
 | 
					        ASSERT(GetCurrentThread()->wait_mutex_threads.empty());
 | 
				
			||||||
        Memory::Write32(address, 0);
 | 
					        Memory::Write32(address, 0);
 | 
				
			||||||
        return RESULT_SUCCESS;
 | 
					        return RESULT_SUCCESS;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Transfer the ownership of the mutex from the previous owner to the new one.
 | 
				
			||||||
 | 
					    TransferMutexOwnership(address, GetCurrentThread(), thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    u32 mutex_value = thread->wait_handle;
 | 
					    u32 mutex_value = thread->wait_handle;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (num_waiters >= 2) {
 | 
					    if (num_waiters >= 2) {
 | 
				
			||||||
@ -103,6 +125,7 @@ ResultCode Mutex::Release(VAddr address) {
 | 
				
			|||||||
    ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
 | 
					    ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
 | 
				
			||||||
    thread->ResumeFromWait();
 | 
					    thread->ResumeFromWait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    thread->lock_owner = nullptr;
 | 
				
			||||||
    thread->condvar_wait_address = 0;
 | 
					    thread->condvar_wait_address = 0;
 | 
				
			||||||
    thread->mutex_wait_address = 0;
 | 
					    thread->mutex_wait_address = 0;
 | 
				
			||||||
    thread->wait_handle = 0;
 | 
					    thread->wait_handle = 0;
 | 
				
			||||||
 | 
				
			|||||||
@ -621,6 +621,8 @@ static ResultCode WaitProcessWideKeyAtomic(VAddr mutex_addr, VAddr condition_var
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    current_thread->WakeAfterDelay(nano_seconds);
 | 
					    current_thread->WakeAfterDelay(nano_seconds);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Note: Deliberately don't attempt to inherit the lock owner's priority.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Core::System::GetInstance().PrepareReschedule();
 | 
					    Core::System::GetInstance().PrepareReschedule();
 | 
				
			||||||
    return RESULT_SUCCESS;
 | 
					    return RESULT_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -651,6 +653,11 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
 | 
				
			|||||||
            ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
 | 
					            ASSERT(thread->status == THREADSTATUS_WAIT_MUTEX);
 | 
				
			||||||
            thread->ResumeFromWait();
 | 
					            thread->ResumeFromWait();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto lock_owner = thread->lock_owner;
 | 
				
			||||||
 | 
					            if (lock_owner)
 | 
				
			||||||
 | 
					                lock_owner->RemoveMutexWaiter(thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            thread->lock_owner = nullptr;
 | 
				
			||||||
            thread->mutex_wait_address = 0;
 | 
					            thread->mutex_wait_address = 0;
 | 
				
			||||||
            thread->condvar_wait_address = 0;
 | 
					            thread->condvar_wait_address = 0;
 | 
				
			||||||
            thread->wait_handle = 0;
 | 
					            thread->wait_handle = 0;
 | 
				
			||||||
@ -666,6 +673,8 @@ static ResultCode SignalProcessWideKey(VAddr condition_variable_addr, s32 target
 | 
				
			|||||||
            // Signal that the mutex now has a waiting thread.
 | 
					            // Signal that the mutex now has a waiting thread.
 | 
				
			||||||
            Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
 | 
					            Memory::Write32(thread->mutex_wait_address, mutex_val | Mutex::MutexHasWaitersFlag);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            owner->AddMutexWaiter(thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            Core::System::GetInstance().PrepareReschedule();
 | 
					            Core::System::GetInstance().PrepareReschedule();
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -129,6 +129,11 @@ static void ThreadWakeupCallback(u64 thread_handle, int cycles_late) {
 | 
				
			|||||||
        thread->mutex_wait_address = 0;
 | 
					        thread->mutex_wait_address = 0;
 | 
				
			||||||
        thread->condvar_wait_address = 0;
 | 
					        thread->condvar_wait_address = 0;
 | 
				
			||||||
        thread->wait_handle = 0;
 | 
					        thread->wait_handle = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        auto lock_owner = thread->lock_owner;
 | 
				
			||||||
 | 
					        // Threads waking up by timeout from WaitProcessWideKey do not perform priority inheritance
 | 
				
			||||||
 | 
					        // and don't have a lock owner.
 | 
				
			||||||
 | 
					        ASSERT(lock_owner == nullptr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (resume)
 | 
					    if (resume)
 | 
				
			||||||
@ -325,8 +330,8 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point,
 | 
				
			|||||||
void Thread::SetPriority(u32 priority) {
 | 
					void Thread::SetPriority(u32 priority) {
 | 
				
			||||||
    ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
 | 
					    ASSERT_MSG(priority <= THREADPRIO_LOWEST && priority >= THREADPRIO_HIGHEST,
 | 
				
			||||||
               "Invalid priority value.");
 | 
					               "Invalid priority value.");
 | 
				
			||||||
    Core::System::GetInstance().Scheduler().SetThreadPriority(this, priority);
 | 
					    nominal_priority = priority;
 | 
				
			||||||
    nominal_priority = current_priority = priority;
 | 
					    UpdatePriority();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Thread::BoostPriority(u32 priority) {
 | 
					void Thread::BoostPriority(u32 priority) {
 | 
				
			||||||
@ -376,6 +381,38 @@ VAddr Thread::GetCommandBufferAddress() const {
 | 
				
			|||||||
    return GetTLSAddress() + CommandHeaderOffset;
 | 
					    return GetTLSAddress() + CommandHeaderOffset;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Thread::AddMutexWaiter(SharedPtr<Thread> thread) {
 | 
				
			||||||
 | 
					    thread->lock_owner = this;
 | 
				
			||||||
 | 
					    wait_mutex_threads.emplace_back(std::move(thread));
 | 
				
			||||||
 | 
					    UpdatePriority();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Thread::RemoveMutexWaiter(SharedPtr<Thread> thread) {
 | 
				
			||||||
 | 
					    boost::remove_erase(wait_mutex_threads, thread);
 | 
				
			||||||
 | 
					    thread->lock_owner = nullptr;
 | 
				
			||||||
 | 
					    UpdatePriority();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Thread::UpdatePriority() {
 | 
				
			||||||
 | 
					    // Find the highest priority among all the threads that are waiting for this thread's lock
 | 
				
			||||||
 | 
					    u32 new_priority = nominal_priority;
 | 
				
			||||||
 | 
					    for (const auto& thread : wait_mutex_threads) {
 | 
				
			||||||
 | 
					        if (thread->nominal_priority < new_priority)
 | 
				
			||||||
 | 
					            new_priority = thread->nominal_priority;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (new_priority == current_priority)
 | 
				
			||||||
 | 
					        return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Core::System::GetInstance().Scheduler().SetThreadPriority(this, new_priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    current_priority = new_priority;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Recursively update the priority of the thread that depends on the priority of this one.
 | 
				
			||||||
 | 
					    if (lock_owner)
 | 
				
			||||||
 | 
					        lock_owner->UpdatePriority();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
					////////////////////////////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
 | 
				
			|||||||
@ -109,6 +109,15 @@ public:
 | 
				
			|||||||
     */
 | 
					     */
 | 
				
			||||||
    void BoostPriority(u32 priority);
 | 
					    void BoostPriority(u32 priority);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Adds a thread to the list of threads that are waiting for a lock held by this thread.
 | 
				
			||||||
 | 
					    void AddMutexWaiter(SharedPtr<Thread> thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Removes a thread from the list of threads that are waiting for a lock held by this thread.
 | 
				
			||||||
 | 
					    void RemoveMutexWaiter(SharedPtr<Thread> thread);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Recalculates the current priority taking into account priority inheritance.
 | 
				
			||||||
 | 
					    void UpdatePriority();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * Gets the thread's thread ID
 | 
					     * Gets the thread's thread ID
 | 
				
			||||||
     * @return The thread's ID
 | 
					     * @return The thread's ID
 | 
				
			||||||
@ -205,6 +214,12 @@ public:
 | 
				
			|||||||
    // passed to WaitSynchronization1/N.
 | 
					    // passed to WaitSynchronization1/N.
 | 
				
			||||||
    std::vector<SharedPtr<WaitObject>> wait_objects;
 | 
					    std::vector<SharedPtr<WaitObject>> wait_objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// List of threads that are waiting for a mutex that is held by this thread.
 | 
				
			||||||
 | 
					    std::vector<SharedPtr<Thread>> wait_mutex_threads;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /// Thread that owns the lock that this thread is waiting for.
 | 
				
			||||||
 | 
					    SharedPtr<Thread> lock_owner;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // If waiting on a ConditionVariable, this is the ConditionVariable  address
 | 
					    // If waiting on a ConditionVariable, this is the ConditionVariable  address
 | 
				
			||||||
    VAddr condvar_wait_address;
 | 
					    VAddr condvar_wait_address;
 | 
				
			||||||
    VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
 | 
					    VAddr mutex_wait_address; ///< If waiting on a Mutex, this is the mutex address
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user