Add partial unmap support (#322)

* Add partial unmap support

* undo accidental whitespace removal

* Fix assertions

* Adjust Reserve and Free functions for partial unmapping
This commit is contained in:
Borchev 2024-08-12 23:05:30 -07:00 committed by GitHub
parent a8b6a55559
commit 18f1799280
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 47 additions and 20 deletions

View File

@ -454,8 +454,28 @@ void* AddressSpace::MapFile(VAddr virtual_addr, size_t size, size_t offset, u32
#endif #endif
} }
void AddressSpace::Unmap(VAddr virtual_addr, size_t size, bool has_backing) { void AddressSpace::Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma,
return impl->Unmap(virtual_addr, size, has_backing); PAddr phys_base, bool is_exec, bool has_backing) {
#ifdef _WIN32
// There does not appear to be comparable support for partial unmapping on Windows.
// Unfortunately, a least one title was found to require this. The workaround is to unmap
// the entire allocation and remap the portions outside of the requested unmapping range.
impl->Unmap(virtual_addr, size, has_backing);
// TODO: Determine if any titles require partial unmapping support for flexible allocations.
ASSERT_MSG(has_backing || (start_in_vma == 0 && end_in_vma == size),
"Partial unmapping of flexible allocations is not supported");
if (start_in_vma != 0) {
Map(virtual_addr, start_in_vma, 0, phys_base, is_exec);
}
if (end_in_vma != size) {
Map(virtual_addr + end_in_vma, size - end_in_vma, 0, phys_base + end_in_vma, is_exec);
}
#else
impl->Unmap(virtual_addr + start_in_vma, end_in_vma - start_in_vma, has_backing);
#endif
} }
void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission perms) { void AddressSpace::Protect(VAddr virtual_addr, size_t size, MemoryPermission perms) {

View File

@ -91,7 +91,8 @@ public:
void* MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot, uintptr_t fd); void* MapFile(VAddr virtual_addr, size_t size, size_t offset, u32 prot, uintptr_t fd);
/// Unmaps specified virtual memory area. /// Unmaps specified virtual memory area.
void Unmap(VAddr virtual_addr, size_t size, bool has_backing); void Unmap(VAddr virtual_addr, size_t size, VAddr start_in_vma, VAddr end_in_vma,
PAddr phys_base, bool is_exec, bool has_backing);
void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms); void Protect(VAddr virtual_addr, size_t size, MemoryPermission perms);

View File

@ -54,7 +54,7 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr; free_addr = alignment > 0 ? Common::AlignUp(free_addr, alignment) : free_addr;
// Add the allocated region to the list and commit its pages. // Add the allocated region to the list and commit its pages.
auto& area = CarveDmemArea(free_addr, size); auto& area = CarveDmemArea(free_addr, size)->second;
area.memory_type = memory_type; area.memory_type = memory_type;
area.is_free = false; area.is_free = false;
return free_addr; return free_addr;
@ -63,9 +63,8 @@ PAddr MemoryManager::Allocate(PAddr search_start, PAddr search_end, size_t size,
void MemoryManager::Free(PAddr phys_addr, size_t size) { void MemoryManager::Free(PAddr phys_addr, size_t size) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
const auto dmem_area = FindDmemArea(phys_addr); auto dmem_area = CarveDmemArea(phys_addr, size);
ASSERT(dmem_area != dmem_map.end() && dmem_area->second.base == phys_addr && ASSERT(dmem_area != dmem_map.end() && dmem_area->second.size >= size);
dmem_area->second.size == size);
// Release any dmem mappings that reference this physical block. // Release any dmem mappings that reference this physical block.
std::vector<std::pair<VAddr, u64>> remove_list; std::vector<std::pair<VAddr, u64>> remove_list;
@ -74,10 +73,11 @@ void MemoryManager::Free(PAddr phys_addr, size_t size) {
continue; continue;
} }
if (mapping.phys_base <= phys_addr && phys_addr < mapping.phys_base + mapping.size) { if (mapping.phys_base <= phys_addr && phys_addr < mapping.phys_base + mapping.size) {
LOG_INFO(Kernel_Vmm, "Unmaping direct mapping {:#x} with size {:#x}", addr, auto vma_segment_start_addr = phys_addr - mapping.phys_base + addr;
mapping.size); LOG_INFO(Kernel_Vmm, "Unmaping direct mapping {:#x} with size {:#x}",
vma_segment_start_addr, size);
// Unmaping might erase from vma_map. We can't do it here. // Unmaping might erase from vma_map. We can't do it here.
remove_list.emplace_back(addr, mapping.size); remove_list.emplace_back(vma_segment_start_addr, size);
} }
} }
for (const auto& [addr, size] : remove_list) { for (const auto& [addr, size] : remove_list) {
@ -104,8 +104,6 @@ int MemoryManager::Reserve(void** out_addr, VAddr virtual_addr, size_t size, Mem
const auto& vma = FindVMA(mapped_addr)->second; const auto& vma = FindVMA(mapped_addr)->second;
// If the VMA is mapped, unmap the region first. // If the VMA is mapped, unmap the region first.
if (vma.IsMapped()) { if (vma.IsMapped()) {
ASSERT_MSG(vma.base == mapped_addr && vma.size == size,
"Region must match when reserving a mapped region");
UnmapMemory(mapped_addr, size); UnmapMemory(mapped_addr, size);
} }
const size_t remaining_size = vma.base + vma.size - mapped_addr; const size_t remaining_size = vma.base + vma.size - mapped_addr;
@ -169,6 +167,7 @@ int MemoryManager::MapMemory(void** out_addr, VAddr virtual_addr, size_t size, M
new_vma.prot = prot; new_vma.prot = prot;
new_vma.name = name; new_vma.name = name;
new_vma.type = type; new_vma.type = type;
new_vma.is_exec = is_exec;
if (type == VMAType::Direct) { if (type == VMAType::Direct) {
new_vma.phys_base = phys_addr; new_vma.phys_base = phys_addr;
@ -216,10 +215,16 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
std::scoped_lock lk{mutex}; std::scoped_lock lk{mutex};
const auto it = FindVMA(virtual_addr); const auto it = FindVMA(virtual_addr);
ASSERT_MSG(it->second.Contains(virtual_addr, size), const auto& vma_base = it->second;
ASSERT_MSG(vma_base.Contains(virtual_addr, size),
"Existing mapping does not contain requested unmap range"); "Existing mapping does not contain requested unmap range");
const auto type = it->second.type; const auto vma_base_addr = vma_base.base;
const auto vma_base_size = vma_base.size;
const auto phys_base = vma_base.phys_base;
const bool is_exec = vma_base.is_exec;
const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type;
const bool has_backing = type == VMAType::Direct || type == VMAType::File; const bool has_backing = type == VMAType::Direct || type == VMAType::File;
if (type == VMAType::Direct) { if (type == VMAType::Direct) {
rasterizer->UnmapMemory(virtual_addr, size); rasterizer->UnmapMemory(virtual_addr, size);
@ -239,7 +244,8 @@ void MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {
MergeAdjacent(vma_map, new_it); MergeAdjacent(vma_map, new_it);
// Unmap the memory region. // Unmap the memory region.
impl.Unmap(virtual_addr, size, has_backing); impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, is_exec,
has_backing);
TRACK_FREE(virtual_addr, "VMEM"); TRACK_FREE(virtual_addr, "VMEM");
} }
@ -397,13 +403,12 @@ MemoryManager::VMAHandle MemoryManager::CarveVMA(VAddr virtual_addr, size_t size
return vma_handle; return vma_handle;
} }
DirectMemoryArea& MemoryManager::CarveDmemArea(PAddr addr, size_t size) { MemoryManager::DMemHandle MemoryManager::CarveDmemArea(PAddr addr, size_t size) {
auto dmem_handle = FindDmemArea(addr); auto dmem_handle = FindDmemArea(addr);
ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map"); ASSERT_MSG(dmem_handle != dmem_map.end(), "Physical address not in dmem_map");
const DirectMemoryArea& area = dmem_handle->second; const DirectMemoryArea& area = dmem_handle->second;
ASSERT_MSG(area.is_free && area.base <= addr, ASSERT_MSG(area.base <= addr, "Adding an allocation to already allocated region");
"Adding an allocation to already allocated region");
const PAddr start_in_area = addr - area.base; const PAddr start_in_area = addr - area.base;
const PAddr end_in_vma = start_in_area + size; const PAddr end_in_vma = start_in_area + size;
@ -418,7 +423,7 @@ DirectMemoryArea& MemoryManager::CarveDmemArea(PAddr addr, size_t size) {
dmem_handle = Split(dmem_handle, start_in_area); dmem_handle = Split(dmem_handle, start_in_area);
} }
return dmem_handle->second; return dmem_handle;
} }
MemoryManager::VMAHandle MemoryManager::Split(VMAHandle vma_handle, size_t offset_in_vma) { MemoryManager::VMAHandle MemoryManager::Split(VMAHandle vma_handle, size_t offset_in_vma) {

View File

@ -84,6 +84,7 @@ struct VirtualMemoryArea {
bool disallow_merge = false; bool disallow_merge = false;
std::string name = ""; std::string name = "";
uintptr_t fd = 0; uintptr_t fd = 0;
bool is_exec = false;
bool Contains(VAddr addr, size_t size) const { bool Contains(VAddr addr, size_t size) const {
return addr >= base && (addr + size) <= (base + this->size); return addr >= base && (addr + size) <= (base + this->size);
@ -205,7 +206,7 @@ private:
VMAHandle CarveVMA(VAddr virtual_addr, size_t size); VMAHandle CarveVMA(VAddr virtual_addr, size_t size);
DirectMemoryArea& CarveDmemArea(PAddr addr, size_t size); DMemHandle CarveDmemArea(PAddr addr, size_t size);
VMAHandle Split(VMAHandle vma_handle, size_t offset_in_vma); VMAHandle Split(VMAHandle vma_handle, size_t offset_in_vma);