MemArena: Prepare interface for proper memory reservation.

This commit is contained in:
Admiral H. Curtiss 2021-06-21 02:13:00 +02:00
parent 812cc8b632
commit 1554128560
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
5 changed files with 176 additions and 67 deletions

View File

@ -15,18 +15,87 @@ namespace Common
{ {
// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it. // This class lets you create a block of anonymous RAM, and then arbitrarily map views into it.
// Multiple views can mirror the same section of the block, which makes it very convenient for // Multiple views can mirror the same section of the block, which makes it very convenient for
// emulating // emulating memory mirrors.
// memory mirrors. class MemArena final
class MemArena
{ {
public: public:
MemArena();
~MemArena();
MemArena(const MemArena&) = delete;
MemArena(MemArena&&) = delete;
MemArena& operator=(const MemArena&) = delete;
MemArena& operator=(MemArena&&) = delete;
///
/// Allocate the singular memory segment handled by this MemArena. This will be the actual
/// 'physical' available memory for this arena. After allocation, it can be interacted with using
/// CreateView() and ReleaseView(). Used to make a mappable region for emulated memory.
///
/// @param size The amount of bytes that should be allocated in this region.
///
void GrabSHMSegment(size_t size); void GrabSHMSegment(size_t size);
///
/// Release the memory segment previously allocated with GrabSHMSegment().
/// Should not be called before all views have been released.
///
void ReleaseSHMSegment(); void ReleaseSHMSegment();
void* CreateView(s64 offset, size_t size, void* base = nullptr);
///
/// Map a memory region in the memory segment previously allocated with GrabSHMSegment().
///
/// @param offset Offset within the memory segment to map at.
/// @param size Size of the region to map.
///
/// @return Pointer to the memory region, or nullptr on failure.
///
void* CreateView(s64 offset, size_t size);
///
/// Unmap a memory region previously mapped with CreateView().
/// Should not be called on a view that is still mapped into the virtual memory region.
///
/// @param view Pointer returned by CreateView().
/// @param size Size passed to the corresponding CreateView() call.
///
void ReleaseView(void* view, size_t size); void ReleaseView(void* view, size_t size);
// This finds 1 GB in 32-bit, 16 GB in 64-bit. ///
static u8* FindMemoryBase(); /// Reserve the singular 'virtual' memory region handled by this MemArena. This is used to create
/// our 'fastmem' memory area for the emulated game code to access directly.
///
/// @param memory_size Size in bytes of the memory region to reserve.
///
/// @return Pointer to the memory region, or nullptr on failure.
///
u8* ReserveMemoryRegion(size_t memory_size);
///
/// Release the memory region previously reserved with ReserveMemoryRegion().
/// Should not be called while any memory region is still mapped.
///
void ReleaseMemoryRegion();
///
/// Map a section from the memory segment previously allocated with GrabSHMSegment()
/// into the region previously reserved with ReserveMemoryRegion().
///
/// @param offset Offset within the memory segment previous allocated by GrabSHMSegment() to map
/// from.
/// @param size Size of the region to map.
/// @param base Address within the memory region from ReserveMemoryRegion() where to map it.
///
/// @return The address we actually ended up mapping, which should be the given 'base'.
///
void* MapInMemoryRegion(s64 offset, size_t size, void* base);
///
/// Unmap a memory region previously mapped with MapInMemoryRegion().
///
/// @param view Pointer returned by MapInMemoryRegion().
/// @param size Size passed to the corresponding MapInMemoryRegion() call.
///
void UnmapFromMemoryRegion(void* view, size_t size);
private: private:
#ifdef _WIN32 #ifdef _WIN32

View File

@ -59,6 +59,9 @@ static int AshmemCreateFileMapping(const char* name, size_t size)
return fd; return fd;
} }
MemArena::MemArena() = default;
MemArena::~MemArena() = default;
void MemArena::GrabSHMSegment(size_t size) void MemArena::GrabSHMSegment(size_t size)
{ {
fd = AshmemCreateFileMapping(("dolphin-emu." + std::to_string(getpid())).c_str(), size); fd = AshmemCreateFileMapping(("dolphin-emu." + std::to_string(getpid())).c_str(), size);
@ -71,7 +74,37 @@ void MemArena::ReleaseSHMSegment()
close(fd); close(fd);
} }
void* MemArena::CreateView(s64 offset, size_t size, void* base) void* MemArena::CreateView(s64 offset, size_t size)
{
return MapInMemoryRegion(offset, size, nullptr);
}
void MemArena::ReleaseView(void* view, size_t size)
{
UnmapFromMemoryRegion(view, size);
}
u8* MemArena::ReserveMemoryRegion(size_t memory_size)
{
// Android 4.3 changed how mmap works.
// if we map it private and then munmap it, we can't use the base returned.
// This may be due to changes in them to support a full SELinux implementation.
const int flags = MAP_ANON | MAP_SHARED;
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
if (base == MAP_FAILED)
{
PanicAlertFmt("Failed to map enough memory space: {}", LastStrerrorString());
return nullptr;
}
munmap(base, memory_size);
return static_cast<u8*>(base);
}
void MemArena::ReleaseMemoryRegion()
{
}
void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base)
{ {
void* retval = mmap(base, size, PROT_READ | PROT_WRITE, void* retval = mmap(base, size, PROT_READ | PROT_WRITE,
MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset); MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
@ -87,30 +120,8 @@ void* MemArena::CreateView(s64 offset, size_t size, void* base)
} }
} }
void MemArena::ReleaseView(void* view, size_t size) void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
{ {
munmap(view, size); munmap(view, size);
} }
u8* MemArena::FindMemoryBase()
{
#if _ARCH_32
const size_t memory_size = 0x31000000;
#else
const size_t memory_size = 0x400000000;
#endif
// Android 4.3 changed how mmap works.
// if we map it private and then munmap it, we can't use the base returned.
// This may be due to changes in them to support a full SELinux implementation.
const int flags = MAP_ANON | MAP_SHARED;
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
if (base == MAP_FAILED)
{
PanicAlertFmt("Failed to map enough memory space: {}", LastStrerrorString());
return nullptr;
}
munmap(base, memory_size);
return static_cast<u8*>(base);
}
} // namespace Common } // namespace Common

View File

@ -22,6 +22,9 @@
namespace Common namespace Common
{ {
MemArena::MemArena() = default;
MemArena::~MemArena() = default;
void MemArena::GrabSHMSegment(size_t size) void MemArena::GrabSHMSegment(size_t size)
{ {
const std::string file_name = "/dolphin-emu." + std::to_string(getpid()); const std::string file_name = "/dolphin-emu." + std::to_string(getpid());
@ -41,7 +44,34 @@ void MemArena::ReleaseSHMSegment()
close(fd); close(fd);
} }
void* MemArena::CreateView(s64 offset, size_t size, void* base) void* MemArena::CreateView(s64 offset, size_t size)
{
return MapInMemoryRegion(offset, size, nullptr);
}
void MemArena::ReleaseView(void* view, size_t size)
{
UnmapFromMemoryRegion(view, size);
}
u8* MemArena::ReserveMemoryRegion(size_t memory_size)
{
const int flags = MAP_ANON | MAP_PRIVATE;
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
if (base == MAP_FAILED)
{
PanicAlertFmt("Failed to map enough memory space: {}", LastStrerrorString());
return nullptr;
}
munmap(base, memory_size);
return static_cast<u8*>(base);
}
void MemArena::ReleaseMemoryRegion()
{
}
void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base)
{ {
void* retval = mmap(base, size, PROT_READ | PROT_WRITE, void* retval = mmap(base, size, PROT_READ | PROT_WRITE,
MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset); MAP_SHARED | ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
@ -57,27 +87,8 @@ void* MemArena::CreateView(s64 offset, size_t size, void* base)
} }
} }
void MemArena::ReleaseView(void* view, size_t size) void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
{ {
munmap(view, size); munmap(view, size);
} }
u8* MemArena::FindMemoryBase()
{
#if _ARCH_32
const size_t memory_size = 0x31000000;
#else
const size_t memory_size = 0x400000000;
#endif
const int flags = MAP_ANON | MAP_PRIVATE;
void* base = mmap(nullptr, memory_size, PROT_NONE, flags, -1, 0);
if (base == MAP_FAILED)
{
PanicAlertFmt("Failed to map enough memory space: {}", LastStrerrorString());
return nullptr;
}
munmap(base, memory_size);
return static_cast<u8*>(base);
}
} // namespace Common } // namespace Common

View File

@ -18,6 +18,9 @@
namespace Common namespace Common
{ {
MemArena::MemArena() = default;
MemArena::~MemArena() = default;
void MemArena::GrabSHMSegment(size_t size) void MemArena::GrabSHMSegment(size_t size)
{ {
const std::string name = "dolphin-emu." + std::to_string(GetCurrentProcessId()); const std::string name = "dolphin-emu." + std::to_string(GetCurrentProcessId());
@ -31,24 +34,18 @@ void MemArena::ReleaseSHMSegment()
hMemoryMapping = 0; hMemoryMapping = 0;
} }
void* MemArena::CreateView(s64 offset, size_t size, void* base) void* MemArena::CreateView(s64 offset, size_t size)
{ {
return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base); return MapInMemoryRegion(offset, size, nullptr);
} }
void MemArena::ReleaseView(void* view, size_t size) void MemArena::ReleaseView(void* view, size_t size)
{ {
UnmapViewOfFile(view); UnmapFromMemoryRegion(view, size);
} }
u8* MemArena::FindMemoryBase() u8* MemArena::ReserveMemoryRegion(size_t memory_size)
{ {
#if _ARCH_32
const size_t memory_size = 0x31000000;
#else
const size_t memory_size = 0x400000000;
#endif
u8* base = static_cast<u8*>(VirtualAlloc(nullptr, memory_size, MEM_RESERVE, PAGE_READWRITE)); u8* base = static_cast<u8*>(VirtualAlloc(nullptr, memory_size, MEM_RESERVE, PAGE_READWRITE));
if (!base) if (!base)
{ {
@ -58,4 +55,18 @@ u8* MemArena::FindMemoryBase()
VirtualFree(base, 0, MEM_RELEASE); VirtualFree(base, 0, MEM_RELEASE);
return base; return base;
} }
void MemArena::ReleaseMemoryRegion()
{
}
void* MemArena::MapInMemoryRegion(s64 offset, size_t size, void* base)
{
return MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
}
void MemArena::UnmapFromMemoryRegion(void* view, size_t size)
{
UnmapViewOfFile(view);
}
} // namespace Common } // namespace Common

View File

@ -314,7 +314,12 @@ void Init()
bool InitFastmemArena() bool InitFastmemArena()
{ {
physical_base = Common::MemArena::FindMemoryBase(); #if _ARCH_32
const size_t memory_size = 0x31000000;
#else
const size_t memory_size = 0x400000000;
#endif
physical_base = g_arena.ReserveMemoryRegion(memory_size);
if (!physical_base) if (!physical_base)
{ {
@ -328,7 +333,7 @@ bool InitFastmemArena()
continue; continue;
u8* base = physical_base + region.physical_address; u8* base = physical_base + region.physical_address;
u8* view = (u8*)g_arena.CreateView(region.shm_position, region.size, base); u8* view = (u8*)g_arena.MapInMemoryRegion(region.shm_position, region.size, base);
if (base != view) if (base != view)
{ {
@ -354,7 +359,7 @@ void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
for (auto& entry : logical_mapped_entries) for (auto& entry : logical_mapped_entries)
{ {
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size); g_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
} }
logical_mapped_entries.clear(); logical_mapped_entries.clear();
for (u32 i = 0; i < dbat_table.size(); ++i) for (u32 i = 0; i < dbat_table.size(); ++i)
@ -381,7 +386,7 @@ void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
u8* base = logical_base + logical_address + intersection_start - translated_address; u8* base = logical_base + logical_address + intersection_start - translated_address;
u32 mapped_size = intersection_end - intersection_start; u32 mapped_size = intersection_end - intersection_start;
void* mapped_pointer = g_arena.CreateView(position, mapped_size, base); void* mapped_pointer = g_arena.MapInMemoryRegion(position, mapped_size, base);
if (!mapped_pointer) if (!mapped_pointer)
{ {
PanicAlertFmt("Memory::UpdateLogicalMemory(): Failed to map memory region at 0x{:08X} " PanicAlertFmt("Memory::UpdateLogicalMemory(): Failed to map memory region at 0x{:08X} "
@ -439,15 +444,17 @@ void ShutdownFastmemArena()
continue; continue;
u8* base = physical_base + region.physical_address; u8* base = physical_base + region.physical_address;
g_arena.ReleaseView(base, region.size); g_arena.UnmapFromMemoryRegion(base, region.size);
} }
for (auto& entry : logical_mapped_entries) for (auto& entry : logical_mapped_entries)
{ {
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size); g_arena.UnmapFromMemoryRegion(entry.mapped_pointer, entry.mapped_size);
} }
logical_mapped_entries.clear(); logical_mapped_entries.clear();
g_arena.ReleaseMemoryRegion();
physical_base = nullptr; physical_base = nullptr;
logical_base = nullptr; logical_base = nullptr;