kernel: Rewrite resource limit to be more accurate
Matches closer to hardware
This commit is contained in:
parent
a4526c4e1a
commit
3be1a565f8
@ -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
|
||||
|
60
src/core/hle/kernel/k_light_condition_variable.h
Normal file
60
src/core/hle/kernel/k_light_condition_variable.h
Normal file
@ -0,0 +1,60 @@
|
||||
// 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 {
|
||||
private:
|
||||
KThreadQueue m_thread_queue;
|
||||
|
||||
public:
|
||||
KLightConditionVariable(KernelCore& kernel) : m_thread_queue(kernel), kernel(kernel) {}
|
||||
|
||||
void Wait(KLightLock* lock, s64 timeout = -1ll) {
|
||||
WaitImpl(lock, timeout);
|
||||
lock->Lock();
|
||||
}
|
||||
|
||||
void Broadcast() {
|
||||
KScopedSchedulerLock lk{kernel};
|
||||
while (m_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);
|
||||
// KHardwareTimer* timer;
|
||||
|
||||
/* Sleep the thread. */
|
||||
{
|
||||
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout);
|
||||
lock->Unlock();
|
||||
|
||||
if (!m_thread_queue.SleepThread(owner)) {
|
||||
lk.CancelSleep();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Cancel the task that the sleep setup. */
|
||||
kernel.TimeManager().UnscheduleTimeEvent(owner);
|
||||
}
|
||||
KernelCore& kernel;
|
||||
};
|
||||
} // namespace Kernel
|
155
src/core/hle/kernel/k_resource_limit.cpp
Normal file
155
src/core/hle/kernel/k_resource_limit.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
// 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 {
|
||||
namespace {
|
||||
static const s64 DefaultTimeout =
|
||||
Core::Timing::msToCycles(std::chrono::milliseconds{10000}); // 10 seconds
|
||||
}
|
||||
|
||||
KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system)
|
||||
: Object{kernel}, m_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{m_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{m_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{m_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(m_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(m_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().GetClockTicks() + DefaultTimeout);
|
||||
}
|
||||
|
||||
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
|
||||
ASSERT(value >= 0);
|
||||
const auto index = static_cast<std::size_t>(which);
|
||||
KScopedLightLock lk(m_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().GetClockTicks() < static_cast<u64>(timeout))) {
|
||||
waiter_count++;
|
||||
cond_var.Wait(&m_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(m_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
|
80
src/core/hle/kernel/k_resource_limit.h
Normal file
80
src/core/hle/kernel/k_resource_limit.h
Normal file
@ -0,0 +1,80 @@
|
||||
// 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 {
|
||||
PhysicalMemoryMax = 0,
|
||||
ThreadCountMax = 1,
|
||||
EventCountMax = 2,
|
||||
TransferMemoryCountMax = 3,
|
||||
SessionCountMax = 4,
|
||||
|
||||
Count,
|
||||
};
|
||||
|
||||
constexpr bool IsValidResourceType(LimitableResource type) {
|
||||
return type < LimitableResource::Count;
|
||||
}
|
||||
|
||||
class KResourceLimit final : public Object {
|
||||
public:
|
||||
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:
|
||||
std::array<s64, static_cast<std::size_t>(LimitableResource::Count)> limit_values{};
|
||||
std::array<s64, static_cast<std::size_t>(LimitableResource::Count)> current_values{};
|
||||
std::array<s64, static_cast<std::size_t>(LimitableResource::Count)> current_hints{};
|
||||
std::array<s64, static_cast<std::size_t>(LimitableResource::Count)> peak_values{};
|
||||
mutable KLightLock m_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::ThreadCountMax, 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,23 @@ 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::PhysicalMemoryMax, 0x100000000)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::EventCountMax, 700)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200)
|
||||
.IsSuccess());
|
||||
ASSERT(system_resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 900)
|
||||
.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());
|
||||
|
||||
if (!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0) ||
|
||||
!system_resource_limit->Reserve(ResourceType::PhysicalMemory, 0x60000)) {
|
||||
if (!system_resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, 0x60000)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@ -320,7 +324,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 +394,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 {
|
||||
@ -413,8 +413,8 @@ ResultCode PageTable::MapPhysicalMemory(VAddr addr, std::size_t size) {
|
||||
const std::size_t remaining_size{size - mapped_size};
|
||||
const std::size_t remaining_pages{remaining_size / PageSize};
|
||||
|
||||
if (process->GetResourceLimit() &&
|
||||
!process->GetResourceLimit()->Reserve(ResourceType::PhysicalMemory, remaining_size)) {
|
||||
if (process->GetResourceLimit() && !process->GetResourceLimit()->Reserve(
|
||||
LimitableResource::PhysicalMemoryMax, remaining_size)) {
|
||||
return ERR_RESOURCE_LIMIT_EXCEEDED;
|
||||
}
|
||||
|
||||
@ -422,7 +422,8 @@ 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::PhysicalMemoryMax,
|
||||
remaining_size);
|
||||
});
|
||||
|
||||
CASCADE_CODE(system.Kernel().MemoryManager().Allocate(page_linked_list, remaining_pages,
|
||||
@ -474,7 +475,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::PhysicalMemoryMax, mapped_size);
|
||||
physical_memory_usage -= mapped_size;
|
||||
|
||||
return RESULT_SUCCESS;
|
||||
@ -783,7 +784,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::PhysicalMemoryMax, 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->GetCurrentValue(LimitableResource::PhysicalMemoryMax) +
|
||||
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::PhysicalMemoryMax,
|
||||
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::ThreadCountMax, 608);
|
||||
resource_limit->SetLimitValue(LimitableResource::EventCountMax, 700);
|
||||
resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 128);
|
||||
resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 894);
|
||||
ASSERT(resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, 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::ThreadCountMax, 1);
|
||||
resource_limit->Reserve(LimitableResource::PhysicalMemoryMax, 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,8 @@ 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::SessionCountMax,
|
||||
1));
|
||||
|
||||
auto client_port = it->second;
|
||||
|
||||
@ -1450,7 +1451,10 @@ 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::ThreadCountMax, 1,
|
||||
system.CoreTiming().GetClockTicks() +
|
||||
Core::Timing::msToCycles(std::chrono::milliseconds{100})));
|
||||
|
||||
std::shared_ptr<KThread> thread;
|
||||
{
|
||||
@ -1972,7 +1976,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 +2023,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 +2033,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 +2045,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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user