| | |
| | |
| |
|
| | #include <fstream> |
| | #include <vector> |
| |
|
| | #include "common/heap_tracker.h" |
| | #include "common/logging/log.h" |
| |
|
| | namespace Common { |
| |
|
| | namespace { |
| |
|
| | s64 GetMaxPermissibleResidentMapCount() { |
| | |
| | s64 value = 65530; |
| |
|
| | |
| | std::ifstream s("/proc/sys/vm/max_map_count"); |
| | s >> value; |
| |
|
| | |
| | LOG_INFO(HW_Memory, "Current maximum map count: {}", value); |
| |
|
| | |
| | return std::max<s64>(value - 20000, 0); |
| | } |
| |
|
| | } |
| |
|
| | HeapTracker::HeapTracker(Common::HostMemory& buffer) |
| | : m_buffer(buffer), m_max_resident_map_count(GetMaxPermissibleResidentMapCount()) {} |
| | HeapTracker::~HeapTracker() = default; |
| |
|
| | void HeapTracker::Map(size_t virtual_offset, size_t host_offset, size_t length, |
| | MemoryPermission perm, bool is_separate_heap) { |
| | |
| | if (!is_separate_heap) { |
| | m_buffer.Map(virtual_offset, host_offset, length, perm, false); |
| | return; |
| | } |
| |
|
| | { |
| | |
| | std::scoped_lock lk{m_lock}; |
| |
|
| | auto* const map = new SeparateHeapMap{ |
| | .vaddr = virtual_offset, |
| | .paddr = host_offset, |
| | .size = length, |
| | .tick = m_tick++, |
| | .perm = perm, |
| | .is_resident = false, |
| | }; |
| |
|
| | |
| | m_map_count++; |
| | m_mappings.insert(*map); |
| | } |
| |
|
| | |
| | this->DeferredMapSeparateHeap(virtual_offset); |
| | } |
| |
|
| | void HeapTracker::Unmap(size_t virtual_offset, size_t size, bool is_separate_heap) { |
| | |
| | if (is_separate_heap) { |
| | std::scoped_lock lk{m_lock}; |
| |
|
| | const SeparateHeapMap key{ |
| | .vaddr = virtual_offset, |
| | }; |
| |
|
| | |
| | this->SplitHeapMapLocked(virtual_offset); |
| | this->SplitHeapMapLocked(virtual_offset + size); |
| |
|
| | |
| | auto it = m_mappings.find(key); |
| | while (it != m_mappings.end() && it->vaddr < virtual_offset + size) { |
| | |
| | auto* const item = std::addressof(*it); |
| |
|
| | |
| | if (item->is_resident) { |
| | ASSERT(--m_resident_map_count >= 0); |
| | m_resident_mappings.erase(m_resident_mappings.iterator_to(*item)); |
| | } |
| |
|
| | |
| | ASSERT(--m_map_count >= 0); |
| | it = m_mappings.erase(it); |
| |
|
| | |
| | delete item; |
| | } |
| | } |
| |
|
| | |
| | m_buffer.Unmap(virtual_offset, size, false); |
| | } |
| |
|
| | void HeapTracker::Protect(size_t virtual_offset, size_t size, MemoryPermission perm) { |
| | |
| | std::shared_lock lk{m_rebuild_lock}; |
| |
|
| | |
| | this->SplitHeapMap(virtual_offset, size); |
| |
|
| | |
| | const VAddr end = virtual_offset + size; |
| | VAddr cur = virtual_offset; |
| |
|
| | while (cur < end) { |
| | VAddr next = cur; |
| | bool should_protect = false; |
| |
|
| | { |
| | std::scoped_lock lk2{m_lock}; |
| |
|
| | const SeparateHeapMap key{ |
| | .vaddr = next, |
| | }; |
| |
|
| | |
| | const auto it = m_mappings.nfind(key); |
| |
|
| | if (it == m_mappings.end()) { |
| | |
| | next = end; |
| | should_protect = true; |
| | } else if (it->vaddr == cur) { |
| | |
| | |
| | it->perm = perm; |
| |
|
| | |
| | next = cur + it->size; |
| | should_protect = it->is_resident; |
| | } else { |
| | |
| | next = it->vaddr; |
| | should_protect = true; |
| | } |
| | } |
| |
|
| | |
| | next = std::min(next, end); |
| |
|
| | |
| | if (should_protect) { |
| | m_buffer.Protect(cur, next - cur, perm); |
| | } |
| |
|
| | |
| | cur = next; |
| | } |
| | } |
| |
|
| | bool HeapTracker::DeferredMapSeparateHeap(u8* fault_address) { |
| | if (m_buffer.IsInVirtualRange(fault_address)) { |
| | return this->DeferredMapSeparateHeap(fault_address - m_buffer.VirtualBasePointer()); |
| | } |
| |
|
| | return false; |
| | } |
| |
|
| | bool HeapTracker::DeferredMapSeparateHeap(size_t virtual_offset) { |
| | bool rebuild_required = false; |
| |
|
| | { |
| | std::scoped_lock lk{m_lock}; |
| |
|
| | |
| | const auto it = this->GetNearestHeapMapLocked(virtual_offset); |
| | if (it == m_mappings.end() || it->is_resident) { |
| | return false; |
| | } |
| |
|
| | |
| | it->tick = m_tick++; |
| |
|
| | |
| | if (m_resident_map_count > m_max_resident_map_count) { |
| | rebuild_required = true; |
| | } |
| |
|
| | |
| | m_buffer.Map(it->vaddr, it->paddr, it->size, it->perm, false); |
| |
|
| | |
| | it->is_resident = true; |
| | m_resident_map_count++; |
| | m_resident_mappings.insert(*it); |
| | } |
| |
|
| | if (rebuild_required) { |
| | |
| | this->RebuildSeparateHeapAddressSpace(); |
| | } |
| |
|
| | return true; |
| | } |
| |
|
| | void HeapTracker::RebuildSeparateHeapAddressSpace() { |
| | std::scoped_lock lk{m_rebuild_lock, m_lock}; |
| |
|
| | ASSERT(!m_resident_mappings.empty()); |
| |
|
| | |
| | |
| | |
| | |
| | |
| | const size_t desired_count = std::min(m_resident_map_count, m_max_resident_map_count) / 2; |
| | const size_t evict_count = m_resident_map_count - desired_count; |
| | auto it = m_resident_mappings.begin(); |
| |
|
| | for (size_t i = 0; i < evict_count && it != m_resident_mappings.end(); i++) { |
| | |
| | it->is_resident = false; |
| | m_buffer.Unmap(it->vaddr, it->size, false); |
| |
|
| | |
| | ASSERT(--m_resident_map_count >= 0); |
| | it = m_resident_mappings.erase(it); |
| | } |
| | } |
| |
|
| | void HeapTracker::SplitHeapMap(VAddr offset, size_t size) { |
| | std::scoped_lock lk{m_lock}; |
| |
|
| | this->SplitHeapMapLocked(offset); |
| | this->SplitHeapMapLocked(offset + size); |
| | } |
| |
|
| | void HeapTracker::SplitHeapMapLocked(VAddr offset) { |
| | const auto it = this->GetNearestHeapMapLocked(offset); |
| | if (it == m_mappings.end() || it->vaddr == offset) { |
| | |
| | return; |
| | } |
| |
|
| | |
| | auto* const left = std::addressof(*it); |
| | const size_t orig_size = left->size; |
| |
|
| | |
| | const size_t left_size = offset - left->vaddr; |
| | left->size = left_size; |
| |
|
| | |
| | auto* const right = new SeparateHeapMap{ |
| | .vaddr = left->vaddr + left_size, |
| | .paddr = left->paddr + left_size, |
| | .size = orig_size - left_size, |
| | .tick = left->tick, |
| | .perm = left->perm, |
| | .is_resident = left->is_resident, |
| | }; |
| |
|
| | |
| | m_map_count++; |
| | m_mappings.insert(*right); |
| |
|
| | |
| | if (right->is_resident) { |
| | m_resident_map_count++; |
| | m_resident_mappings.insert(*right); |
| | } |
| | } |
| |
|
| | HeapTracker::AddrTree::iterator HeapTracker::GetNearestHeapMapLocked(VAddr offset) { |
| | const SeparateHeapMap key{ |
| | .vaddr = offset, |
| | }; |
| |
|
| | return m_mappings.find(key); |
| | } |
| |
|
| | } |
| |
|