kernel/resource_limit: Clean up interface
Cleans out the citra/3DS-specific implementation details that don't apply to the Switch. Sets the stage for implementing ResourceLimit instances properly. While we're at it, remove the erroneous checks within CreateThread() and SetThreadPriority(). While these are indeed checked in some capacity, they are not checked via a ResourceLimit instance. In the process of moving out Citra-specifics, this also replaces the system ResourceLimit instance's values with ones from the Switch.
This commit is contained in:
parent
048da7240d
commit
5d46038c5c
|
@ -105,7 +105,7 @@ struct KernelCore::Impl {
|
||||||
void Initialize(KernelCore& kernel) {
|
void Initialize(KernelCore& kernel) {
|
||||||
Shutdown();
|
Shutdown();
|
||||||
|
|
||||||
InitializeResourceLimits(kernel);
|
InitializeSystemResourceLimit(kernel);
|
||||||
InitializeThreads();
|
InitializeThreads();
|
||||||
InitializeTimers();
|
InitializeTimers();
|
||||||
}
|
}
|
||||||
|
@ -118,7 +118,7 @@ struct KernelCore::Impl {
|
||||||
process_list.clear();
|
process_list.clear();
|
||||||
current_process = nullptr;
|
current_process = nullptr;
|
||||||
|
|
||||||
resource_limits.fill(nullptr);
|
system_resource_limit = nullptr;
|
||||||
|
|
||||||
thread_wakeup_callback_handle_table.Clear();
|
thread_wakeup_callback_handle_table.Clear();
|
||||||
thread_wakeup_event_type = nullptr;
|
thread_wakeup_event_type = nullptr;
|
||||||
|
@ -129,63 +129,17 @@ struct KernelCore::Impl {
|
||||||
named_ports.clear();
|
named_ports.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeResourceLimits(KernelCore& kernel) {
|
// Creates the default system resource limit
|
||||||
// Create the four resource limits that the system uses
|
void InitializeSystemResourceLimit(KernelCore& kernel) {
|
||||||
// Create the APPLICATION resource limit
|
system_resource_limit = ResourceLimit::Create(kernel, "System");
|
||||||
SharedPtr<ResourceLimit> resource_limit = ResourceLimit::Create(kernel, "Applications");
|
|
||||||
resource_limit->max_priority = 0x18;
|
|
||||||
resource_limit->max_commit = 0x4000000;
|
|
||||||
resource_limit->max_threads = 0x20;
|
|
||||||
resource_limit->max_events = 0x20;
|
|
||||||
resource_limit->max_mutexes = 0x20;
|
|
||||||
resource_limit->max_semaphores = 0x8;
|
|
||||||
resource_limit->max_timers = 0x8;
|
|
||||||
resource_limit->max_shared_mems = 0x10;
|
|
||||||
resource_limit->max_address_arbiters = 0x2;
|
|
||||||
resource_limit->max_cpu_time = 0x1E;
|
|
||||||
resource_limits[static_cast<u8>(ResourceLimitCategory::APPLICATION)] = resource_limit;
|
|
||||||
|
|
||||||
// Create the SYS_APPLET resource limit
|
// If setting the default system values fails, then something seriously wrong has occurred.
|
||||||
resource_limit = ResourceLimit::Create(kernel, "System Applets");
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
|
||||||
resource_limit->max_priority = 0x4;
|
.IsSuccess());
|
||||||
resource_limit->max_commit = 0x5E00000;
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Threads, 800).IsSuccess());
|
||||||
resource_limit->max_threads = 0x1D;
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Events, 700).IsSuccess());
|
||||||
resource_limit->max_events = 0xB;
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::TransferMemory, 200).IsSuccess());
|
||||||
resource_limit->max_mutexes = 0x8;
|
ASSERT(system_resource_limit->SetLimitValue(ResourceType::Sessions, 900).IsSuccess());
|
||||||
resource_limit->max_semaphores = 0x4;
|
|
||||||
resource_limit->max_timers = 0x4;
|
|
||||||
resource_limit->max_shared_mems = 0x8;
|
|
||||||
resource_limit->max_address_arbiters = 0x3;
|
|
||||||
resource_limit->max_cpu_time = 0x2710;
|
|
||||||
resource_limits[static_cast<u8>(ResourceLimitCategory::SYS_APPLET)] = resource_limit;
|
|
||||||
|
|
||||||
// Create the LIB_APPLET resource limit
|
|
||||||
resource_limit = ResourceLimit::Create(kernel, "Library Applets");
|
|
||||||
resource_limit->max_priority = 0x4;
|
|
||||||
resource_limit->max_commit = 0x600000;
|
|
||||||
resource_limit->max_threads = 0xE;
|
|
||||||
resource_limit->max_events = 0x8;
|
|
||||||
resource_limit->max_mutexes = 0x8;
|
|
||||||
resource_limit->max_semaphores = 0x4;
|
|
||||||
resource_limit->max_timers = 0x4;
|
|
||||||
resource_limit->max_shared_mems = 0x8;
|
|
||||||
resource_limit->max_address_arbiters = 0x1;
|
|
||||||
resource_limit->max_cpu_time = 0x2710;
|
|
||||||
resource_limits[static_cast<u8>(ResourceLimitCategory::LIB_APPLET)] = resource_limit;
|
|
||||||
|
|
||||||
// Create the OTHER resource limit
|
|
||||||
resource_limit = ResourceLimit::Create(kernel, "Others");
|
|
||||||
resource_limit->max_priority = 0x4;
|
|
||||||
resource_limit->max_commit = 0x2180000;
|
|
||||||
resource_limit->max_threads = 0xE1;
|
|
||||||
resource_limit->max_events = 0x108;
|
|
||||||
resource_limit->max_mutexes = 0x25;
|
|
||||||
resource_limit->max_semaphores = 0x43;
|
|
||||||
resource_limit->max_timers = 0x2C;
|
|
||||||
resource_limit->max_shared_mems = 0x1F;
|
|
||||||
resource_limit->max_address_arbiters = 0x2D;
|
|
||||||
resource_limit->max_cpu_time = 0x3E8;
|
|
||||||
resource_limits[static_cast<u8>(ResourceLimitCategory::OTHER)] = resource_limit;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InitializeThreads() {
|
void InitializeThreads() {
|
||||||
|
@ -208,7 +162,7 @@ struct KernelCore::Impl {
|
||||||
std::vector<SharedPtr<Process>> process_list;
|
std::vector<SharedPtr<Process>> process_list;
|
||||||
Process* current_process = nullptr;
|
Process* current_process = nullptr;
|
||||||
|
|
||||||
std::array<SharedPtr<ResourceLimit>, 4> resource_limits;
|
SharedPtr<ResourceLimit> system_resource_limit;
|
||||||
|
|
||||||
/// The event type of the generic timer callback event
|
/// The event type of the generic timer callback event
|
||||||
CoreTiming::EventType* timer_callback_event_type = nullptr;
|
CoreTiming::EventType* timer_callback_event_type = nullptr;
|
||||||
|
@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
|
||||||
impl->Shutdown();
|
impl->Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
|
SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
|
||||||
ResourceLimitCategory category) const {
|
return impl->system_resource_limit;
|
||||||
return impl->resource_limits.at(static_cast<std::size_t>(category));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
|
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {
|
||||||
|
|
|
@ -24,8 +24,6 @@ class ResourceLimit;
|
||||||
class Thread;
|
class Thread;
|
||||||
class Timer;
|
class Timer;
|
||||||
|
|
||||||
enum class ResourceLimitCategory : u8;
|
|
||||||
|
|
||||||
/// Represents a single instance of the kernel.
|
/// Represents a single instance of the kernel.
|
||||||
class KernelCore {
|
class KernelCore {
|
||||||
private:
|
private:
|
||||||
|
@ -47,8 +45,8 @@ public:
|
||||||
/// Clears all resources in use by the kernel instance.
|
/// Clears all resources in use by the kernel instance.
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
|
/// Retrieves a shared pointer to the system resource limit instance.
|
||||||
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
|
SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
|
||||||
|
|
||||||
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
|
||||||
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;
|
||||||
|
|
|
@ -28,7 +28,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
|
||||||
process->name = std::move(name);
|
process->name = std::move(name);
|
||||||
process->flags.raw = 0;
|
process->flags.raw = 0;
|
||||||
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
|
||||||
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
|
process->resource_limit = kernel.GetSystemResourceLimit();
|
||||||
process->status = ProcessStatus::Created;
|
process->status = ProcessStatus::Created;
|
||||||
process->program_id = 0;
|
process->program_id = 0;
|
||||||
process->process_id = kernel.CreateNewProcessID();
|
process->process_id = kernel.CreateNewProcessID();
|
||||||
|
|
|
@ -2,12 +2,16 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <cstring>
|
#include "core/hle/kernel/errors.h"
|
||||||
#include "common/assert.h"
|
|
||||||
#include "common/logging/log.h"
|
|
||||||
#include "core/hle/kernel/resource_limit.h"
|
#include "core/hle/kernel/resource_limit.h"
|
||||||
|
#include "core/hle/result.h"
|
||||||
|
|
||||||
namespace Kernel {
|
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(KernelCore& kernel) : Object{kernel} {}
|
||||||
ResourceLimit::~ResourceLimit() = default;
|
ResourceLimit::~ResourceLimit() = default;
|
||||||
|
@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
|
||||||
return resource_limit;
|
return resource_limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
|
||||||
switch (resource) {
|
return values.at(ResourceTypeToIndex(resource));
|
||||||
case ResourceType::Commit:
|
|
||||||
return current_commit;
|
|
||||||
case ResourceType::Thread:
|
|
||||||
return current_threads;
|
|
||||||
case ResourceType::Event:
|
|
||||||
return current_events;
|
|
||||||
case ResourceType::Mutex:
|
|
||||||
return current_mutexes;
|
|
||||||
case ResourceType::Semaphore:
|
|
||||||
return current_semaphores;
|
|
||||||
case ResourceType::Timer:
|
|
||||||
return current_timers;
|
|
||||||
case ResourceType::SharedMemory:
|
|
||||||
return current_shared_mems;
|
|
||||||
case ResourceType::AddressArbiter:
|
|
||||||
return current_address_arbiters;
|
|
||||||
case ResourceType::CPUTime:
|
|
||||||
return current_cpu_time;
|
|
||||||
default:
|
|
||||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
|
||||||
switch (resource) {
|
return limits.at(ResourceTypeToIndex(resource));
|
||||||
case ResourceType::Priority:
|
}
|
||||||
return max_priority;
|
|
||||||
case ResourceType::Commit:
|
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
|
||||||
return max_commit;
|
const auto index = ResourceTypeToIndex(resource);
|
||||||
case ResourceType::Thread:
|
|
||||||
return max_threads;
|
if (value < values[index]) {
|
||||||
case ResourceType::Event:
|
return ERR_INVALID_STATE;
|
||||||
return max_events;
|
|
||||||
case ResourceType::Mutex:
|
|
||||||
return max_mutexes;
|
|
||||||
case ResourceType::Semaphore:
|
|
||||||
return max_semaphores;
|
|
||||||
case ResourceType::Timer:
|
|
||||||
return max_timers;
|
|
||||||
case ResourceType::SharedMemory:
|
|
||||||
return max_shared_mems;
|
|
||||||
case ResourceType::AddressArbiter:
|
|
||||||
return max_address_arbiters;
|
|
||||||
case ResourceType::CPUTime:
|
|
||||||
return max_cpu_time;
|
|
||||||
default:
|
|
||||||
LOG_ERROR(Kernel, "Unknown resource type={:08X}", static_cast<u32>(resource));
|
|
||||||
UNIMPLEMENTED();
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
values[index] = value;
|
||||||
|
return RESULT_SUCCESS;
|
||||||
}
|
}
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -4,31 +4,25 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "core/hle/kernel/object.h"
|
#include "core/hle/kernel/object.h"
|
||||||
|
|
||||||
|
union ResultCode;
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
class KernelCore;
|
class KernelCore;
|
||||||
|
|
||||||
enum class ResourceLimitCategory : u8 {
|
|
||||||
APPLICATION = 0,
|
|
||||||
SYS_APPLET = 1,
|
|
||||||
LIB_APPLET = 2,
|
|
||||||
OTHER = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ResourceType {
|
enum class ResourceType {
|
||||||
Priority = 0,
|
PhysicalMemory,
|
||||||
Commit = 1,
|
Threads,
|
||||||
Thread = 2,
|
Events,
|
||||||
Event = 3,
|
TransferMemory,
|
||||||
Mutex = 4,
|
Sessions,
|
||||||
Semaphore = 5,
|
|
||||||
Timer = 6,
|
// Used as a count, not an actual type.
|
||||||
SharedMemory = 7,
|
ResourceTypeCount
|
||||||
AddressArbiter = 8,
|
|
||||||
CPUTime = 9,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class ResourceLimit final : public Object {
|
class ResourceLimit final : public Object {
|
||||||
|
@ -55,61 +49,51 @@ public:
|
||||||
* @param resource Requested resource type
|
* @param resource Requested resource type
|
||||||
* @returns The current value of the resource type
|
* @returns The current value of the resource type
|
||||||
*/
|
*/
|
||||||
s32 GetCurrentResourceValue(ResourceType resource) const;
|
s64 GetCurrentResourceValue(ResourceType resource) const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the max value for the specified resource.
|
* Gets the max value for the specified resource.
|
||||||
* @param resource Requested resource type
|
* @param resource Requested resource type
|
||||||
* @returns The max value of the resource type
|
* @returns The max value of the resource type
|
||||||
*/
|
*/
|
||||||
u32 GetMaxResourceValue(ResourceType resource) const;
|
s64 GetMaxResourceValue(ResourceType resource) const;
|
||||||
|
|
||||||
/// Name of resource limit object.
|
/**
|
||||||
std::string name;
|
* Sets the limit value for a given resource type.
|
||||||
|
*
|
||||||
/// Max thread priority that a process in this category can create
|
* @param resource The resource type to apply the limit to.
|
||||||
s32 max_priority = 0;
|
* @param value The limit to apply to the given resource type.
|
||||||
|
*
|
||||||
/// Max memory that processes in this category can use
|
* @return A result code indicating if setting the limit value
|
||||||
s32 max_commit = 0;
|
* was successful or not.
|
||||||
|
*
|
||||||
///< Max number of objects that can be collectively created by the processes in this category
|
* @note The supplied limit value *must* be greater than or equal to
|
||||||
s32 max_threads = 0;
|
* the current resource value for the given resource type,
|
||||||
s32 max_events = 0;
|
* otherwise ERR_INVALID_STATE will be returned.
|
||||||
s32 max_mutexes = 0;
|
*/
|
||||||
s32 max_semaphores = 0;
|
ResultCode SetLimitValue(ResourceType resource, s64 value);
|
||||||
s32 max_timers = 0;
|
|
||||||
s32 max_shared_mems = 0;
|
|
||||||
s32 max_address_arbiters = 0;
|
|
||||||
|
|
||||||
/// Max CPU time that the processes in this category can utilize
|
|
||||||
s32 max_cpu_time = 0;
|
|
||||||
|
|
||||||
// TODO(Subv): Increment these in their respective Kernel::T::Create functions, keeping in mind
|
|
||||||
// that APPLICATION resource limits should not be affected by the objects created by service
|
|
||||||
// modules.
|
|
||||||
// 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
|
|
||||||
|
|
||||||
/// Current memory that the processes in this category are using
|
|
||||||
s32 current_commit = 0;
|
|
||||||
|
|
||||||
///< Current number of objects among all processes in this category
|
|
||||||
s32 current_threads = 0;
|
|
||||||
s32 current_events = 0;
|
|
||||||
s32 current_mutexes = 0;
|
|
||||||
s32 current_semaphores = 0;
|
|
||||||
s32 current_timers = 0;
|
|
||||||
s32 current_shared_mems = 0;
|
|
||||||
s32 current_address_arbiters = 0;
|
|
||||||
|
|
||||||
/// Current CPU time that the processes in this category are utilizing
|
|
||||||
s32 current_cpu_time = 0;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit ResourceLimit(KernelCore& kernel);
|
explicit ResourceLimit(KernelCore& kernel);
|
||||||
~ResourceLimit() override;
|
~ResourceLimit() override;
|
||||||
|
|
||||||
|
// 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)>;
|
||||||
|
|
||||||
|
/// Maximum values a resource type may reach.
|
||||||
|
ResourceArray limits{};
|
||||||
|
/// Current resource limit values.
|
||||||
|
ResourceArray values{};
|
||||||
|
|
||||||
|
/// Name of resource limit object.
|
||||||
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Kernel
|
} // namespace Kernel
|
||||||
|
|
|
@ -736,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
|
||||||
|
|
||||||
const auto* const current_process = Core::CurrentProcess();
|
const auto* const current_process = Core::CurrentProcess();
|
||||||
|
|
||||||
// Note: The kernel uses the current process's resource limit instead of
|
|
||||||
// the one from the thread owner's resource limit.
|
|
||||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
|
||||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
|
||||||
return ERR_INVALID_THREAD_PRIORITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
SharedPtr<Thread> thread = current_process->GetHandleTable().Get<Thread>(handle);
|
||||||
if (!thread) {
|
if (!thread) {
|
||||||
return ERR_INVALID_HANDLE;
|
return ERR_INVALID_HANDLE;
|
||||||
|
@ -885,10 +878,6 @@ static ResultCode CreateThread(Handle* out_handle, VAddr entry_point, u64 arg, V
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* const current_process = Core::CurrentProcess();
|
auto* const current_process = Core::CurrentProcess();
|
||||||
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
|
|
||||||
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
|
|
||||||
return ERR_INVALID_THREAD_PRIORITY;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
if (processor_id == THREADPROCESSORID_DEFAULT) {
|
||||||
// Set the target CPU to the one specified in the process' exheader.
|
// Set the target CPU to the one specified in the process' exheader.
|
||||||
|
|
Loading…
Reference in New Issue