mirror of
https://git.eden-emu.dev/eden-emu/eden
synced 2026-06-02 22:47:10 +02:00
[hle, service] fix errors related to race conditions triggering under SMG1 and SMG2 (#3927)
- service manager may add a service while someone else is finding it, so properly lock - nvhost doesn't properly account for the fact that iterators GET FUCKING INVALIDATED - use proper exit routine for mapping locked that failed (try/catch hell) the last two were introduced by #3858, but the first one has been present since ??? either way, remember that ankerl map has invalidated iterators upon erase/insert, so i accounted for that and SMG1 (and probably smg2) boot fine now Signed-off-by: lizzie <lizzie@eden-emu.dev> Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/3927 Reviewed-by: crueter <crueter@eden-emu.dev> Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
This commit is contained in:
parent
fee603f0b9
commit
4f4c298a39
3 changed files with 37 additions and 31 deletions
|
|
@ -199,25 +199,28 @@ NvResult nvhost_as_gpu::AllocateSpace(IoctlAllocSpace& params) {
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
void nvhost_as_gpu::FreeMappingLocked(u64 offset) {
|
bool nvhost_as_gpu::FreeMappingLocked(u64 offset) noexcept {
|
||||||
auto const it = mapping_map.find(offset);
|
if (auto const it = mapping_map.find(offset); it != mapping_map.end()) {
|
||||||
auto const mapping = it->second;
|
auto const mapping = it->second;
|
||||||
if (!mapping.fixed) {
|
if (!mapping.fixed) {
|
||||||
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
|
auto& allocator{mapping.big_page ? *vm.big_page_allocator : *vm.small_page_allocator};
|
||||||
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
|
u32 page_size_bits{mapping.big_page ? vm.big_page_size_bits : VM::PAGE_SIZE_BITS};
|
||||||
u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
|
u32 page_size{mapping.big_page ? vm.big_page_size : VM::YUZU_PAGESIZE};
|
||||||
u64 aligned_size{Common::AlignUp(mapping.size, page_size)};
|
u64 aligned_size{Common::AlignUp(mapping.size, page_size)};
|
||||||
allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits));
|
allocator.Free(u32(mapping.offset >> page_size_bits), u32(aligned_size >> page_size_bits));
|
||||||
|
}
|
||||||
|
nvmap.UnpinHandle(mapping.handle);
|
||||||
|
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
||||||
|
// Only FreeSpace can unmap them fully
|
||||||
|
if (mapping.sparse_alloc) {
|
||||||
|
gmmu->MapSparse(offset, mapping.size, mapping.big_page);
|
||||||
|
} else {
|
||||||
|
gmmu->Unmap(offset, mapping.size);
|
||||||
|
}
|
||||||
|
mapping_map.erase(offset);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
nvmap.UnpinHandle(mapping.handle);
|
return false;
|
||||||
// Sparse mappings shouldn't be fully unmapped, just returned to their sparse state
|
|
||||||
// Only FreeSpace can unmap them fully
|
|
||||||
if (mapping.sparse_alloc) {
|
|
||||||
gmmu->MapSparse(offset, mapping.size, mapping.big_page);
|
|
||||||
} else {
|
|
||||||
gmmu->Unmap(offset, mapping.size);
|
|
||||||
}
|
|
||||||
mapping_map.erase(it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
|
NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
|
||||||
|
|
@ -232,7 +235,8 @@ NvResult nvhost_as_gpu::FreeSpace(IoctlFreeSpace& params) {
|
||||||
return NvResult::BadValue;
|
return NvResult::BadValue;
|
||||||
|
|
||||||
for (const auto mapping_offset : allocation.mappings)
|
for (const auto mapping_offset : allocation.mappings)
|
||||||
FreeMappingLocked(mapping_offset);
|
if (!FreeMappingLocked(mapping_offset))
|
||||||
|
return NvResult::BadValue;
|
||||||
|
|
||||||
// Unset sparse flag if required
|
// Unset sparse flag if required
|
||||||
if (allocation.sparse)
|
if (allocation.sparse)
|
||||||
|
|
@ -402,8 +406,8 @@ NvResult nvhost_as_gpu::UnmapBuffer(IoctlUnmapBuffer& params) {
|
||||||
}
|
}
|
||||||
|
|
||||||
nvmap.UnpinHandle(mapping.handle);
|
nvmap.UnpinHandle(mapping.handle);
|
||||||
mapping_map.erase(it);
|
mapping_map.erase(params.offset);
|
||||||
map_buffer_offsets.erase(offset_it);
|
map_buffer_offsets.erase(params.offset);
|
||||||
}
|
}
|
||||||
return NvResult::Success;
|
return NvResult::Success;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -157,7 +157,7 @@ private:
|
||||||
NvResult GetVARegions1(IoctlGetVaRegions& params);
|
NvResult GetVARegions1(IoctlGetVaRegions& params);
|
||||||
NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
|
NvResult GetVARegions3(IoctlGetVaRegions& params, std::span<VaRegion> regions);
|
||||||
|
|
||||||
void FreeMappingLocked(u64 offset);
|
[[nodiscard]] bool FreeMappingLocked(u64 offset) noexcept;
|
||||||
|
|
||||||
Module& module;
|
Module& module;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -67,22 +67,24 @@ public:
|
||||||
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
|
Result GetServicePort(Kernel::KClientPort** out_client_port, const std::string& name);
|
||||||
|
|
||||||
template <std::derived_from<SessionRequestHandler> T>
|
template <std::derived_from<SessionRequestHandler> T>
|
||||||
std::shared_ptr<T> GetService(const std::string& service_name, bool block = false) const {
|
std::shared_ptr<T> GetService(const std::string& name, bool block = false) const {
|
||||||
auto service = registered_services.find(service_name);
|
std::unique_lock l{lock};
|
||||||
if (service == registered_services.end() && !block) {
|
auto it = registered_services.find(name);
|
||||||
LOG_DEBUG(Service, "Can't find service: {}", service_name);
|
if (it == registered_services.end() && !block) {
|
||||||
|
LOG_DEBUG(Service, "Can't find service: {}", name);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
} else if (block) {
|
} else if (block) {
|
||||||
using namespace std::literals::chrono_literals;
|
using namespace std::literals::chrono_literals;
|
||||||
while (service == registered_services.end()) {
|
while (it == registered_services.end()) {
|
||||||
|
l.unlock();
|
||||||
Kernel::Svc::SleepThread(
|
Kernel::Svc::SleepThread(
|
||||||
kernel.System(),
|
kernel.System(),
|
||||||
std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
|
std::chrono::duration_cast<std::chrono::nanoseconds>(100ms).count());
|
||||||
service = registered_services.find(service_name);
|
l.lock();
|
||||||
|
it = registered_services.find(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return std::static_pointer_cast<T>(it->second());
|
||||||
return std::static_pointer_cast<T>(service->second());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InvokeControlRequest(HLERequestContext& context);
|
void InvokeControlRequest(HLERequestContext& context);
|
||||||
|
|
@ -96,7 +98,7 @@ private:
|
||||||
std::unique_ptr<Controller> controller_interface;
|
std::unique_ptr<Controller> controller_interface;
|
||||||
|
|
||||||
/// Map of registered services, retrieved using GetServicePort.
|
/// Map of registered services, retrieved using GetServicePort.
|
||||||
std::mutex lock;
|
mutable std::mutex lock;
|
||||||
ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services;
|
ankerl::unordered_dense::map<std::string, SessionRequestHandlerFactory> registered_services;
|
||||||
ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports;
|
ankerl::unordered_dense::map<std::string, Kernel::KClientPort*> service_ports;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue