Track pool budgets

If you try to commit more pooled memory than is allocated, PoolCommit returns ENOMEM.
Also fixes error conditions for PoolDecommit, that should return EINVAL if given an address that isn't part of the pool.

Note: Seems like the pool budget can't hit zero? I used a <= comparison based on hardware tests, otherwise we're able to make more mappings than real hardware can.
This commit is contained in:
Stephen Miller 2025-05-10 16:23:30 -05:00
parent b2cb05eeb7
commit 17bea1069c
3 changed files with 38 additions and 7 deletions

View File

@ -477,9 +477,8 @@ s32 PS4_SYSV_ABI sceKernelMemoryPoolDecommit(void* addr, size_t len, int flags)
const VAddr pool_addr = reinterpret_cast<VAddr>(addr); const VAddr pool_addr = reinterpret_cast<VAddr>(addr);
auto* memory = Core::Memory::Instance(); auto* memory = Core::Memory::Instance();
memory->PoolDecommit(pool_addr, len);
return ORBIS_OK; return memory->PoolDecommit(pool_addr, len);
} }
int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset, int PS4_SYSV_ABI sceKernelMmap(void* addr, u64 len, int prot, int flags, int fd, size_t offset,

View File

@ -140,6 +140,10 @@ PAddr MemoryManager::PoolExpand(PAddr search_start, PAddr search_end, size_t siz
auto& area = CarveDmemArea(mapping_start, size)->second; auto& area = CarveDmemArea(mapping_start, size)->second;
area.is_free = false; area.is_free = false;
area.is_pooled = true; area.is_pooled = true;
// Track how much dmem was allocated for pools.
pool_budget += size;
return mapping_start; return mapping_start;
} }
@ -303,14 +307,29 @@ int MemoryManager::PoolCommit(VAddr virtual_addr, size_t size, MemoryProt prot)
VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment); VAddr mapped_addr = Common::AlignUp(virtual_addr, alignment);
auto& vma = FindVMA(mapped_addr)->second; auto& vma = FindVMA(mapped_addr)->second;
if (vma.type != VMAType::PoolReserved || !vma.Contains(mapped_addr, size)) { if (vma.type != VMAType::PoolReserved) {
// If the VMA isn't PoolReserved or if there's not enough space to commit, return EINVAL // If we're attempting to commit non-pooled memory, return EINVAL
LOG_ERROR(Kernel_Vmm, "Attempting to commit non-pooled memory at {:#x}", mapped_addr);
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (!vma.Contains(mapped_addr, size)) {
// If there's not enough space to commit, return EINVAL
LOG_ERROR(Kernel_Vmm, LOG_ERROR(Kernel_Vmm,
"Pooled region {:#x} to {:#x} is not large enough to commit from {:#x} to {:#x}", "Pooled region {:#x} to {:#x} is not large enough to commit from {:#x} to {:#x}",
vma.base, vma.base + vma.size, mapped_addr, mapped_addr + size); vma.base, vma.base + vma.size, mapped_addr, mapped_addr + size);
return ORBIS_KERNEL_ERROR_EINVAL; return ORBIS_KERNEL_ERROR_EINVAL;
} }
if (pool_budget <= size) {
// If there isn't enough pooled memory to perform the mapping, return ENOMEM
LOG_ERROR(Kernel_Vmm, "Not enough pooled memory to perform mapping");
return ORBIS_KERNEL_ERROR_ENOMEM;
} else {
// Track how much pooled memory this commit will take
pool_budget -= size;
}
// Carve out the new VMA representing this mapping // Carve out the new VMA representing this mapping
const auto new_vma_handle = CarveVMA(mapped_addr, size); const auto new_vma_handle = CarveVMA(mapped_addr, size);
auto& new_vma = new_vma_handle->second; auto& new_vma = new_vma_handle->second;
@ -443,7 +462,7 @@ int MemoryManager::MapFile(void** out_addr, VAddr virtual_addr, size_t size, Mem
return ORBIS_OK; return ORBIS_OK;
} }
void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) { s32 MemoryManager::PoolDecommit(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);
@ -458,6 +477,16 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
const auto start_in_vma = virtual_addr - vma_base_addr; const auto start_in_vma = virtual_addr - vma_base_addr;
const auto type = vma_base.type; const auto type = vma_base.type;
if (type != VMAType::PoolReserved && type != VMAType::Pooled) {
LOG_ERROR(Kernel_Vmm, "Attempting to decommit non-pooled memory!");
return ORBIS_KERNEL_ERROR_EINVAL;
}
if (type == VMAType::Pooled) {
// Track how much pooled memory is decommitted
pool_budget += size;
}
if (IsValidGpuMapping(virtual_addr, size)) { if (IsValidGpuMapping(virtual_addr, size)) {
rasterizer->UnmapMemory(virtual_addr, size); rasterizer->UnmapMemory(virtual_addr, size);
} }
@ -472,12 +501,14 @@ void MemoryManager::PoolDecommit(VAddr virtual_addr, size_t size) {
vma.name = "anon"; vma.name = "anon";
MergeAdjacent(vma_map, new_it); MergeAdjacent(vma_map, new_it);
if (type != VMAType::Reserved && type != VMAType::PoolReserved) { if (type != VMAType::PoolReserved) {
// Unmap the memory region. // Unmap the memory region.
impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base, impl.Unmap(vma_base_addr, vma_base_size, start_in_vma, start_in_vma + size, phys_base,
is_exec, false, false); is_exec, false, false);
TRACK_FREE(virtual_addr, "VMEM"); TRACK_FREE(virtual_addr, "VMEM");
} }
return ORBIS_OK;
} }
s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) { s32 MemoryManager::UnmapMemory(VAddr virtual_addr, size_t size) {

View File

@ -198,7 +198,7 @@ public:
int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot, int MapFile(void** out_addr, VAddr virtual_addr, size_t size, MemoryProt prot,
MemoryMapFlags flags, uintptr_t fd, size_t offset); MemoryMapFlags flags, uintptr_t fd, size_t offset);
void PoolDecommit(VAddr virtual_addr, size_t size); s32 PoolDecommit(VAddr virtual_addr, size_t size);
s32 UnmapMemory(VAddr virtual_addr, size_t size); s32 UnmapMemory(VAddr virtual_addr, size_t size);
@ -274,6 +274,7 @@ private:
size_t total_direct_size{}; size_t total_direct_size{};
size_t total_flexible_size{}; size_t total_flexible_size{};
size_t flexible_usage{}; size_t flexible_usage{};
size_t pool_budget{};
Vulkan::Rasterizer* rasterizer{}; Vulkan::Rasterizer* rasterizer{};
friend class ::Core::Devtools::Widget::MemoryMapViewer; friend class ::Core::Devtools::Widget::MemoryMapViewer;