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