2022-04-23 10:59:50 +02:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2021-01-30 10:40:49 +01:00
|
|
|
|
|
|
|
#include "common/assert.h"
|
2022-02-21 21:29:19 +01:00
|
|
|
#include "core/core.h"
|
2021-01-30 10:40:49 +01:00
|
|
|
#include "core/core_timing.h"
|
|
|
|
#include "core/hle/kernel/k_resource_limit.h"
|
|
|
|
#include "core/hle/kernel/svc_results.h"
|
|
|
|
|
|
|
|
namespace Kernel {
|
2021-01-30 11:03:10 +01:00
|
|
|
constexpr s64 DefaultTimeout = 10000000000; // 10 seconds
|
2021-01-30 10:40:49 +01:00
|
|
|
|
2021-05-08 18:11:36 +02:00
|
|
|
KResourceLimit::KResourceLimit(KernelCore& kernel_)
|
|
|
|
: KAutoObjectWithSlabHeapAndContainer{kernel_}, lock{kernel_}, cond_var{kernel_} {}
|
2021-01-30 10:40:49 +01:00
|
|
|
KResourceLimit::~KResourceLimit() = default;
|
|
|
|
|
2021-04-21 06:28:11 +02:00
|
|
|
void KResourceLimit::Initialize(const Core::Timing::CoreTiming* core_timing_) {
|
|
|
|
core_timing = core_timing_;
|
|
|
|
}
|
|
|
|
|
|
|
|
void KResourceLimit::Finalize() {}
|
|
|
|
|
2021-01-30 10:40:49 +01:00
|
|
|
s64 KResourceLimit::GetLimitValue(LimitableResource which) const {
|
|
|
|
const auto index = static_cast<std::size_t>(which);
|
|
|
|
s64 value{};
|
|
|
|
{
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk{lock};
|
2021-01-30 10:40:49 +01:00
|
|
|
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{};
|
|
|
|
{
|
2021-01-30 11:51:22 +01:00
|
|
|
KScopedLightLock lk{lock};
|
2021-01-30 10:40:49 +01:00
|
|
|
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{};
|
|
|
|
{
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk{lock};
|
2021-01-30 10:40:49 +01:00
|
|
|
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{};
|
|
|
|
{
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk(lock);
|
2021-01-30 10:40:49 +01:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-06-26 05:44:19 +02:00
|
|
|
Result KResourceLimit::SetLimitValue(LimitableResource which, s64 value) {
|
2021-01-30 10:40:49 +01:00
|
|
|
const auto index = static_cast<std::size_t>(which);
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk(lock);
|
2021-02-13 00:43:01 +01:00
|
|
|
R_UNLESS(current_values[index] <= value, ResultInvalidState);
|
2021-01-30 10:40:49 +01:00
|
|
|
|
|
|
|
limit_values[index] = value;
|
2021-06-18 13:27:48 +02:00
|
|
|
peak_values[index] = current_values[index];
|
2021-01-30 10:40:49 +01:00
|
|
|
|
2021-05-21 07:05:04 +02:00
|
|
|
return ResultSuccess;
|
2021-01-30 10:40:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool KResourceLimit::Reserve(LimitableResource which, s64 value) {
|
2021-04-21 06:28:11 +02:00
|
|
|
return Reserve(which, value, core_timing->GetGlobalTimeNs().count() + DefaultTimeout);
|
2021-01-30 10:40:49 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool KResourceLimit::Reserve(LimitableResource which, s64 value, s64 timeout) {
|
|
|
|
ASSERT(value >= 0);
|
|
|
|
const auto index = static_cast<std::size_t>(which);
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk(lock);
|
2021-01-30 10:40:49 +01:00
|
|
|
|
|
|
|
ASSERT(current_hints[index] <= current_values[index]);
|
|
|
|
if (current_hints[index] >= limit_values[index]) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-01-30 11:20:35 +01:00
|
|
|
// Loop until we reserve or run out of time.
|
2021-01-30 10:40:49 +01:00
|
|
|
while (true) {
|
|
|
|
ASSERT(current_values[index] <= limit_values[index]);
|
|
|
|
ASSERT(current_hints[index] <= current_values[index]);
|
|
|
|
|
2021-01-30 11:20:35 +01:00
|
|
|
// If we would overflow, don't allow to succeed.
|
2021-01-30 10:40:49 +01:00
|
|
|
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] &&
|
2021-04-21 06:28:11 +02:00
|
|
|
(timeout < 0 || core_timing->GetGlobalTimeNs().count() < timeout)) {
|
2021-01-30 10:40:49 +01:00
|
|
|
waiter_count++;
|
2021-06-10 19:56:35 +02:00
|
|
|
cond_var.Wait(&lock, timeout, false);
|
2021-01-30 10:40:49 +01:00
|
|
|
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);
|
2021-01-30 11:19:49 +01:00
|
|
|
KScopedLightLock lk(lock);
|
2021-01-30 10:40:49 +01:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-21 21:29:19 +01:00
|
|
|
KResourceLimit* CreateResourceLimitForProcess(Core::System& system, s64 physical_memory_size) {
|
|
|
|
auto* resource_limit = KResourceLimit::Create(system.Kernel());
|
|
|
|
resource_limit->Initialize(&system.CoreTiming());
|
|
|
|
|
|
|
|
// Initialize default resource limit values.
|
|
|
|
// TODO(bunnei): These values are the system defaults, the limits for service processes are
|
|
|
|
// lower. These should use the correct limit values.
|
|
|
|
|
2022-11-03 15:22:05 +01:00
|
|
|
ASSERT(resource_limit->SetLimitValue(LimitableResource::PhysicalMemoryMax, physical_memory_size)
|
2022-02-21 21:29:19 +01:00
|
|
|
.IsSuccess());
|
2022-11-03 15:22:05 +01:00
|
|
|
ASSERT(resource_limit->SetLimitValue(LimitableResource::ThreadCountMax, 800).IsSuccess());
|
|
|
|
ASSERT(resource_limit->SetLimitValue(LimitableResource::EventCountMax, 900).IsSuccess());
|
|
|
|
ASSERT(
|
|
|
|
resource_limit->SetLimitValue(LimitableResource::TransferMemoryCountMax, 200).IsSuccess());
|
|
|
|
ASSERT(resource_limit->SetLimitValue(LimitableResource::SessionCountMax, 1133).IsSuccess());
|
2022-02-21 21:29:19 +01:00
|
|
|
|
|
|
|
return resource_limit;
|
|
|
|
}
|
|
|
|
|
2021-01-30 10:40:49 +01:00
|
|
|
} // namespace Kernel
|