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:
Lioncash 2018-11-19 12:54:06 -05:00
parent 048da7240d
commit 5d46038c5c
6 changed files with 84 additions and 193 deletions

View File

@ -105,7 +105,7 @@ struct KernelCore::Impl {
void Initialize(KernelCore& kernel) {
Shutdown();
InitializeResourceLimits(kernel);
InitializeSystemResourceLimit(kernel);
InitializeThreads();
InitializeTimers();
}
@ -118,7 +118,7 @@ struct KernelCore::Impl {
process_list.clear();
current_process = nullptr;
resource_limits.fill(nullptr);
system_resource_limit = nullptr;
thread_wakeup_callback_handle_table.Clear();
thread_wakeup_event_type = nullptr;
@ -129,63 +129,17 @@ struct KernelCore::Impl {
named_ports.clear();
}
void InitializeResourceLimits(KernelCore& kernel) {
// Create the four resource limits that the system uses
// Create the APPLICATION resource limit
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;
// Creates the default system resource limit
void InitializeSystemResourceLimit(KernelCore& kernel) {
system_resource_limit = ResourceLimit::Create(kernel, "System");
// Create the SYS_APPLET resource limit
resource_limit = ResourceLimit::Create(kernel, "System Applets");
resource_limit->max_priority = 0x4;
resource_limit->max_commit = 0x5E00000;
resource_limit->max_threads = 0x1D;
resource_limit->max_events = 0xB;
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 = 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;
// If setting the default system values fails, then something seriously wrong has occurred.
ASSERT(system_resource_limit->SetLimitValue(ResourceType::PhysicalMemory, 0x200000000)
.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());
}
void InitializeThreads() {
@ -208,7 +162,7 @@ struct KernelCore::Impl {
std::vector<SharedPtr<Process>> process_list;
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
CoreTiming::EventType* timer_callback_event_type = nullptr;
@ -239,9 +193,8 @@ void KernelCore::Shutdown() {
impl->Shutdown();
}
SharedPtr<ResourceLimit> KernelCore::ResourceLimitForCategory(
ResourceLimitCategory category) const {
return impl->resource_limits.at(static_cast<std::size_t>(category));
SharedPtr<ResourceLimit> KernelCore::GetSystemResourceLimit() const {
return impl->system_resource_limit;
}
SharedPtr<Thread> KernelCore::RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const {

View File

@ -24,8 +24,6 @@ class ResourceLimit;
class Thread;
class Timer;
enum class ResourceLimitCategory : u8;
/// Represents a single instance of the kernel.
class KernelCore {
private:
@ -47,8 +45,8 @@ public:
/// Clears all resources in use by the kernel instance.
void Shutdown();
/// Retrieves a shared pointer to a ResourceLimit identified by the given category.
SharedPtr<ResourceLimit> ResourceLimitForCategory(ResourceLimitCategory category) const;
/// Retrieves a shared pointer to the system resource limit instance.
SharedPtr<ResourceLimit> GetSystemResourceLimit() const;
/// Retrieves a shared pointer to a Thread instance within the thread wakeup handle table.
SharedPtr<Thread> RetrieveThreadFromWakeupCallbackHandleTable(Handle handle) const;

View File

@ -28,7 +28,7 @@ SharedPtr<Process> Process::Create(KernelCore& kernel, std::string&& name) {
process->name = std::move(name);
process->flags.raw = 0;
process->flags.memory_region.Assign(MemoryRegion::APPLICATION);
process->resource_limit = kernel.ResourceLimitForCategory(ResourceLimitCategory::APPLICATION);
process->resource_limit = kernel.GetSystemResourceLimit();
process->status = ProcessStatus::Created;
process->program_id = 0;
process->process_id = kernel.CreateNewProcessID();

View File

@ -2,12 +2,16 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <cstring>
#include "common/assert.h"
#include "common/logging/log.h"
#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;
@ -19,59 +23,22 @@ SharedPtr<ResourceLimit> ResourceLimit::Create(KernelCore& kernel, std::string n
return resource_limit;
}
s32 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
switch (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;
}
s64 ResourceLimit::GetCurrentResourceValue(ResourceType resource) const {
return values.at(ResourceTypeToIndex(resource));
}
u32 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
switch (resource) {
case ResourceType::Priority:
return max_priority;
case ResourceType::Commit:
return max_commit;
case ResourceType::Thread:
return max_threads;
case ResourceType::Event:
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;
s64 ResourceLimit::GetMaxResourceValue(ResourceType resource) const {
return limits.at(ResourceTypeToIndex(resource));
}
ResultCode ResourceLimit::SetLimitValue(ResourceType resource, s64 value) {
const auto index = ResourceTypeToIndex(resource);
if (value < values[index]) {
return ERR_INVALID_STATE;
}
values[index] = value;
return RESULT_SUCCESS;
}
} // namespace Kernel

View File

@ -4,31 +4,25 @@
#pragma once
#include <array>
#include "common/common_types.h"
#include "core/hle/kernel/object.h"
union ResultCode;
namespace Kernel {
class KernelCore;
enum class ResourceLimitCategory : u8 {
APPLICATION = 0,
SYS_APPLET = 1,
LIB_APPLET = 2,
OTHER = 3
};
enum class ResourceType {
Priority = 0,
Commit = 1,
Thread = 2,
Event = 3,
Mutex = 4,
Semaphore = 5,
Timer = 6,
SharedMemory = 7,
AddressArbiter = 8,
CPUTime = 9,
PhysicalMemory,
Threads,
Events,
TransferMemory,
Sessions,
// Used as a count, not an actual type.
ResourceTypeCount
};
class ResourceLimit final : public Object {
@ -55,61 +49,51 @@ public:
* @param resource Requested 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.
* @param resource Requested 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;
/// Max thread priority that a process in this category can create
s32 max_priority = 0;
/// Max memory that processes in this category can use
s32 max_commit = 0;
///< Max number of objects that can be collectively created by the processes in this category
s32 max_threads = 0;
s32 max_events = 0;
s32 max_mutexes = 0;
s32 max_semaphores = 0;
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;
/**
* 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);
private:
explicit ResourceLimit(KernelCore& kernel);
~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

View File

@ -736,13 +736,6 @@ static ResultCode SetThreadPriority(Handle handle, u32 priority) {
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);
if (!thread) {
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();
const ResourceLimit& resource_limit = current_process->GetResourceLimit();
if (resource_limit.GetMaxResourceValue(ResourceType::Priority) > priority) {
return ERR_INVALID_THREAD_PRIORITY;
}
if (processor_id == THREADPROCESSORID_DEFAULT) {
// Set the target CPU to the one specified in the process' exheader.