diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.cpp b/app/src/main/cpp/skyline/gpu/memory_manager.cpp index fd9cf421..b0c8aca2 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.cpp +++ b/app/src/main/cpp/skyline/gpu/memory_manager.cpp @@ -6,17 +6,17 @@ namespace skyline::gpu::vmm { MemoryManager::MemoryManager(const DeviceState &state) : state(state) { - constexpr u64 GpuAddressSpaceSize{1ul << 40}; //!< The size of the GPU address space - constexpr u64 GpuAddressSpaceBase{0x100000}; //!< The base of the GPU address space - must be non-zero + constexpr u64 gpuAddressSpaceSize{1UL << 40}; //!< The size of the GPU address space + constexpr u64 gpuAddressSpaceBase{0x100000}; //!< The base of the GPU address space - must be non-zero // Create the initial chunk that will be split to create new chunks - ChunkDescriptor baseChunk(GpuAddressSpaceBase, GpuAddressSpaceSize, 0, ChunkState::Unmapped); + ChunkDescriptor baseChunk(gpuAddressSpaceBase, gpuAddressSpaceSize, nullptr, ChunkState::Unmapped); chunks.push_back(baseChunk); } - std::optional MemoryManager::FindChunk(ChunkState state, u64 size, u64 alignment) { - auto chunk{std::find_if(chunks.begin(), chunks.end(), [state, size, alignment](const ChunkDescriptor &chunk) -> bool { - return (alignment ? util::IsAligned(chunk.virtAddr, alignment) : true) && chunk.size > size && chunk.state == state; + std::optional MemoryManager::FindChunk(ChunkState desiredState, u64 size, u64 alignment) { + auto chunk{std::find_if(chunks.begin(), chunks.end(), [desiredState, size, alignment](const ChunkDescriptor &chunk) -> bool { + return (alignment ? util::IsAligned(chunk.virtAddr, alignment) : true) && chunk.size > size && chunk.state == desiredState; })}; if (chunk != chunks.end()) @@ -41,7 +41,7 @@ namespace skyline::gpu::vmm { } if (extension) - chunks.insert(std::next(chunk), ChunkDescriptor(newChunk.virtAddr + newChunk.size, extension, (oldChunk.state == ChunkState::Mapped) ? (oldChunk.cpuPtr + newSize + newChunk.size) : 0, oldChunk.state)); + chunks.insert(std::next(chunk), ChunkDescriptor(newChunk.virtAddr + newChunk.size, extension, (oldChunk.state == ChunkState::Mapped) ? (oldChunk.cpuPtr + newSize + newChunk.size) : nullptr, oldChunk.state)); return newChunk.virtAddr; } else if (chunk->virtAddr + chunk->size > newChunk.virtAddr) { @@ -83,6 +83,8 @@ namespace skyline::gpu::vmm { u64 MemoryManager::ReserveSpace(u64 size, u64 alignment) { size = util::AlignUp(size, constant::GpuPageSize); + + std::unique_lock lock(vmmMutex); auto newChunk{FindChunk(ChunkState::Unmapped, size, alignment)}; if (!newChunk) return 0; @@ -100,11 +102,14 @@ namespace skyline::gpu::vmm { size = util::AlignUp(size, constant::GpuPageSize); + std::unique_lock lock(vmmMutex); return InsertChunk(ChunkDescriptor(virtAddr, size, nullptr, ChunkState::Reserved)); } u64 MemoryManager::MapAllocate(u8 *cpuPtr, u64 size) { size = util::AlignUp(size, constant::GpuPageSize); + + std::unique_lock lock(vmmMutex); auto mappedChunk{FindChunk(ChunkState::Unmapped, size)}; if (!mappedChunk) return 0; @@ -123,6 +128,7 @@ namespace skyline::gpu::vmm { size = util::AlignUp(size, constant::GpuPageSize); + std::unique_lock lock(vmmMutex); return InsertChunk(ChunkDescriptor(virtAddr, size, cpuPtr, ChunkState::Mapped)); } @@ -131,7 +137,8 @@ namespace skyline::gpu::vmm { return false; try { - InsertChunk(ChunkDescriptor(virtAddr, size, 0, ChunkState::Unmapped)); + std::unique_lock lock(vmmMutex); + InsertChunk(ChunkDescriptor(virtAddr, size, nullptr, ChunkState::Unmapped)); } catch (const std::exception &e) { return false; } @@ -139,7 +146,9 @@ namespace skyline::gpu::vmm { return true; } - void MemoryManager::Read(u8 *destination, u64 virtAddr, u64 size) const { + void MemoryManager::Read(u8 *destination, u64 virtAddr, u64 size) { + std::shared_lock lock(vmmMutex); + auto chunk{std::upper_bound(chunks.begin(), chunks.end(), virtAddr, [](const u64 address, const ChunkDescriptor &chunk) -> bool { return address < chunk.virtAddr; })}; @@ -169,7 +178,9 @@ namespace skyline::gpu::vmm { } } - void MemoryManager::Write(u8 *source, u64 virtAddr, u64 size) const { + void MemoryManager::Write(u8 *source, u64 virtAddr, u64 size) { + std::shared_lock lock(vmmMutex); + auto chunk{std::upper_bound(chunks.begin(), chunks.end(), virtAddr, [](const u64 address, const ChunkDescriptor &chunk) -> bool { return address < chunk.virtAddr; })}; diff --git a/app/src/main/cpp/skyline/gpu/memory_manager.h b/app/src/main/cpp/skyline/gpu/memory_manager.h index e166aac8..cbe1cff5 100644 --- a/app/src/main/cpp/skyline/gpu/memory_manager.h +++ b/app/src/main/cpp/skyline/gpu/memory_manager.h @@ -40,18 +40,21 @@ namespace skyline { private: const DeviceState &state; std::vector chunks; + std::shared_mutex vmmMutex; /** * @brief Finds a chunk in the virtual address space that is larger than meets the given requirements - * @param state The state of the chunk to find + * @note vmmMutex MUST be locked when calling this + * @param desiredState The state of the chunk to find * @param size The minimum size of the chunk to find * @param alignment The minimum alignment of the chunk to find * @return The first applicable chunk */ - std::optional FindChunk(ChunkState state, u64 size, u64 alignment = 0); + std::optional FindChunk(ChunkState desiredState, u64 size, u64 alignment = 0); /** * @brief Inserts a chunk into the chunk list, resizing and splitting as necessary + * @note vmmMutex MUST be locked when calling this * @param newChunk The chunk to insert * @return The base virtual address of the inserted chunk */ @@ -99,13 +102,13 @@ namespace skyline { */ bool Unmap(u64 virtAddr, u64 size); - void Read(u8 *destination, u64 virtAddr, u64 size) const; + void Read(u8 *destination, u64 virtAddr, u64 size); /** * @brief Reads in a span from a region of the virtual address space */ template - void Read(span destination, u64 virtAddr) const { + void Read(span destination, u64 virtAddr) { Read(reinterpret_cast(destination.data()), virtAddr, destination.size_bytes()); } @@ -114,19 +117,19 @@ namespace skyline { * @tparam T The type of object to return */ template - T Read(u64 virtAddr) const { + T Read(u64 virtAddr) { T obj; Read(reinterpret_cast(&obj), virtAddr, sizeof(T)); return obj; } - void Write(u8 *source, u64 virtAddr, u64 size) const; + void Write(u8 *source, u64 virtAddr, u64 size); /** * @brief Writes out a span to a region of the virtual address space */ template - void Write(span source, u64 virtAddr) const { + void Write(span source, u64 virtAddr) { Write(reinterpret_cast(source.data()), virtAddr, source.size_bytes()); } @@ -134,7 +137,7 @@ namespace skyline { * @brief Reads in an object from a region of the virtual address space */ template - void Write(T source, u64 virtAddr) const { + void Write(T source, u64 virtAddr) { Write(reinterpret_cast(&source), virtAddr, sizeof(T)); } };