From 3be1a565f895d5399a6c1f6d0997dc528537fe86 Mon Sep 17 00:00:00 2001 From: Chloe Marcec Date: Sat, 30 Jan 2021 20:40:49 +1100 Subject: kernel: Rewrite resource limit to be more accurate Matches closer to hardware --- src/core/hle/kernel/k_resource_limit.cpp | 155 +++++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 src/core/hle/kernel/k_resource_limit.cpp (limited to 'src/core/hle/kernel/k_resource_limit.cpp') diff --git a/src/core/hle/kernel/k_resource_limit.cpp b/src/core/hle/kernel/k_resource_limit.cpp new file mode 100644 index 000000000..f943d6562 --- /dev/null +++ b/src/core/hle/kernel/k_resource_limit.cpp @@ -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(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(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(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(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(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(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(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(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 -- cgit v1.2.3