Browse Source
Merge pull request #5848 from ogniK5377/k-resourcelimit
Merge pull request #5848 from ogniK5377/k-resourcelimit
kernel: Rewrite resource limit to be more accuratepull/15/merge
committed by
GitHub
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 343 additions and 230 deletions
-
5src/core/CMakeLists.txt
-
57src/core/hle/kernel/k_light_condition_variable.h
-
152src/core/hle/kernel/k_resource_limit.cpp
-
81src/core/hle/kernel/k_resource_limit.h
-
4src/core/hle/kernel/k_thread.cpp
-
26src/core/hle/kernel/kernel.cpp
-
4src/core/hle/kernel/kernel.h
-
10src/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
-
25src/core/hle/kernel/svc.cpp
@ -0,0 +1,57 @@ |
|||
// 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 { |
|||
public: |
|||
explicit KLightConditionVariable(KernelCore& kernel) : thread_queue(kernel), kernel(kernel) {} |
|||
|
|||
void Wait(KLightLock* lock, s64 timeout = -1) { |
|||
WaitImpl(lock, timeout); |
|||
lock->Lock(); |
|||
} |
|||
|
|||
void Broadcast() { |
|||
KScopedSchedulerLock lk{kernel}; |
|||
while (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); |
|||
|
|||
// Sleep the thread. |
|||
{ |
|||
KScopedSchedulerLockAndSleep lk(kernel, owner, timeout); |
|||
lock->Unlock(); |
|||
|
|||
if (!thread_queue.SleepThread(owner)) { |
|||
lk.CancelSleep(); |
|||
return; |
|||
} |
|||
} |
|||
|
|||
// Cancel the task that the sleep setup. |
|||
kernel.TimeManager().UnscheduleTimeEvent(owner); |
|||
} |
|||
KThreadQueue thread_queue; |
|||
KernelCore& kernel; |
|||
}; |
|||
} // namespace Kernel |
|||
@ -0,0 +1,152 @@ |
|||
// 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 { |
|||
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
|
|||
|
|||
KResourceLimit::KResourceLimit(KernelCore& kernel, Core::System& system) |
|||
: Object{kernel}, 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{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{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{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(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(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().GetGlobalTimeNs().count() + DefaultTimeout); |
|||
} |
|||
|
|||
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) { |
|||
ASSERT(value >= 0); |
|||
const auto index = static_cast<std::size_t>(which); |
|||
KScopedLightLock lk(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().GetGlobalTimeNs().count() < timeout)) { |
|||
waiter_count++; |
|||
cond_var.Wait(&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(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,81 @@ |
|||
// 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 { |
|||
PhysicalMemory = 0, |
|||
Threads = 1, |
|||
Events = 2, |
|||
TransferMemory = 3, |
|||
Sessions = 4, |
|||
|
|||
Count, |
|||
}; |
|||
|
|||
constexpr bool IsValidResourceType(LimitableResource type) { |
|||
return type < LimitableResource::Count; |
|||
} |
|||
|
|||
class KResourceLimit final : public Object { |
|||
public: |
|||
explicit 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: |
|||
using ResourceArray = std::array<s64, static_cast<std::size_t>(LimitableResource::Count)>; |
|||
ResourceArray limit_values{}; |
|||
ResourceArray current_values{}; |
|||
ResourceArray current_hints{}; |
|||
ResourceArray peak_values{}; |
|||
mutable KLightLock 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