Merge pull request #5848 from ogniK5377/k-resourcelimit
kernel: Rewrite resource limit to be more accurate
This commit is contained in:
		| @@ -160,9 +160,12 @@ add_library(core STATIC | ||||
|     hle/kernel/k_affinity_mask.h | ||||
|     hle/kernel/k_condition_variable.cpp | ||||
|     hle/kernel/k_condition_variable.h | ||||
|     hle/kernel/k_light_condition_variable.h | ||||
|     hle/kernel/k_light_lock.cpp | ||||
|     hle/kernel/k_light_lock.h | ||||
|     hle/kernel/k_priority_queue.h | ||||
|     hle/kernel/k_resource_limit.cpp | ||||
|     hle/kernel/k_resource_limit.h | ||||
|     hle/kernel/k_scheduler.cpp | ||||
|     hle/kernel/k_scheduler.h | ||||
|     hle/kernel/k_scheduler_lock.h | ||||
| @@ -203,8 +206,6 @@ add_library(core STATIC | ||||
|     hle/kernel/process_capability.h | ||||
|     hle/kernel/readable_event.cpp | ||||
|     hle/kernel/readable_event.h | ||||
|     hle/kernel/resource_limit.cpp | ||||
|     hle/kernel/resource_limit.h | ||||
|     hle/kernel/server_port.cpp | ||||
|     hle/kernel/server_port.h | ||||
|     hle/kernel/server_session.cpp | ||||
|   | ||||
							
								
								
									
										57
									
								
								src/core/hle/kernel/k_light_condition_variable.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								src/core/hle/kernel/k_light_condition_variable.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/k_scheduler.h" | ||||
| #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||||
| #include "core/hle/kernel/k_thread_queue.h" | ||||
| #include "core/hle/kernel/time_manager.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| class KernelCore; | ||||
|  | ||||
| class KLightConditionVariable { | ||||
| public: | ||||
|     explicit KLightConditionVariable(KernelCore& kernel) : thread_queue(kernel), kernel(kernel) {} | ||||
|  | ||||
|     void Wait(KLightLock* lock, s64 timeout = -1) { | ||||
|         WaitImpl(lock, timeout); | ||||
|         lock->Lock(); | ||||
|     } | ||||
|  | ||||
|     void Broadcast() { | ||||
|         KScopedSchedulerLock lk{kernel}; | ||||
|         while (thread_queue.WakeupFrontThread() != nullptr) { | ||||
|             // We want to signal all threads, and so should continue waking up until there's nothing | ||||
|             // to wake. | ||||
|         } | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     void WaitImpl(KLightLock* lock, s64 timeout) { | ||||
|         KThread* owner = GetCurrentThreadPointer(kernel); | ||||
|  | ||||
|         // Sleep the thread. | ||||
|         { | ||||
|             KScopedSchedulerLockAndSleep lk(kernel, owner, timeout); | ||||
|             lock->Unlock(); | ||||
|  | ||||
|             if (!thread_queue.SleepThread(owner)) { | ||||
|                 lk.CancelSleep(); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Cancel the task that the sleep setup. | ||||
|         kernel.TimeManager().UnscheduleTimeEvent(owner); | ||||
|     } | ||||
|     KThreadQueue thread_queue; | ||||
|     KernelCore& kernel; | ||||
| }; | ||||
| } // namespace Kernel | ||||
							
								
								
									
										152
									
								
								src/core/hle/kernel/k_resource_limit.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/core/hle/kernel/k_resource_limit.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,152 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #include "common/assert.h" | ||||
| #include "core/core.h" | ||||
| #include "core/core_timing.h" | ||||
| #include "core/core_timing_util.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/svc_results.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| constexpr s64 DefaultTimeout = 10000000000; // 10 seconds | ||||
|  | ||||
| KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system) | ||||
|     : Object{kernel}, lock{kernel}, cond_var{kernel}, kernel{kernel}, system(system) {} | ||||
| KResourceLimit::~KResourceLimit() = default; | ||||
|  | ||||
| s64 KResourceLimit::GetLimitValue(LimitableResource which) const { | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     s64 value{}; | ||||
|     { | ||||
|         KScopedLightLock lk{lock}; | ||||
|         value = limit_values[index]; | ||||
|         ASSERT(value >= 0); | ||||
|         ASSERT(current_values[index] <= limit_values[index]); | ||||
|         ASSERT(current_hints[index] <= current_values[index]); | ||||
|     } | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| s64 KResourceLimit::GetCurrentValue(LimitableResource which) const { | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     s64 value{}; | ||||
|     { | ||||
|         KScopedLightLock lk{lock}; | ||||
|         value = current_values[index]; | ||||
|         ASSERT(value >= 0); | ||||
|         ASSERT(current_values[index] <= limit_values[index]); | ||||
|         ASSERT(current_hints[index] <= current_values[index]); | ||||
|     } | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| s64 KResourceLimit::GetPeakValue(LimitableResource which) const { | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     s64 value{}; | ||||
|     { | ||||
|         KScopedLightLock lk{lock}; | ||||
|         value = peak_values[index]; | ||||
|         ASSERT(value >= 0); | ||||
|         ASSERT(current_values[index] <= limit_values[index]); | ||||
|         ASSERT(current_hints[index] <= current_values[index]); | ||||
|     } | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| s64 KResourceLimit::GetFreeValue(LimitableResource which) const { | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     s64 value{}; | ||||
|     { | ||||
|         KScopedLightLock lk(lock); | ||||
|         ASSERT(current_values[index] >= 0); | ||||
|         ASSERT(current_values[index] <= limit_values[index]); | ||||
|         ASSERT(current_hints[index] <= current_values[index]); | ||||
|         value = limit_values[index] - current_values[index]; | ||||
|     } | ||||
|  | ||||
|     return value; | ||||
| } | ||||
|  | ||||
| ResultCode KResourceLimit::SetLimitValue(LimitableResource which, s64 value) { | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     KScopedLightLock lk(lock); | ||||
|     R_UNLESS(current_values[index] <= value, Svc::ResultInvalidState); | ||||
|  | ||||
|     limit_values[index] = value; | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| bool KResourceLimit::Reserve(LimitableResource which, s64 value) { | ||||
|     return Reserve(which, value, system.CoreTiming().GetGlobalTimeNs().count() + DefaultTimeout); | ||||
| } | ||||
|  | ||||
| bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { | ||||
|     ASSERT(value >= 0); | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     KScopedLightLock lk(lock); | ||||
|  | ||||
|     ASSERT(current_hints[index] <= current_values[index]); | ||||
|     if (current_hints[index] >= limit_values[index]) { | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     // Loop until we reserve or run out of time. | ||||
|     while (true) { | ||||
|         ASSERT(current_values[index] <= limit_values[index]); | ||||
|         ASSERT(current_hints[index] <= current_values[index]); | ||||
|  | ||||
|         // If we would overflow, don't allow to succeed. | ||||
|         if (current_values[index] + value <= current_values[index]) { | ||||
|             break; | ||||
|         } | ||||
|  | ||||
|         if (current_values[index] + value <= limit_values[index]) { | ||||
|             current_values[index] += value; | ||||
|             current_hints[index] += value; | ||||
|             peak_values[index] = std::max(peak_values[index], current_values[index]); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (current_hints[index] + value <= limit_values[index] && | ||||
|             (timeout < 0 || system.CoreTiming().GetGlobalTimeNs().count() < timeout)) { | ||||
|             waiter_count++; | ||||
|             cond_var.Wait(&lock, timeout); | ||||
|             waiter_count--; | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| void KResourceLimit::Release(LimitableResource which, s64 value) { | ||||
|     Release(which, value, value); | ||||
| } | ||||
|  | ||||
| void KResourceLimit::Release(LimitableResource which, s64 value, s64 hint) { | ||||
|     ASSERT(value >= 0); | ||||
|     ASSERT(hint >= 0); | ||||
|  | ||||
|     const auto index = static_cast<std::size_t>(which); | ||||
|     KScopedLightLock lk(lock); | ||||
|     ASSERT(current_values[index] <= limit_values[index]); | ||||
|     ASSERT(current_hints[index] <= current_values[index]); | ||||
|     ASSERT(value <= current_values[index]); | ||||
|     ASSERT(hint <= current_hints[index]); | ||||
|  | ||||
|     current_values[index] -= value; | ||||
|     current_hints[index] -= hint; | ||||
|  | ||||
|     if (waiter_count != 0) { | ||||
|         cond_var.Broadcast(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| } // namespace Kernel | ||||
							
								
								
									
										81
									
								
								src/core/hle/kernel/k_resource_limit.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/core/hle/kernel/k_resource_limit.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,81 @@ | ||||
| // Copyright 2020 yuzu Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| // This file references various implementation details from Atmosphere, an open-source firmware for | ||||
| // the Nintendo Switch. Copyright 2018-2020 Atmosphere-NX. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/k_light_condition_variable.h" | ||||
| #include "core/hle/kernel/k_light_lock.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
|  | ||||
| union ResultCode; | ||||
|  | ||||
| namespace Core { | ||||
| class System; | ||||
| } | ||||
|  | ||||
| namespace Kernel { | ||||
| class KernelCore; | ||||
| enum class LimitableResource : u32 { | ||||
|     PhysicalMemory = 0, | ||||
|     Threads = 1, | ||||
|     Events = 2, | ||||
|     TransferMemory = 3, | ||||
|     Sessions = 4, | ||||
|  | ||||
|     Count, | ||||
| }; | ||||
|  | ||||
| constexpr bool IsValidResourceType(LimitableResource type) { | ||||
|     return type < LimitableResource::Count; | ||||
| } | ||||
|  | ||||
| class KResourceLimit final : public Object { | ||||
| public: | ||||
|     explicit KResourceLimit(KernelCore& kernel, Core::System& system); | ||||
|     ~KResourceLimit(); | ||||
|  | ||||
|     s64 GetLimitValue(LimitableResource which) const; | ||||
|     s64 GetCurrentValue(LimitableResource which) const; | ||||
|     s64 GetPeakValue(LimitableResource which) const; | ||||
|     s64 GetFreeValue(LimitableResource which) const; | ||||
|  | ||||
|     ResultCode SetLimitValue(LimitableResource which, s64 value); | ||||
|  | ||||
|     bool Reserve(LimitableResource which, s64 value); | ||||
|     bool Reserve(LimitableResource which, s64 value, s64 timeout); | ||||
|     void Release(LimitableResource which, s64 value); | ||||
|     void Release(LimitableResource which, s64 value, s64 hint); | ||||
|  | ||||
|     std::string GetTypeName() const override { | ||||
|         return "KResourceLimit"; | ||||
|     } | ||||
|     std::string GetName() const override { | ||||
|         return GetTypeName(); | ||||
|     } | ||||
|  | ||||
|     static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
|  | ||||
|     virtual void Finalize() override {} | ||||
|  | ||||
| private: | ||||
|     using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>; | ||||
|     ResourceArray limit_values{}; | ||||
|     ResourceArray current_values{}; | ||||
|     ResourceArray current_hints{}; | ||||
|     ResourceArray peak_values{}; | ||||
|     mutable KLightLock lock; | ||||
|     s32 waiter_count{}; | ||||
|     KLightConditionVariable cond_var; | ||||
|     KernelCore& kernel; | ||||
|     Core::System& system; | ||||
| }; | ||||
| } // namespace Kernel | ||||
| @@ -21,6 +21,7 @@ | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/k_condition_variable.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/k_scheduler.h" | ||||
| #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||||
| #include "core/hle/kernel/k_thread.h" | ||||
| @@ -29,7 +30,6 @@ | ||||
| #include "core/hle/kernel/memory/memory_layout.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/svc_results.h" | ||||
| #include "core/hle/kernel/time_manager.h" | ||||
| #include "core/hle/result.h" | ||||
| @@ -247,7 +247,7 @@ void KThread::Finalize() { | ||||
|     // Decrement the parent process's thread count. | ||||
|     if (parent != nullptr) { | ||||
|         parent->DecrementThreadCount(); | ||||
|         parent->GetResourceLimit()->Release(ResourceType::Threads, 1); | ||||
|         parent->GetResourceLimit()->Release(LimitableResource::Threads, 1); | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ | ||||
| #include "core/hle/kernel/client_port.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/k_scheduler.h" | ||||
| #include "core/hle/kernel/k_thread.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| @@ -36,7 +37,6 @@ | ||||
| #include "core/hle/kernel/memory/slab_heap.h" | ||||
| #include "core/hle/kernel/physical_core.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/service_thread.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/time_manager.h" | ||||
| @@ -66,7 +66,7 @@ struct KernelCore::Impl { | ||||
|         is_phantom_mode_for_singlecore = false; | ||||
|  | ||||
|         InitializePhysicalCores(); | ||||
|         InitializeSystemResourceLimit(kernel); | ||||
|         InitializeSystemResourceLimit(kernel, system); | ||||
|         InitializeMemoryLayout(); | ||||
|         InitializePreemption(kernel); | ||||
|         InitializeSchedulers(); | ||||
| @@ -131,19 +131,19 @@ struct KernelCore::Impl { | ||||
|     } | ||||
|  | ||||
|     // Creates the default system resource limit | ||||
|     void InitializeSystemResourceLimit(KernelCore& kernel) { | ||||
|         system_resource_limit = ResourceLimit::Create(kernel); | ||||
|     void InitializeSystemResourceLimit(KernelCore& kernel, Core::System& system) { | ||||
|         system_resource_limit = std::make_shared<KResourceLimit>(kernel, system); | ||||
|  | ||||
|         // If setting the default system values fails, then something seriously wrong has occurred. | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x100000000) | ||||
|         ASSERT(system_resource_limit->SetLimitValue(LimitableResource::PhysicalMemory, 0x100000000) | ||||
|                    .IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Threads, 800).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Events, 700).IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemory, 200) | ||||
|                    .IsSuccess()); | ||||
|         ASSERT(system_resource_limit->SetLimitValue(LimitableResource::Sessions, 900).IsSuccess()); | ||||
|  | ||||
|         if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) || | ||||
|             !system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) { | ||||
|         if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemory, 0x60000)) { | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|     } | ||||
| @@ -320,7 +320,7 @@ struct KernelCore::Impl { | ||||
|     std::unique_ptr<Kernel::GlobalSchedulerContext> global_scheduler_context; | ||||
|     Kernel::TimeManager time_manager; | ||||
|  | ||||
|     std::shared_ptr<ResourceLimit> system_resource_limit; | ||||
|     std::shared_ptr<KResourceLimit> system_resource_limit; | ||||
|  | ||||
|     std::shared_ptr<Core::Timing::EventType> preemption_event; | ||||
|  | ||||
| @@ -390,7 +390,7 @@ void KernelCore::Shutdown() { | ||||
|     impl->Shutdown(); | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ResourceLimit> KernelCore::GetSystemResourceLimit() const { | ||||
| std::shared_ptr<KResourceLimit> KernelCore::GetSystemResourceLimit() const { | ||||
|     return impl->system_resource_limit; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -38,7 +38,7 @@ class GlobalSchedulerContext; | ||||
| class HandleTable; | ||||
| class PhysicalCore; | ||||
| class Process; | ||||
| class ResourceLimit; | ||||
| class KResourceLimit; | ||||
| class KScheduler; | ||||
| class SharedMemory; | ||||
| class ServiceThread; | ||||
| @@ -85,7 +85,7 @@ public: | ||||
|     void Shutdown(); | ||||
|  | ||||
|     /// Retrieves a shared pointer to the system resource limit instance. | ||||
|     std::shared_ptr<ResourceLimit> GetSystemResourceLimit() const; | ||||
|     std::shared_ptr<KResourceLimit> GetSystemResourceLimit() const; | ||||
|  | ||||
|     /// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table. | ||||
|     std::shared_ptr<KThread> RetrieveThreadFromGlobalHandleTable(Handle handle) const; | ||||
|   | ||||
| @@ -7,6 +7,7 @@ | ||||
| #include "common/scope_exit.h" | ||||
| #include "core/core.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| #include "core/hle/kernel/memory/address_space_info.h" | ||||
| #include "core/hle/kernel/memory/memory_block.h" | ||||
| @@ -15,7 +16,6 @@ | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/memory/system_control.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/memory.h" | ||||
|  | ||||
| namespace Kernel::Memory { | ||||
| @@ -414,7 +414,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { | ||||
|     const std::size_t remaining_pages{remaining_size / PageSize}; | ||||
|  | ||||
|     if (process->GetResourceLimit() && | ||||
|         !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) { | ||||
|         !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, remaining_size)) { | ||||
|         return ERR_RESOURCE_LIMIT_EXCEEDED; | ||||
|     } | ||||
|  | ||||
| @@ -422,7 +422,7 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) { | ||||
|     { | ||||
|         auto block_guard = detail::ScopeExit([&] { | ||||
|             system.Kernel().MemoryManager().Free(page_linked_list, remaining_pages, memory_pool); | ||||
|             process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, remaining_size); | ||||
|             process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, remaining_size); | ||||
|         }); | ||||
|  | ||||
|         CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages, | ||||
| @@ -474,7 +474,7 @@ ResultCode PageTable::UnmapPhysicalMemory(VAddr addr, std::size_t size) { | ||||
|     CASCADE_CODE(UnmapMemory(addr, size)); | ||||
|  | ||||
|     auto process{system.Kernel().CurrentProcess()}; | ||||
|     process->GetResourceLimit()->Release(ResourceType::PhysicalMemory, mapped_size); | ||||
|     process->GetResourceLimit()->Release(LimitableResource::PhysicalMemory, mapped_size); | ||||
|     physical_memory_usage -= mapped_size; | ||||
|  | ||||
|     return RESULT_SUCCESS; | ||||
| @@ -783,7 +783,7 @@ ResultVal<VAddr> PageTable::SetHeapSize(std::size_t size) { | ||||
|  | ||||
|         auto process{system.Kernel().CurrentProcess()}; | ||||
|         if (process->GetResourceLimit() && delta != 0 && | ||||
|             !process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, delta)) { | ||||
|             !process->GetResourceLimit()->Reserve(LimitableResource::PhysicalMemory, delta)) { | ||||
|             return ERR_RESOURCE_LIMIT_EXCEEDED; | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -15,6 +15,7 @@ | ||||
| #include "core/file_sys/program_metadata.h" | ||||
| #include "core/hle/kernel/code_set.h" | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/k_scheduler.h" | ||||
| #include "core/hle/kernel/k_thread.h" | ||||
| #include "core/hle/kernel/kernel.h" | ||||
| @@ -22,7 +23,6 @@ | ||||
| #include "core/hle/kernel/memory/page_table.h" | ||||
| #include "core/hle/kernel/memory/slab_heap.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/lock.h" | ||||
| #include "core/memory.h" | ||||
| #include "core/settings.h" | ||||
| @@ -116,7 +116,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, | ||||
|  | ||||
|     std::shared_ptr<Process> process = std::make_shared<Process>(system); | ||||
|     process->name = std::move(name); | ||||
|     process->resource_limit = ResourceLimit::Create(kernel); | ||||
|     process->resource_limit = std::make_shared<KResourceLimit>(kernel, system); | ||||
|     process->status = ProcessStatus::Created; | ||||
|     process->program_id = 0; | ||||
|     process->process_id = type == ProcessType::KernelInternal ? kernel.CreateNewKernelProcessID() | ||||
| @@ -132,7 +132,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name, | ||||
|     return process; | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ResourceLimit> Process::GetResourceLimit() const { | ||||
| std::shared_ptr<KResourceLimit> Process::GetResourceLimit() const { | ||||
|     return resource_limit; | ||||
| } | ||||
|  | ||||
| @@ -154,7 +154,7 @@ void Process::DecrementThreadCount() { | ||||
| } | ||||
|  | ||||
| u64 Process::GetTotalPhysicalMemoryAvailable() const { | ||||
|     const u64 capacity{resource_limit->GetCurrentResourceValue(ResourceType::PhysicalMemory) + | ||||
|     const u64 capacity{resource_limit->GetFreeValue(LimitableResource::PhysicalMemory) + | ||||
|                        page_table->GetTotalHeapSize() + GetSystemResourceSize() + image_size + | ||||
|                        main_thread_stack_size}; | ||||
|  | ||||
| @@ -308,13 +308,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata, | ||||
|  | ||||
|     // Set initial resource limits | ||||
|     resource_limit->SetLimitValue( | ||||
|         ResourceType::PhysicalMemory, | ||||
|         LimitableResource::PhysicalMemory, | ||||
|         kernel.MemoryManager().GetSize(Memory::MemoryManager::Pool::Application)); | ||||
|     resource_limit->SetLimitValue(ResourceType::Threads, 608); | ||||
|     resource_limit->SetLimitValue(ResourceType::Events, 700); | ||||
|     resource_limit->SetLimitValue(ResourceType::TransferMemory, 128); | ||||
|     resource_limit->SetLimitValue(ResourceType::Sessions, 894); | ||||
|     ASSERT(resource_limit->Reserve(ResourceType::PhysicalMemory, code_size)); | ||||
|     resource_limit->SetLimitValue(LimitableResource::Threads, 608); | ||||
|     resource_limit->SetLimitValue(LimitableResource::Events, 700); | ||||
|     resource_limit->SetLimitValue(LimitableResource::TransferMemory, 128); | ||||
|     resource_limit->SetLimitValue(LimitableResource::Sessions, 894); | ||||
|     ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemory, code_size)); | ||||
|  | ||||
|     // Create TLS region | ||||
|     tls_region_address = CreateTLSRegion(); | ||||
| @@ -331,8 +331,8 @@ void Process::Run(s32 main_thread_priority, u64 stack_size) { | ||||
|     ChangeStatus(ProcessStatus::Running); | ||||
|  | ||||
|     SetupMainThread(system, *this, main_thread_priority, main_thread_stack_top); | ||||
|     resource_limit->Reserve(ResourceType::Threads, 1); | ||||
|     resource_limit->Reserve(ResourceType::PhysicalMemory, main_thread_stack_size); | ||||
|     resource_limit->Reserve(LimitableResource::Threads, 1); | ||||
|     resource_limit->Reserve(LimitableResource::PhysicalMemory, main_thread_stack_size); | ||||
| } | ||||
|  | ||||
| void Process::PrepareForTermination() { | ||||
|   | ||||
| @@ -29,7 +29,7 @@ class ProgramMetadata; | ||||
| namespace Kernel { | ||||
|  | ||||
| class KernelCore; | ||||
| class ResourceLimit; | ||||
| class KResourceLimit; | ||||
| class KThread; | ||||
| class TLSPage; | ||||
|  | ||||
| @@ -170,7 +170,7 @@ public: | ||||
|     } | ||||
|  | ||||
|     /// Gets the resource limit descriptor for this process | ||||
|     std::shared_ptr<ResourceLimit> GetResourceLimit() const; | ||||
|     std::shared_ptr<KResourceLimit> GetResourceLimit() const; | ||||
|  | ||||
|     /// Gets the ideal CPU core ID for this process | ||||
|     u8 GetIdealCoreId() const { | ||||
| @@ -402,7 +402,7 @@ private: | ||||
|     u32 system_resource_size = 0; | ||||
|  | ||||
|     /// Resource limit descriptor for this process | ||||
|     std::shared_ptr<ResourceLimit> resource_limit; | ||||
|     std::shared_ptr<KResourceLimit> resource_limit; | ||||
|  | ||||
|     /// The ideal CPU core for this process, threads are scheduled on this core by default. | ||||
|     u8 ideal_core = 0; | ||||
|   | ||||
| @@ -1,73 +0,0 @@ | ||||
| // Copyright 2015 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #include "core/hle/kernel/errors.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/result.h" | ||||
|  | ||||
| namespace Kernel { | ||||
| namespace { | ||||
| constexpr std::size_t ResourceTypeToIndex(ResourceType type) { | ||||
|     return static_cast<std::size_t>(type); | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| ResourceLimit::ResourceLimit(KernelCore& kernel) : Object{kernel} {} | ||||
| ResourceLimit::~ResourceLimit() = default; | ||||
|  | ||||
| bool ResourceLimit::Reserve(ResourceType resource, s64 amount) { | ||||
|     return Reserve(resource, amount, 10000000000); | ||||
| } | ||||
|  | ||||
| bool ResourceLimit::Reserve(ResourceType resource, s64 amount, u64 timeout) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
|  | ||||
|     s64 new_value = current[index] + amount; | ||||
|     if (new_value > limit[index] && available[index] + amount <= limit[index]) { | ||||
|         // TODO(bunnei): This is wrong for multicore, we should wait the calling thread for timeout | ||||
|         new_value = current[index] + amount; | ||||
|     } | ||||
|  | ||||
|     if (new_value <= limit[index]) { | ||||
|         current[index] = new_value; | ||||
|         return true; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| void ResourceLimit::Release(ResourceType resource, u64 amount) { | ||||
|     Release(resource, amount, amount); | ||||
| } | ||||
|  | ||||
| void ResourceLimit::Release(ResourceType resource, u64 used_amount, u64 available_amount) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
|  | ||||
|     current[index] -= used_amount; | ||||
|     available[index] -= available_amount; | ||||
| } | ||||
|  | ||||
| std::shared_ptr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel) { | ||||
|     return std::make_shared<ResourceLimit>(kernel); | ||||
| } | ||||
|  | ||||
| s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const { | ||||
|     return limit.at(ResourceTypeToIndex(resource)) - current.at(ResourceTypeToIndex(resource)); | ||||
| } | ||||
|  | ||||
| s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const { | ||||
|     return limit.at(ResourceTypeToIndex(resource)); | ||||
| } | ||||
|  | ||||
| ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) { | ||||
|     const std::size_t index{ResourceTypeToIndex(resource)}; | ||||
|     if (current[index] <= value) { | ||||
|         limit[index] = value; | ||||
|         return RESULT_SUCCESS; | ||||
|     } else { | ||||
|         LOG_ERROR(Kernel, "Limit value is too large! resource={}, value={}, index={}", resource, | ||||
|                   value, index); | ||||
|         return ERR_INVALID_STATE; | ||||
|     } | ||||
| } | ||||
| } // namespace Kernel | ||||
| @@ -1,106 +0,0 @@ | ||||
| // Copyright 2015 Citra Emulator Project | ||||
| // Licensed under GPLv2 or any later version | ||||
| // Refer to the license.txt file included. | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <array> | ||||
| #include <memory> | ||||
|  | ||||
| #include "common/common_types.h" | ||||
| #include "core/hle/kernel/object.h" | ||||
|  | ||||
| union ResultCode; | ||||
|  | ||||
| namespace Kernel { | ||||
|  | ||||
| class KernelCore; | ||||
|  | ||||
| enum class ResourceType : u32 { | ||||
|     PhysicalMemory, | ||||
|     Threads, | ||||
|     Events, | ||||
|     TransferMemory, | ||||
|     Sessions, | ||||
|  | ||||
|     // Used as a count, not an actual type. | ||||
|     ResourceTypeCount | ||||
| }; | ||||
|  | ||||
| constexpr bool IsValidResourceType(ResourceType type) { | ||||
|     return type < ResourceType::ResourceTypeCount; | ||||
| } | ||||
|  | ||||
| class ResourceLimit final : public Object { | ||||
| public: | ||||
|     explicit ResourceLimit(KernelCore& kernel); | ||||
|     ~ResourceLimit() override; | ||||
|  | ||||
|     /// Creates a resource limit object. | ||||
|     static std::shared_ptr<ResourceLimit> Create(KernelCore& kernel); | ||||
|  | ||||
|     std::string GetTypeName() const override { | ||||
|         return "ResourceLimit"; | ||||
|     } | ||||
|     std::string GetName() const override { | ||||
|         return GetTypeName(); | ||||
|     } | ||||
|  | ||||
|     static constexpr HandleType HANDLE_TYPE = HandleType::ResourceLimit; | ||||
|     HandleType GetHandleType() const override { | ||||
|         return HANDLE_TYPE; | ||||
|     } | ||||
|  | ||||
|     bool Reserve(ResourceType resource, s64 amount); | ||||
|     bool Reserve(ResourceType resource, s64 amount, u64 timeout); | ||||
|     void Release(ResourceType resource, u64 amount); | ||||
|     void Release(ResourceType resource, u64 used_amount, u64 available_amount); | ||||
|  | ||||
|     /** | ||||
|      * Gets the current value for the specified resource. | ||||
|      * @param resource Requested resource type | ||||
|      * @returns The current value of the resource type | ||||
|      */ | ||||
|     s64 GetCurrentResourceValue(ResourceType resource) const; | ||||
|  | ||||
|     /** | ||||
|      * Gets the max value for the specified resource. | ||||
|      * @param resource Requested resource type | ||||
|      * @returns The max value of the resource type | ||||
|      */ | ||||
|     s64 GetMaxResourceValue(ResourceType resource) const; | ||||
|  | ||||
|     /** | ||||
|      * Sets the limit value for a given resource type. | ||||
|      * | ||||
|      * @param resource The resource type to apply the limit to. | ||||
|      * @param value    The limit to apply to the given resource type. | ||||
|      * | ||||
|      * @return A result code indicating if setting the limit value | ||||
|      *         was successful or not. | ||||
|      * | ||||
|      * @note The supplied limit value *must* be greater than or equal to | ||||
|      *       the current resource value for the given resource type, | ||||
|      *       otherwise ERR_INVALID_STATE will be returned. | ||||
|      */ | ||||
|     ResultCode SetLimitValue(ResourceType resource, s64 value); | ||||
|  | ||||
|     void Finalize() override {} | ||||
|  | ||||
| private: | ||||
|     // TODO(Subv): Increment resource limit current values in their respective Kernel::T::Create | ||||
|     // functions | ||||
|     // | ||||
|     // Currently we have no way of distinguishing if a Create was called by the running application, | ||||
|     // or by a service module. Approach this once we have separated the service modules into their | ||||
|     // own processes | ||||
|  | ||||
|     using ResourceArray = | ||||
|         std::array<s64, static_cast<std::size_t>(ResourceType::ResourceTypeCount)>; | ||||
|  | ||||
|     ResourceArray limit{}; | ||||
|     ResourceArray current{}; | ||||
|     ResourceArray available{}; | ||||
| }; | ||||
|  | ||||
| } // namespace Kernel | ||||
| @@ -26,6 +26,7 @@ | ||||
| #include "core/hle/kernel/handle_table.h" | ||||
| #include "core/hle/kernel/k_address_arbiter.h" | ||||
| #include "core/hle/kernel/k_condition_variable.h" | ||||
| #include "core/hle/kernel/k_resource_limit.h" | ||||
| #include "core/hle/kernel/k_scheduler.h" | ||||
| #include "core/hle/kernel/k_scoped_scheduler_lock_and_sleep.h" | ||||
| #include "core/hle/kernel/k_synchronization_object.h" | ||||
| @@ -37,7 +38,6 @@ | ||||
| #include "core/hle/kernel/physical_core.h" | ||||
| #include "core/hle/kernel/process.h" | ||||
| #include "core/hle/kernel/readable_event.h" | ||||
| #include "core/hle/kernel/resource_limit.h" | ||||
| #include "core/hle/kernel/shared_memory.h" | ||||
| #include "core/hle/kernel/svc.h" | ||||
| #include "core/hle/kernel/svc_results.h" | ||||
| @@ -141,7 +141,7 @@ enum class ResourceLimitValueType { | ||||
| ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_limit, | ||||
|                                           u32 resource_type, ResourceLimitValueType value_type) { | ||||
|     std::lock_guard lock{HLE::g_hle_lock}; | ||||
|     const auto type = static_cast<ResourceType>(resource_type); | ||||
|     const auto type = static_cast<LimitableResource>(resource_type); | ||||
|     if (!IsValidResourceType(type)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | ||||
|         return ERR_INVALID_ENUM_VALUE; | ||||
| @@ -151,7 +151,7 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ | ||||
|     ASSERT(current_process != nullptr); | ||||
|  | ||||
|     const auto resource_limit_object = | ||||
|         current_process->GetHandleTable().Get<ResourceLimit>(resource_limit); | ||||
|         current_process->GetHandleTable().Get<KResourceLimit>(resource_limit); | ||||
|     if (!resource_limit_object) { | ||||
|         LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", | ||||
|                   resource_limit); | ||||
| @@ -159,10 +159,10 @@ ResultVal<s64> RetrieveResourceLimitValue(Core::System& system, Handle resource_ | ||||
|     } | ||||
|  | ||||
|     if (value_type == ResourceLimitValueType::CurrentValue) { | ||||
|         return MakeResult(resource_limit_object->GetCurrentResourceValue(type)); | ||||
|         return MakeResult(resource_limit_object->GetCurrentValue(type)); | ||||
|     } | ||||
|  | ||||
|     return MakeResult(resource_limit_object->GetMaxResourceValue(type)); | ||||
|     return MakeResult(resource_limit_object->GetLimitValue(type)); | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
| @@ -312,7 +312,7 @@ static ResultCode ConnectToNamedPort(Core::System& system, Handle* out_handle, | ||||
|         return ERR_NOT_FOUND; | ||||
|     } | ||||
|  | ||||
|     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(ResourceType::Sessions, 1)); | ||||
|     ASSERT(kernel.CurrentProcess()->GetResourceLimit()->Reserve(LimitableResource::Sessions, 1)); | ||||
|  | ||||
|     auto client_port = it->second; | ||||
|  | ||||
| @@ -1450,7 +1450,8 @@ static ResultCode CreateThread(Core::System& system, Handle* out_handle, VAddr e | ||||
|              Svc::ResultInvalidPriority); | ||||
|     R_UNLESS(process.CheckThreadPriority(priority), Svc::ResultInvalidPriority); | ||||
|  | ||||
|     ASSERT(process.GetResourceLimit()->Reserve(ResourceType::Threads, 1)); | ||||
|     ASSERT(process.GetResourceLimit()->Reserve( | ||||
|         LimitableResource::Threads, 1, system.CoreTiming().GetGlobalTimeNs().count() + 100000000)); | ||||
|  | ||||
|     std::shared_ptr<KThread> thread; | ||||
|     { | ||||
| @@ -1972,7 +1973,7 @@ static ResultCode CreateResourceLimit(Core::System& system, Handle* out_handle) | ||||
|     LOG_DEBUG(Kernel_SVC, "called"); | ||||
|  | ||||
|     auto& kernel = system.Kernel(); | ||||
|     auto resource_limit = ResourceLimit::Create(kernel); | ||||
|     auto resource_limit = std::make_shared<KResourceLimit>(kernel, system); | ||||
|  | ||||
|     auto* const current_process = kernel.CurrentProcess(); | ||||
|     ASSERT(current_process != nullptr); | ||||
| @@ -2019,7 +2020,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour | ||||
|     LOG_DEBUG(Kernel_SVC, "called. Handle={:08X}, Resource type={}, Value={}", resource_limit, | ||||
|               resource_type, value); | ||||
|  | ||||
|     const auto type = static_cast<ResourceType>(resource_type); | ||||
|     const auto type = static_cast<LimitableResource>(resource_type); | ||||
|     if (!IsValidResourceType(type)) { | ||||
|         LOG_ERROR(Kernel_SVC, "Invalid resource limit type: '{}'", resource_type); | ||||
|         return ERR_INVALID_ENUM_VALUE; | ||||
| @@ -2029,7 +2030,7 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour | ||||
|     ASSERT(current_process != nullptr); | ||||
|  | ||||
|     auto resource_limit_object = | ||||
|         current_process->GetHandleTable().Get<ResourceLimit>(resource_limit); | ||||
|         current_process->GetHandleTable().Get<KResourceLimit>(resource_limit); | ||||
|     if (!resource_limit_object) { | ||||
|         LOG_ERROR(Kernel_SVC, "Handle to non-existent resource limit instance used. Handle={:08X}", | ||||
|                   resource_limit); | ||||
| @@ -2041,8 +2042,8 @@ static ResultCode SetResourceLimitLimitValue(Core::System& system, Handle resour | ||||
|         LOG_ERROR( | ||||
|             Kernel_SVC, | ||||
|             "Attempted to lower resource limit ({}) for category '{}' below its current value ({})", | ||||
|             resource_limit_object->GetMaxResourceValue(type), resource_type, | ||||
|             resource_limit_object->GetCurrentResourceValue(type)); | ||||
|             resource_limit_object->GetLimitValue(type), resource_type, | ||||
|             resource_limit_object->GetCurrentValue(type)); | ||||
|         return set_result; | ||||
|     } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user