Revert "hoc-clk: add live vdd2, live boost clock and basic pwm dimming"
This reverts commit 15b7df8ef1.
This commit is contained in:
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem::buffers {
|
||||
|
||||
namespace {
|
||||
|
||||
/* TODO: os::SdkThreadLocalStorage g_buffer_manager_context_tls_slot; */
|
||||
|
||||
class ThreadLocalStorageWrapper {
|
||||
private:
|
||||
os::TlsSlot m_tls_slot;
|
||||
public:
|
||||
ThreadLocalStorageWrapper() { R_ABORT_UNLESS(os::AllocateTlsSlot(std::addressof(m_tls_slot), nullptr)); }
|
||||
~ThreadLocalStorageWrapper() { os::FreeTlsSlot(m_tls_slot); }
|
||||
|
||||
void SetValue(uintptr_t value) { os::SetTlsValue(m_tls_slot, value); }
|
||||
uintptr_t GetValue() const { return os::GetTlsValue(m_tls_slot); }
|
||||
os::TlsSlot GetTlsSlot() const { return m_tls_slot; }
|
||||
} g_buffer_manager_context_tls_slot;
|
||||
|
||||
}
|
||||
|
||||
void RegisterBufferManagerContext(const BufferManagerContext *context) {
|
||||
g_buffer_manager_context_tls_slot.SetValue(reinterpret_cast<uintptr_t>(context));
|
||||
}
|
||||
|
||||
BufferManagerContext *GetBufferManagerContext() {
|
||||
return reinterpret_cast<BufferManagerContext *>(g_buffer_manager_context_tls_slot.GetValue());
|
||||
}
|
||||
|
||||
void EnableBlockingBufferManagerAllocation() {
|
||||
if (auto context = GetBufferManagerContext(); context != nullptr) {
|
||||
context->SetNeedBlocking(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,360 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
FileSystemBuddyHeap::PageEntry *FileSystemBuddyHeap::PageList::PopFront() {
|
||||
AMS_ASSERT(m_entry_count > 0);
|
||||
|
||||
/* Get the first entry. */
|
||||
auto page_entry = m_first_page_entry;
|
||||
|
||||
/* Advance our list. */
|
||||
m_first_page_entry = page_entry->next;
|
||||
page_entry->next = nullptr;
|
||||
|
||||
/* Decrement our count. */
|
||||
--m_entry_count;
|
||||
AMS_ASSERT(m_entry_count >= 0);
|
||||
|
||||
/* If this was our last page, clear our last entry. */
|
||||
if (m_entry_count == 0) {
|
||||
m_last_page_entry = nullptr;
|
||||
}
|
||||
|
||||
return page_entry;
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::PageList::PushBack(PageEntry *page_entry) {
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
|
||||
/* If we're empty, we want to set the first page entry. */
|
||||
if (this->IsEmpty()) {
|
||||
m_first_page_entry = page_entry;
|
||||
} else {
|
||||
/* We're not empty, so push the page to the back. */
|
||||
AMS_ASSERT(m_last_page_entry != page_entry);
|
||||
m_last_page_entry->next = page_entry;
|
||||
}
|
||||
|
||||
/* Set our last page entry to be this one, and link it to the list. */
|
||||
m_last_page_entry = page_entry;
|
||||
m_last_page_entry->next = nullptr;
|
||||
|
||||
/* Increment our entry count. */
|
||||
++m_entry_count;
|
||||
AMS_ASSERT(m_entry_count > 0);
|
||||
}
|
||||
|
||||
bool FileSystemBuddyHeap::PageList::Remove(PageEntry *page_entry) {
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
|
||||
/* If we're empty, we can't remove the page list. */
|
||||
if (this->IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* We're going to loop over all pages to find this one, then unlink it. */
|
||||
PageEntry *prev_entry = nullptr;
|
||||
PageEntry *cur_entry = m_first_page_entry;
|
||||
|
||||
while (true) {
|
||||
/* Check if we found the page. */
|
||||
if (cur_entry == page_entry) {
|
||||
if (cur_entry == m_first_page_entry) {
|
||||
/* If it's the first page, we just set our first. */
|
||||
m_first_page_entry = cur_entry->next;
|
||||
} else if (cur_entry == m_last_page_entry) {
|
||||
/* If it's the last page, we set our last. */
|
||||
m_last_page_entry = prev_entry;
|
||||
m_last_page_entry->next = nullptr;
|
||||
} else {
|
||||
/* If it's in the middle, we just unlink. */
|
||||
prev_entry->next = cur_entry->next;
|
||||
}
|
||||
|
||||
/* Unlink this entry's next. */
|
||||
cur_entry->next = nullptr;
|
||||
|
||||
/* Update our entry count. */
|
||||
--m_entry_count;
|
||||
AMS_ASSERT(m_entry_count >= 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* If we have no next page, we can't remove. */
|
||||
if (cur_entry->next == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Advance to the next item in the list. */
|
||||
prev_entry = cur_entry;
|
||||
cur_entry = cur_entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
Result FileSystemBuddyHeap::Initialize(uintptr_t address, size_t size, size_t block_size, s32 order_max) {
|
||||
/* Ensure our preconditions. */
|
||||
AMS_ASSERT(m_free_lists == nullptr);
|
||||
AMS_ASSERT(address != 0);
|
||||
AMS_ASSERT(util::IsAligned(address, BufferAlignment));
|
||||
AMS_ASSERT(block_size >= BlockSizeMin);
|
||||
AMS_ASSERT(util::IsPowerOfTwo(block_size));
|
||||
AMS_ASSERT(size >= block_size);
|
||||
AMS_ASSERT(order_max > 0);
|
||||
AMS_ASSERT(order_max < OrderUpperLimit);
|
||||
|
||||
/* Set up our basic member variables */
|
||||
m_block_size = block_size;
|
||||
m_order_max = order_max;
|
||||
m_heap_start = address;
|
||||
m_heap_size = (size / m_block_size) * m_block_size;
|
||||
|
||||
m_total_free_size = 0;
|
||||
|
||||
/* Determine page sizes. */
|
||||
const auto max_page_size = m_block_size << m_order_max;
|
||||
const auto max_page_count = util::AlignUp(m_heap_size, max_page_size) / max_page_size;
|
||||
AMS_ASSERT(max_page_count > 0);
|
||||
|
||||
/* Setup the free lists. */
|
||||
if (m_external_free_lists != nullptr) {
|
||||
AMS_ASSERT(m_internal_free_lists == nullptr);
|
||||
m_free_lists = m_external_free_lists;
|
||||
} else {
|
||||
m_internal_free_lists.reset(new PageList[m_order_max + 1]);
|
||||
m_free_lists = m_internal_free_lists.get();
|
||||
R_UNLESS(m_free_lists != nullptr, fs::ResultAllocationMemoryFailedInFileSystemBuddyHeapA());
|
||||
}
|
||||
|
||||
/* All but the last page region should go to the max order. */
|
||||
for (size_t i = 0; i < max_page_count - 1; i++) {
|
||||
auto page_entry = this->GetPageEntryFromAddress(m_heap_start + i * max_page_size);
|
||||
m_free_lists[m_order_max].PushBack(page_entry);
|
||||
}
|
||||
m_total_free_size += m_free_lists[m_order_max].GetSize() * this->GetBytesFromOrder(m_order_max);
|
||||
|
||||
/* Allocate remaining space to smaller orders as possible. */
|
||||
{
|
||||
auto remaining = m_heap_size - (max_page_count - 1) * max_page_size;
|
||||
auto cur_address = m_heap_start + (max_page_count - 1) * max_page_size;
|
||||
AMS_ASSERT(util::IsAligned(remaining, m_block_size));
|
||||
|
||||
do {
|
||||
/* Determine what order we can use. */
|
||||
auto order = GetOrderFromBytes(remaining + 1);
|
||||
if (order < 0) {
|
||||
AMS_ASSERT(GetOrderFromBytes(remaining) == m_order_max);
|
||||
order = m_order_max + 1;
|
||||
}
|
||||
AMS_ASSERT(0 < order);
|
||||
AMS_ASSERT(order <= m_order_max + 1);
|
||||
|
||||
/* Add to the correct free list. */
|
||||
m_free_lists[order - 1].PushBack(GetPageEntryFromAddress(cur_address));
|
||||
m_total_free_size += GetBytesFromOrder(order - 1);
|
||||
|
||||
/* Move on to the next order. */
|
||||
const auto page_size = GetBytesFromOrder(order - 1);
|
||||
cur_address += page_size;
|
||||
remaining -= page_size;
|
||||
} while (m_block_size <= remaining);
|
||||
}
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::Finalize() {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
m_free_lists = nullptr;
|
||||
m_external_free_lists = nullptr;
|
||||
m_internal_free_lists.reset();
|
||||
}
|
||||
|
||||
void *FileSystemBuddyHeap::AllocateByOrder(s32 order) {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
AMS_ASSERT(order >= 0);
|
||||
AMS_ASSERT(order <= this->GetOrderMax());
|
||||
|
||||
/* Get the page entry. */
|
||||
if (const auto page_entry = this->GetFreePageEntry(order); page_entry != nullptr) {
|
||||
/* Ensure we're allocating an unlinked page. */
|
||||
AMS_ASSERT(page_entry->next == nullptr);
|
||||
|
||||
/* Return the address for this entry. */
|
||||
return reinterpret_cast<void *>(this->GetAddressFromPageEntry(*page_entry));
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::Free(void *ptr, s32 order) {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
AMS_ASSERT(order >= 0);
|
||||
AMS_ASSERT(order <= this->GetOrderMax());
|
||||
|
||||
/* Allow free(nullptr) */
|
||||
if (ptr == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the pointer is block aligned. */
|
||||
AMS_ASSERT(util::IsAligned(reinterpret_cast<uintptr_t>(ptr) - m_heap_start, this->GetBlockSize()));
|
||||
|
||||
/* Get the page entry. */
|
||||
auto page_entry = this->GetPageEntryFromAddress(reinterpret_cast<uintptr_t>(ptr));
|
||||
AMS_ASSERT(this->IsAlignedToOrder(page_entry, order));
|
||||
|
||||
/* Reinsert into the free lists. */
|
||||
this->JoinBuddies(page_entry, order);
|
||||
}
|
||||
|
||||
size_t FileSystemBuddyHeap::GetTotalFreeSize() const {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
return m_total_free_size;
|
||||
}
|
||||
|
||||
size_t FileSystemBuddyHeap::GetAllocatableSizeMax() const {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
|
||||
/* The maximum allocatable size is a chunk from the biggest non-empty order. */
|
||||
for (s32 order = this->GetOrderMax(); order >= 0; --order) {
|
||||
if (!m_free_lists[order].IsEmpty()) {
|
||||
return this->GetBytesFromOrder(order);
|
||||
}
|
||||
}
|
||||
|
||||
/* If all orders are empty, then we can't allocate anything. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::Dump() const {
|
||||
AMS_ASSERT(m_free_lists != nullptr);
|
||||
/* TODO: Support logging metrics. */
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::DivideBuddies(PageEntry *page_entry, s32 required_order, s32 chosen_order) {
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
AMS_ASSERT(required_order >= 0);
|
||||
AMS_ASSERT(chosen_order >= required_order);
|
||||
AMS_ASSERT(chosen_order <= this->GetOrderMax());
|
||||
|
||||
/* Start at the end of the entry. */
|
||||
auto address = this->GetAddressFromPageEntry(*page_entry) + this->GetBytesFromOrder(chosen_order);
|
||||
|
||||
for (auto order = chosen_order; order > required_order; --order) {
|
||||
/* For each order, subtract that order's size from the address to get the start of a new block. */
|
||||
address -= this->GetBytesFromOrder(order - 1);
|
||||
auto divided_entry = this->GetPageEntryFromAddress(address);
|
||||
|
||||
/* Push back to the list. */
|
||||
m_free_lists[order - 1].PushBack(divided_entry);
|
||||
m_total_free_size += this->GetBytesFromOrder(order - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void FileSystemBuddyHeap::JoinBuddies(PageEntry *page_entry, s32 order) {
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
AMS_ASSERT(order >= 0);
|
||||
AMS_ASSERT(order <= this->GetOrderMax());
|
||||
|
||||
auto cur_entry = page_entry;
|
||||
auto cur_order = order;
|
||||
|
||||
while (cur_order < this->GetOrderMax()) {
|
||||
/* Get the buddy page. */
|
||||
const auto buddy_entry = this->GetBuddy(cur_entry, cur_order);
|
||||
|
||||
/* Check whether the buddy is in the relevant free list. */
|
||||
if (buddy_entry != nullptr && m_free_lists[cur_order].Remove(buddy_entry)) {
|
||||
m_total_free_size -= GetBytesFromOrder(cur_order);
|
||||
|
||||
/* Ensure we coalesce with the correct buddy when page is aligned */
|
||||
if (!this->IsAlignedToOrder(cur_entry, cur_order + 1)) {
|
||||
cur_entry = buddy_entry;
|
||||
}
|
||||
|
||||
++cur_order;
|
||||
} else {
|
||||
/* Buddy isn't in the free list, so we can't coalesce. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Insert the coalesced entry into the free list. */
|
||||
m_free_lists[cur_order].PushBack(cur_entry);
|
||||
m_total_free_size += this->GetBytesFromOrder(cur_order);
|
||||
}
|
||||
|
||||
FileSystemBuddyHeap::PageEntry *FileSystemBuddyHeap::GetBuddy(PageEntry *page_entry, s32 order) {
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
AMS_ASSERT(order >= 0);
|
||||
AMS_ASSERT(order <= this->GetOrderMax());
|
||||
|
||||
const auto address = this->GetAddressFromPageEntry(*page_entry);
|
||||
const auto offset = this->GetBlockCountFromOrder(order) * this->GetBlockSize();
|
||||
|
||||
if (this->IsAlignedToOrder(page_entry, order + 1)) {
|
||||
/* If the page entry is aligned to the next order, return the buddy block to the right of the current entry. */
|
||||
return (address + offset < m_heap_start + m_heap_size) ? GetPageEntryFromAddress(address + offset) : nullptr;
|
||||
} else {
|
||||
/* If the page entry isn't aligned, return the buddy block to the left of the current entry. */
|
||||
return (m_heap_start <= address - offset) ? GetPageEntryFromAddress(address - offset) : nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
FileSystemBuddyHeap::PageEntry *FileSystemBuddyHeap::GetFreePageEntry(s32 order) {
|
||||
AMS_ASSERT(order >= 0);
|
||||
AMS_ASSERT(order <= this->GetOrderMax());
|
||||
|
||||
/* Try orders from low to high until we find a free page entry. */
|
||||
for (auto cur_order = order; cur_order <= this->GetOrderMax(); cur_order++) {
|
||||
if (auto &free_list = m_free_lists[cur_order]; !free_list.IsEmpty()) {
|
||||
/* The current list isn't empty, so grab an entry from it. */
|
||||
PageEntry *page_entry = free_list.PopFront();
|
||||
AMS_ASSERT(page_entry != nullptr);
|
||||
|
||||
/* Update size bookkeeping. */
|
||||
m_total_free_size -= GetBytesFromOrder(cur_order);
|
||||
|
||||
/* If we allocated more memory than needed, free the unneeded portion. */
|
||||
this->DivideBuddies(page_entry, order, cur_order);
|
||||
AMS_ASSERT(page_entry->next == nullptr);
|
||||
|
||||
/* Return the newly-divided entry. */
|
||||
return page_entry;
|
||||
}
|
||||
}
|
||||
|
||||
/* We failed to find a free page. */
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
s32 FileSystemBuddyHeap::GetOrderFromBlockCount(s32 block_count) const {
|
||||
AMS_ASSERT(block_count >= 0);
|
||||
|
||||
/* Return the first order with a big enough block count. */
|
||||
for (s32 order = 0; order <= this->GetOrderMax(); ++order) {
|
||||
if (block_count <= this->GetBlockCountFromOrder(order)) {
|
||||
return order;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,419 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Atmosphère-NX
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <stratosphere.hpp>
|
||||
|
||||
namespace ams::fssystem {
|
||||
|
||||
Result FileSystemBufferManager::CacheHandleTable::Initialize(s32 max_cache_count) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries == nullptr);
|
||||
AMS_ASSERT(m_internal_entry_buffer == nullptr);
|
||||
|
||||
/* If we don't have an external buffer, try to allocate an internal one. */
|
||||
if (m_external_entry_buffer == nullptr) {
|
||||
m_entry_buffer_size = sizeof(Entry) * max_cache_count;
|
||||
m_internal_entry_buffer = fs::impl::MakeUnique<char[]>(m_entry_buffer_size);
|
||||
}
|
||||
|
||||
/* We need to have at least one entry buffer. */
|
||||
R_UNLESS(m_internal_entry_buffer != nullptr || m_external_entry_buffer != nullptr, fs::ResultAllocationMemoryFailedInFileSystemBufferManagerA());
|
||||
|
||||
/* Set entries. */
|
||||
m_entries = reinterpret_cast<Entry *>(m_external_entry_buffer != nullptr ? m_external_entry_buffer : m_internal_entry_buffer.get());
|
||||
m_entry_count = 0;
|
||||
m_entry_count_max = max_cache_count;
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
|
||||
m_cache_count_min = max_cache_count / 16;
|
||||
m_cache_size_min = m_cache_count_min * 0x100;
|
||||
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::CacheHandleTable::Finalize() {
|
||||
if (m_entries != nullptr) {
|
||||
AMS_ASSERT(m_entry_count == 0);
|
||||
|
||||
if (m_external_attr_info_buffer == nullptr) {
|
||||
auto it = m_attr_list.begin();
|
||||
while (it != m_attr_list.end()) {
|
||||
const auto attr_info = std::addressof(*it);
|
||||
it = m_attr_list.erase(it);
|
||||
delete attr_info;
|
||||
}
|
||||
}
|
||||
|
||||
m_internal_entry_buffer.reset();
|
||||
m_external_entry_buffer = nullptr;
|
||||
m_entry_buffer_size = 0;
|
||||
m_entries = nullptr;
|
||||
m_total_cache_size = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSystemBufferManager::CacheHandleTable::Register(CacheHandle *out, uintptr_t address, size_t size, const BufferAttribute &attr) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
AMS_ASSERT(out != nullptr);
|
||||
|
||||
/* Get the entry. */
|
||||
auto entry = this->AcquireEntry(address, size, attr);
|
||||
|
||||
/* If we don't have an entry, we can't register. */
|
||||
if (entry == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Get the attr info. If we have one, increment. */
|
||||
if (const auto attr_info = this->FindAttrInfo(attr); attr_info != nullptr) {
|
||||
attr_info->IncrementCacheCount();
|
||||
attr_info->AddCacheSize(size);
|
||||
} else {
|
||||
/* Make a new attr info and add it to the list. */
|
||||
AttrInfo *new_info = nullptr;
|
||||
|
||||
if (m_external_attr_info_buffer == nullptr) {
|
||||
new_info = new AttrInfo(attr.GetLevel(), 1, size);
|
||||
} else if (0 <= attr.GetLevel() && attr.GetLevel() < m_external_attr_info_count) {
|
||||
void *buffer = m_external_attr_info_buffer + attr.GetLevel() * sizeof(AttrInfo);
|
||||
new_info = std::construct_at(reinterpret_cast<AttrInfo *>(buffer), attr.GetLevel(), 1, size);
|
||||
}
|
||||
|
||||
/* If we failed to make a new attr info, we can't register. */
|
||||
if (new_info == nullptr) {
|
||||
this->ReleaseEntry(entry);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_attr_list.push_back(*new_info);
|
||||
}
|
||||
|
||||
m_total_cache_size += size;
|
||||
*out = entry->GetHandle();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FileSystemBufferManager::CacheHandleTable::Unregister(uintptr_t *out_address, size_t *out_size, CacheHandle handle) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
AMS_ASSERT(out_address != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* Find the lower bound for the entry. */
|
||||
const auto entry = std::lower_bound(m_entries, m_entries + m_entry_count, handle, [](const Entry &entry, CacheHandle handle) {
|
||||
return entry.GetHandle() < handle;
|
||||
});
|
||||
|
||||
/* If the entry is a match, unregister it. */
|
||||
if (entry != m_entries + m_entry_count && entry->GetHandle() == handle) {
|
||||
this->UnregisterCore(out_address, out_size, entry);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool FileSystemBufferManager::CacheHandleTable::UnregisterOldest(uintptr_t *out_address, size_t *out_size, const BufferAttribute &attr, size_t required_size) {
|
||||
AMS_UNUSED(attr, required_size);
|
||||
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
AMS_ASSERT(out_address != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
|
||||
/* If we have no entries, we can't unregister any. */
|
||||
if (m_entry_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto CanUnregister = [this](const Entry &entry) {
|
||||
const auto attr_info = this->FindAttrInfo(entry.GetBufferAttribute());
|
||||
AMS_ASSERT(attr_info != nullptr);
|
||||
|
||||
const auto ccm = this->GetCacheCountMin(entry.GetBufferAttribute());
|
||||
const auto csm = this->GetCacheSizeMin(entry.GetBufferAttribute());
|
||||
|
||||
return ccm < attr_info->GetCacheCount() && csm + entry.GetSize() <= attr_info->GetCacheSize();
|
||||
};
|
||||
|
||||
/* Find an entry, falling back to the first entry. */
|
||||
auto entry = std::find_if(m_entries, m_entries + m_entry_count, CanUnregister);
|
||||
if (entry == m_entries + m_entry_count) {
|
||||
entry = m_entries;
|
||||
}
|
||||
|
||||
AMS_ASSERT(entry != m_entries + m_entry_count);
|
||||
this->UnregisterCore(out_address, out_size, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::CacheHandleTable::UnregisterCore(uintptr_t *out_address, size_t *out_size, Entry *entry) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
AMS_ASSERT(out_address != nullptr);
|
||||
AMS_ASSERT(out_size != nullptr);
|
||||
AMS_ASSERT(entry != nullptr);
|
||||
|
||||
/* Get the attribute info. */
|
||||
const auto attr_info = this->FindAttrInfo(entry->GetBufferAttribute());
|
||||
AMS_ASSERT(attr_info != nullptr);
|
||||
AMS_ASSERT(attr_info->GetCacheCount() > 0);
|
||||
AMS_ASSERT(attr_info->GetCacheSize() >= entry->GetSize());
|
||||
|
||||
/* Release from the attr info. */
|
||||
attr_info->DecrementCacheCount();
|
||||
attr_info->SubtractCacheSize(entry->GetSize());
|
||||
|
||||
/* Release from cached size. */
|
||||
AMS_ASSERT(m_total_cache_size >= entry->GetSize());
|
||||
m_total_cache_size -= entry->GetSize();
|
||||
|
||||
/* Release the entry. */
|
||||
*out_address = entry->GetAddress();
|
||||
*out_size = entry->GetSize();
|
||||
this->ReleaseEntry(entry);
|
||||
}
|
||||
|
||||
FileSystemBufferManager::CacheHandle FileSystemBufferManager::CacheHandleTable::PublishCacheHandle() {
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
return (++m_current_handle);
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::CacheHandleTable::GetTotalCacheSize() const {
|
||||
return m_total_cache_size;
|
||||
}
|
||||
|
||||
FileSystemBufferManager::CacheHandleTable::Entry *FileSystemBufferManager::CacheHandleTable::AcquireEntry(uintptr_t address, size_t size, const BufferAttribute &attr) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
|
||||
Entry *entry = nullptr;
|
||||
if (m_entry_count < m_entry_count_max) {
|
||||
entry = m_entries + m_entry_count;
|
||||
entry->Initialize(this->PublishCacheHandle(), address, size, attr);
|
||||
++m_entry_count;
|
||||
AMS_ASSERT(m_entry_count == 1 || (entry-1)->GetHandle() < entry->GetHandle());
|
||||
}
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::CacheHandleTable::ReleaseEntry(Entry *entry) {
|
||||
/* Validate pre-conditions. */
|
||||
AMS_ASSERT(m_entries != nullptr);
|
||||
AMS_ASSERT(entry != nullptr);
|
||||
|
||||
/* Ensure the entry is valid. */
|
||||
{
|
||||
const auto entry_buffer = m_external_entry_buffer != nullptr ? m_external_entry_buffer : m_internal_entry_buffer.get();
|
||||
AMS_ASSERT(static_cast<void *>(entry_buffer) <= static_cast<void *>(entry));
|
||||
AMS_ASSERT(static_cast<void *>(entry) < static_cast<void *>(entry_buffer + m_entry_buffer_size));
|
||||
AMS_UNUSED(entry_buffer);
|
||||
}
|
||||
|
||||
/* Copy the entries back by one. */
|
||||
std::memmove(entry, entry + 1, sizeof(Entry) * (m_entry_count - ((entry + 1) - m_entries)));
|
||||
|
||||
/* Decrement our entry count. */
|
||||
--m_entry_count;
|
||||
}
|
||||
|
||||
FileSystemBufferManager::CacheHandleTable::AttrInfo *FileSystemBufferManager::CacheHandleTable::FindAttrInfo(const BufferAttribute &attr) {
|
||||
const auto it = std::find_if(m_attr_list.begin(), m_attr_list.end(), [&attr](const AttrInfo &info) {
|
||||
return attr.GetLevel() == info.GetLevel();
|
||||
});
|
||||
|
||||
return it != m_attr_list.end() ? std::addressof(*it) : nullptr;
|
||||
}
|
||||
|
||||
const fs::IBufferManager::MemoryRange FileSystemBufferManager::AllocateBufferImpl(size_t size, const BufferAttribute &attr) {
|
||||
/* Get/sanity check the required order. */
|
||||
fs::IBufferManager::MemoryRange range = {};
|
||||
const auto order = m_buddy_heap.GetOrderFromBytes(size);
|
||||
AMS_ASSERT(order >= 0);
|
||||
|
||||
while (true) {
|
||||
/* Try to allocate a buffer at the desired order. */
|
||||
if (auto address = m_buddy_heap.AllocateByOrder(order); address != 0) {
|
||||
/* Check that we allocated enough. */
|
||||
const auto allocated_size = m_buddy_heap.GetBytesFromOrder(order);
|
||||
AMS_ASSERT(size <= allocated_size);
|
||||
|
||||
/* Set up the range extents. */
|
||||
range.first = reinterpret_cast<uintptr_t>(address);
|
||||
range.second = allocated_size;
|
||||
|
||||
/* Update our peak tracking variables. */
|
||||
const size_t free_size = m_buddy_heap.GetTotalFreeSize();
|
||||
m_peak_free_size = std::min(m_peak_free_size, free_size);
|
||||
|
||||
const size_t total_allocatable_size = free_size + m_cache_handle_table.GetTotalCacheSize();
|
||||
m_peak_total_allocatable_size = std::min(m_peak_total_allocatable_size, total_allocatable_size);
|
||||
break;
|
||||
}
|
||||
|
||||
/* We failed, to we'll need to deallocate something and retry. */
|
||||
++m_retried_count;
|
||||
|
||||
/* Deallocate a buffer. */
|
||||
uintptr_t deallocate_address = 0;
|
||||
size_t deallocate_size = 0;
|
||||
if (m_cache_handle_table.UnregisterOldest(std::addressof(deallocate_address), std::addressof(deallocate_size), attr, size)) {
|
||||
this->DeallocateBufferImpl(deallocate_address, deallocate_size);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return the range we allocated. */
|
||||
return range;
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::DeallocateBufferImpl(uintptr_t address, size_t size) {
|
||||
AMS_ASSERT(util::IsPowerOfTwo(size));
|
||||
|
||||
m_buddy_heap.Free(reinterpret_cast<void *>(address), m_buddy_heap.GetOrderFromBytes(size));
|
||||
}
|
||||
|
||||
FileSystemBufferManager::CacheHandle FileSystemBufferManager::RegisterCacheImpl(uintptr_t address, size_t size, const BufferAttribute &attr) {
|
||||
CacheHandle handle = 0;
|
||||
while (true) {
|
||||
/* Try to register the handle. */
|
||||
if (m_cache_handle_table.Register(std::addressof(handle), address, size, attr)) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Deallocate a buffer. */
|
||||
uintptr_t deallocate_address = 0;
|
||||
size_t deallocate_size = 0;
|
||||
|
||||
++m_retried_count;
|
||||
if (m_cache_handle_table.UnregisterOldest(std::addressof(deallocate_address), std::addressof(deallocate_size), attr)) {
|
||||
this->DeallocateBufferImpl(deallocate_address, deallocate_size);
|
||||
} else {
|
||||
this->DeallocateBufferImpl(address, size);
|
||||
handle = m_cache_handle_table.PublishCacheHandle();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
const fs::IBufferManager::MemoryRange FileSystemBufferManager::AcquireCacheImpl(CacheHandle handle) {
|
||||
fs::IBufferManager::MemoryRange range = {};
|
||||
if (m_cache_handle_table.Unregister(std::addressof(range.first), std::addressof(range.second), handle)) {
|
||||
const size_t total_allocatable_size = m_buddy_heap.GetTotalFreeSize() + m_cache_handle_table.GetTotalCacheSize();
|
||||
m_peak_total_allocatable_size = std::min(m_peak_total_allocatable_size, total_allocatable_size);
|
||||
} else {
|
||||
range.first = 0;
|
||||
range.second = 0;
|
||||
}
|
||||
|
||||
return range;
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::GetFreeSizeImpl() const {
|
||||
return m_buddy_heap.GetTotalFreeSize();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::GetTotalAllocatableSizeImpl() const {
|
||||
return this->GetFreeSizeImpl() + m_cache_handle_table.GetTotalCacheSize();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::GetFreeSizePeakImpl() const {
|
||||
return m_peak_free_size;
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::GetTotalAllocatableSizePeakImpl() const {
|
||||
return m_peak_total_allocatable_size;
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::GetRetriedCountImpl() const {
|
||||
return m_retried_count;
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::ClearPeakImpl() {
|
||||
m_peak_free_size = this->GetFreeSizeImpl();
|
||||
m_peak_total_allocatable_size = this->GetTotalAllocatableSizeImpl();
|
||||
m_retried_count = 0;
|
||||
}
|
||||
|
||||
const fs::IBufferManager::MemoryRange FileSystemBufferManager::DoAllocateBuffer(size_t size, const BufferAttribute &attr) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->AllocateBufferImpl(size, attr);
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::DoDeallocateBuffer(uintptr_t address, size_t size) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->DeallocateBufferImpl(address, size);
|
||||
}
|
||||
|
||||
FileSystemBufferManager::CacheHandle FileSystemBufferManager::DoRegisterCache(uintptr_t address, size_t size, const BufferAttribute &attr) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->RegisterCacheImpl(address, size, attr);
|
||||
}
|
||||
|
||||
const fs::IBufferManager::MemoryRange FileSystemBufferManager::DoAcquireCache(CacheHandle handle) {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->AcquireCacheImpl(handle);
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetTotalSize() const {
|
||||
return m_total_size;
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetFreeSize() const {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->GetFreeSizeImpl();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetTotalAllocatableSize() const {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->GetTotalAllocatableSizeImpl();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetFreeSizePeak() const {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->GetFreeSizePeakImpl();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetTotalAllocatableSizePeak() const {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->GetTotalAllocatableSizePeakImpl();
|
||||
}
|
||||
|
||||
size_t FileSystemBufferManager::DoGetRetriedCount() const {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->GetRetriedCountImpl();
|
||||
}
|
||||
|
||||
void FileSystemBufferManager::DoClearPeak() {
|
||||
std::scoped_lock lk(m_mutex);
|
||||
|
||||
return this->ClearPeakImpl();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user