diff options
| author | 2023-01-27 15:06:09 -0800 | |
|---|---|---|
| committer | 2023-01-27 15:06:09 -0800 | |
| commit | 32b2a72e7b68427c619d2dae4daef963a826bb9e (patch) | |
| tree | 50129dff54c382ab8dfe7bd1c0af803dcc9cf851 /src | |
| parent | Merge pull request #9685 from liamwhite/minmax (diff) | |
| parent | kernel: split SetAddressKey into user and kernel variants (diff) | |
| download | yuzu-32b2a72e7b68427c619d2dae4daef963a826bb9e.tar.gz yuzu-32b2a72e7b68427c619d2dae4daef963a826bb9e.tar.xz yuzu-32b2a72e7b68427c619d2dae4daef963a826bb9e.zip | |
Merge pull request #9666 from liamwhite/wait-for-me
kernel: fix incorrect locking order in suspension
Diffstat (limited to 'src')
| -rw-r--r-- | src/core/hle/kernel/k_condition_variable.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_light_lock.cpp | 2 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_memory_layout.h | 6 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.cpp | 21 | ||||
| -rw-r--r-- | src/core/hle/kernel/k_thread.h | 24 | ||||
| -rw-r--r-- | src/core/hle/kernel/kernel.cpp | 39 |
6 files changed, 52 insertions, 42 deletions
diff --git a/src/core/hle/kernel/k_condition_variable.cpp b/src/core/hle/kernel/k_condition_variable.cpp index 124149697..0c6b20db3 100644 --- a/src/core/hle/kernel/k_condition_variable.cpp +++ b/src/core/hle/kernel/k_condition_variable.cpp | |||
| @@ -171,7 +171,7 @@ Result KConditionVariable::WaitForAddress(Handle handle, VAddr addr, u32 value) | |||
| 171 | R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); | 171 | R_UNLESS(owner_thread != nullptr, ResultInvalidHandle); |
| 172 | 172 | ||
| 173 | // Update the lock. | 173 | // Update the lock. |
| 174 | cur_thread->SetAddressKey(addr, value); | 174 | cur_thread->SetUserAddressKey(addr, value); |
| 175 | owner_thread->AddWaiter(cur_thread); | 175 | owner_thread->AddWaiter(cur_thread); |
| 176 | 176 | ||
| 177 | // Begin waiting. | 177 | // Begin waiting. |
diff --git a/src/core/hle/kernel/k_light_lock.cpp b/src/core/hle/kernel/k_light_lock.cpp index 43185320d..d791acbe3 100644 --- a/src/core/hle/kernel/k_light_lock.cpp +++ b/src/core/hle/kernel/k_light_lock.cpp | |||
| @@ -68,7 +68,7 @@ bool KLightLock::LockSlowPath(uintptr_t _owner, uintptr_t _cur_thread) { | |||
| 68 | 68 | ||
| 69 | // Add the current thread as a waiter on the owner. | 69 | // Add the current thread as a waiter on the owner. |
| 70 | KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); | 70 | KThread* owner_thread = reinterpret_cast<KThread*>(_owner & ~1ULL); |
| 71 | cur_thread->SetAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); | 71 | cur_thread->SetKernelAddressKey(reinterpret_cast<uintptr_t>(std::addressof(tag))); |
| 72 | owner_thread->AddWaiter(cur_thread); | 72 | owner_thread->AddWaiter(cur_thread); |
| 73 | 73 | ||
| 74 | // Begin waiting to hold the lock. | 74 | // Begin waiting to hold the lock. |
diff --git a/src/core/hle/kernel/k_memory_layout.h b/src/core/hle/kernel/k_memory_layout.h index fd6e1d3e6..17fa1a6ed 100644 --- a/src/core/hle/kernel/k_memory_layout.h +++ b/src/core/hle/kernel/k_memory_layout.h | |||
| @@ -67,9 +67,9 @@ constexpr size_t KernelPageBufferAdditionalSize = 0x33C000; | |||
| 67 | constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + | 67 | constexpr std::size_t KernelResourceSize = KernelPageTableHeapSize + KernelInitialPageHeapSize + |
| 68 | KernelSlabHeapSize + KernelPageBufferHeapSize; | 68 | KernelSlabHeapSize + KernelPageBufferHeapSize; |
| 69 | 69 | ||
| 70 | constexpr bool IsKernelAddressKey(VAddr key) { | 70 | //! NB: Use KThread::GetAddressKeyIsKernel(). |
| 71 | return KernelVirtualAddressSpaceBase <= key && key <= KernelVirtualAddressSpaceLast; | 71 | //! See explanation for deviation of GetAddressKey. |
| 72 | } | 72 | bool IsKernelAddressKey(VAddr key) = delete; |
| 73 | 73 | ||
| 74 | constexpr bool IsKernelAddress(VAddr address) { | 74 | constexpr bool IsKernelAddress(VAddr address) { |
| 75 | return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; | 75 | return KernelVirtualAddressSpaceBase <= address && address < KernelVirtualAddressSpaceEnd; |
diff --git a/src/core/hle/kernel/k_thread.cpp b/src/core/hle/kernel/k_thread.cpp index 21207fe99..84ff3c64b 100644 --- a/src/core/hle/kernel/k_thread.cpp +++ b/src/core/hle/kernel/k_thread.cpp | |||
| @@ -330,7 +330,7 @@ void KThread::Finalize() { | |||
| 330 | KThread* const waiter = std::addressof(*it); | 330 | KThread* const waiter = std::addressof(*it); |
| 331 | 331 | ||
| 332 | // The thread shouldn't be a kernel waiter. | 332 | // The thread shouldn't be a kernel waiter. |
| 333 | ASSERT(!IsKernelAddressKey(waiter->GetAddressKey())); | 333 | ASSERT(!waiter->GetAddressKeyIsKernel()); |
| 334 | 334 | ||
| 335 | // Clear the lock owner. | 335 | // Clear the lock owner. |
| 336 | waiter->SetLockOwner(nullptr); | 336 | waiter->SetLockOwner(nullptr); |
| @@ -763,19 +763,6 @@ void KThread::Continue() { | |||
| 763 | KScheduler::OnThreadStateChanged(kernel, this, old_state); | 763 | KScheduler::OnThreadStateChanged(kernel, this, old_state); |
| 764 | } | 764 | } |
| 765 | 765 | ||
| 766 | void KThread::WaitUntilSuspended() { | ||
| 767 | // Make sure we have a suspend requested. | ||
| 768 | ASSERT(IsSuspendRequested()); | ||
| 769 | |||
| 770 | // Loop until the thread is not executing on any core. | ||
| 771 | for (std::size_t i = 0; i < static_cast<std::size_t>(Core::Hardware::NUM_CPU_CORES); ++i) { | ||
| 772 | KThread* core_thread{}; | ||
| 773 | do { | ||
| 774 | core_thread = kernel.Scheduler(i).GetSchedulerCurrentThread(); | ||
| 775 | } while (core_thread == this); | ||
| 776 | } | ||
| 777 | } | ||
| 778 | |||
| 779 | Result KThread::SetActivity(Svc::ThreadActivity activity) { | 766 | Result KThread::SetActivity(Svc::ThreadActivity activity) { |
| 780 | // Lock ourselves. | 767 | // Lock ourselves. |
| 781 | KScopedLightLock lk(activity_pause_lock); | 768 | KScopedLightLock lk(activity_pause_lock); |
| @@ -897,7 +884,7 @@ void KThread::AddWaiterImpl(KThread* thread) { | |||
| 897 | } | 884 | } |
| 898 | 885 | ||
| 899 | // Keep track of how many kernel waiters we have. | 886 | // Keep track of how many kernel waiters we have. |
| 900 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 887 | if (thread->GetAddressKeyIsKernel()) { |
| 901 | ASSERT((num_kernel_waiters++) >= 0); | 888 | ASSERT((num_kernel_waiters++) >= 0); |
| 902 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 889 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 903 | } | 890 | } |
| @@ -911,7 +898,7 @@ void KThread::RemoveWaiterImpl(KThread* thread) { | |||
| 911 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); | 898 | ASSERT(kernel.GlobalSchedulerContext().IsLocked()); |
| 912 | 899 | ||
| 913 | // Keep track of how many kernel waiters we have. | 900 | // Keep track of how many kernel waiters we have. |
| 914 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 901 | if (thread->GetAddressKeyIsKernel()) { |
| 915 | ASSERT((num_kernel_waiters--) > 0); | 902 | ASSERT((num_kernel_waiters--) > 0); |
| 916 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 903 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 917 | } | 904 | } |
| @@ -987,7 +974,7 @@ KThread* KThread::RemoveWaiterByKey(s32* out_num_waiters, VAddr key) { | |||
| 987 | KThread* thread = std::addressof(*it); | 974 | KThread* thread = std::addressof(*it); |
| 988 | 975 | ||
| 989 | // Keep track of how many kernel waiters we have. | 976 | // Keep track of how many kernel waiters we have. |
| 990 | if (IsKernelAddressKey(thread->GetAddressKey())) { | 977 | if (thread->GetAddressKeyIsKernel()) { |
| 991 | ASSERT((num_kernel_waiters--) > 0); | 978 | ASSERT((num_kernel_waiters--) > 0); |
| 992 | KScheduler::SetSchedulerUpdateNeeded(kernel); | 979 | KScheduler::SetSchedulerUpdateNeeded(kernel); |
| 993 | } | 980 | } |
diff --git a/src/core/hle/kernel/k_thread.h b/src/core/hle/kernel/k_thread.h index 7cd94a340..9d771de0e 100644 --- a/src/core/hle/kernel/k_thread.h +++ b/src/core/hle/kernel/k_thread.h | |||
| @@ -214,8 +214,6 @@ public: | |||
| 214 | 214 | ||
| 215 | void Continue(); | 215 | void Continue(); |
| 216 | 216 | ||
| 217 | void WaitUntilSuspended(); | ||
| 218 | |||
| 219 | constexpr void SetSyncedIndex(s32 index) { | 217 | constexpr void SetSyncedIndex(s32 index) { |
| 220 | synced_index = index; | 218 | synced_index = index; |
| 221 | } | 219 | } |
| @@ -607,13 +605,30 @@ public: | |||
| 607 | return address_key_value; | 605 | return address_key_value; |
| 608 | } | 606 | } |
| 609 | 607 | ||
| 610 | void SetAddressKey(VAddr key) { | 608 | [[nodiscard]] bool GetAddressKeyIsKernel() const { |
| 609 | return address_key_is_kernel; | ||
| 610 | } | ||
| 611 | |||
| 612 | //! NB: intentional deviation from official kernel. | ||
| 613 | // | ||
| 614 | // Separate SetAddressKey into user and kernel versions | ||
| 615 | // to cope with arbitrary host pointers making their way | ||
| 616 | // into things. | ||
| 617 | |||
| 618 | void SetUserAddressKey(VAddr key) { | ||
| 611 | address_key = key; | 619 | address_key = key; |
| 620 | address_key_is_kernel = false; | ||
| 612 | } | 621 | } |
| 613 | 622 | ||
| 614 | void SetAddressKey(VAddr key, u32 val) { | 623 | void SetUserAddressKey(VAddr key, u32 val) { |
| 615 | address_key = key; | 624 | address_key = key; |
| 616 | address_key_value = val; | 625 | address_key_value = val; |
| 626 | address_key_is_kernel = false; | ||
| 627 | } | ||
| 628 | |||
| 629 | void SetKernelAddressKey(VAddr key) { | ||
| 630 | address_key = key; | ||
| 631 | address_key_is_kernel = true; | ||
| 617 | } | 632 | } |
| 618 | 633 | ||
| 619 | void ClearWaitQueue() { | 634 | void ClearWaitQueue() { |
| @@ -772,6 +787,7 @@ private: | |||
| 772 | bool debug_attached{}; | 787 | bool debug_attached{}; |
| 773 | s8 priority_inheritance_count{}; | 788 | s8 priority_inheritance_count{}; |
| 774 | bool resource_limit_release_hint{}; | 789 | bool resource_limit_release_hint{}; |
| 790 | bool address_key_is_kernel{}; | ||
| 775 | StackParameters stack_parameters{}; | 791 | StackParameters stack_parameters{}; |
| 776 | Common::SpinLock context_guard{}; | 792 | Common::SpinLock context_guard{}; |
| 777 | 793 | ||
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp index 1fb25f221..d9eafe261 100644 --- a/src/core/hle/kernel/kernel.cpp +++ b/src/core/hle/kernel/kernel.cpp | |||
| @@ -1198,28 +1198,35 @@ void KernelCore::Suspend(bool suspended) { | |||
| 1198 | const bool should_suspend{exception_exited || suspended}; | 1198 | const bool should_suspend{exception_exited || suspended}; |
| 1199 | const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; | 1199 | const auto activity = should_suspend ? ProcessActivity::Paused : ProcessActivity::Runnable; |
| 1200 | 1200 | ||
| 1201 | std::vector<KScopedAutoObject<KThread>> process_threads; | 1201 | //! This refers to the application process, not the current process. |
| 1202 | { | 1202 | KScopedAutoObject<KProcess> process = CurrentProcess(); |
| 1203 | KScopedSchedulerLock sl{*this}; | 1203 | if (process.IsNull()) { |
| 1204 | return; | ||
| 1205 | } | ||
| 1204 | 1206 | ||
| 1205 | if (auto* process = CurrentProcess(); process != nullptr) { | 1207 | // Set the new activity. |
| 1206 | process->SetActivity(activity); | 1208 | process->SetActivity(activity); |
| 1207 | 1209 | ||
| 1208 | if (!should_suspend) { | 1210 | // Wait for process execution to stop. |
| 1209 | // Runnable now; no need to wait. | 1211 | bool must_wait{should_suspend}; |
| 1210 | return; | 1212 | |
| 1211 | } | 1213 | // KernelCore::Suspend must be called from locked context, or we |
| 1214 | // could race another call to SetActivity, interfering with waiting. | ||
| 1215 | while (must_wait) { | ||
| 1216 | KScopedSchedulerLock sl{*this}; | ||
| 1217 | |||
| 1218 | // Assume that all threads have finished running. | ||
| 1219 | must_wait = false; | ||
| 1212 | 1220 | ||
| 1213 | for (auto* thread : process->GetThreadList()) { | 1221 | for (auto i = 0; i < static_cast<s32>(Core::Hardware::NUM_CPU_CORES); ++i) { |
| 1214 | process_threads.emplace_back(thread); | 1222 | if (Scheduler(i).GetSchedulerCurrentThread()->GetOwnerProcess() == |
| 1223 | process.GetPointerUnsafe()) { | ||
| 1224 | // A thread has not finished running yet. | ||
| 1225 | // Continue waiting. | ||
| 1226 | must_wait = true; | ||
| 1215 | } | 1227 | } |
| 1216 | } | 1228 | } |
| 1217 | } | 1229 | } |
| 1218 | |||
| 1219 | // Wait for execution to stop. | ||
| 1220 | for (auto& thread : process_threads) { | ||
| 1221 | thread->WaitUntilSuspended(); | ||
| 1222 | } | ||
| 1223 | } | 1230 | } |
| 1224 | 1231 | ||
| 1225 | void KernelCore::ShutdownCores() { | 1232 | void KernelCore::ShutdownCores() { |