Browse Source
kernel: Rewrite resource limit to be more accurate
kernel: Rewrite resource limit to be more accurate
Matches closer to hardwarepull/15/merge
13 changed files with 357 additions and 231 deletions
-
5src/core/CMakeLists.txt
-
60src/core/hle/kernel/k_light_condition_variable.h
-
155src/core/hle/kernel/k_resource_limit.cpp
-
80src/core/hle/kernel/k_resource_limit.h
-
4src/core/hle/kernel/k_thread.cpp
-
30src/core/hle/kernel/kernel.cpp
-
4src/core/hle/kernel/kernel.h
-
13src/core/hle/kernel/memory/page_table.cpp
-
24src/core/hle/kernel/process.cpp
-
6src/core/hle/kernel/process.h
-
73src/core/hle/kernel/resource_limit.cpp
-
106src/core/hle/kernel/resource_limit.h
-
28src/core/hle/kernel/svc.cpp
@ -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 |
||||
@ -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
|
||||
@ -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 |
||||
@ -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 |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue