mirror of
https://git.eden-emu.dev/eden-emu/eden.git
synced 2026-08-16 21:33:01 +00:00
[nvdrv] Reset process resources for homebrew handoff
Track NVDRV sessions by process and aruid so in-place homebrew handoffs can close process-owned device files and sessions before loading the next NRO. Also unlock nvmap device-shared pages during session cleanup to avoid stale GPU mappings leaking across repeated handoffs.
This commit is contained in:
@@ -1,10 +1,15 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2022 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 2022 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
@@ -40,6 +45,16 @@ Container::Container(Tegra::Host1x::Host1x& host1x_) {
|
||||
|
||||
Container::~Container() = default;
|
||||
|
||||
static bool IsSameProcess(Kernel::KProcess* lhs, Kernel::KProcess* rhs) {
|
||||
if (lhs == rhs) {
|
||||
return true;
|
||||
}
|
||||
if (lhs == nullptr || rhs == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return lhs->GetProcessId() == rhs->GetProcessId();
|
||||
}
|
||||
|
||||
SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
using namespace Common::Literals;
|
||||
|
||||
@@ -48,7 +63,7 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
if (!session.is_active) {
|
||||
continue;
|
||||
}
|
||||
if (session.process == process) {
|
||||
if (IsSameProcess(session.process, process)) {
|
||||
session.ref_count++;
|
||||
return session.id;
|
||||
}
|
||||
@@ -116,7 +131,15 @@ SessionId Container::OpenSession(Kernel::KProcess* process) {
|
||||
|
||||
void Container::CloseSession(SessionId session_id) {
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
if (session_id.id >= impl->sessions.size()) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto& session = impl->sessions[session_id.id];
|
||||
if (!session.is_active || session.ref_count <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (--session.ref_count > 0) {
|
||||
return;
|
||||
}
|
||||
@@ -134,6 +157,73 @@ void Container::CloseSession(SessionId session_id) {
|
||||
impl->id_pool.emplace_front(session_id.id);
|
||||
}
|
||||
|
||||
size_t Container::CloseSessions(std::span<const SessionId> session_ids) {
|
||||
std::vector<SessionId> valid_session_ids;
|
||||
valid_session_ids.reserve(session_ids.size());
|
||||
|
||||
{
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
for (const auto session_id : session_ids) {
|
||||
if (session_id.id >= impl->sessions.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto& session = impl->sessions[session_id.id];
|
||||
if (!session.is_active) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto duplicate = std::ranges::any_of(
|
||||
valid_session_ids, [session_id](const auto candidate) {
|
||||
return candidate.id == session_id.id;
|
||||
});
|
||||
if (duplicate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
session.ref_count = 1;
|
||||
valid_session_ids.push_back(session_id);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto session_id : valid_session_ids) {
|
||||
CloseSession(session_id);
|
||||
}
|
||||
|
||||
return valid_session_ids.size();
|
||||
}
|
||||
|
||||
std::vector<SessionId> Container::GetSessionIdsForProcess(Kernel::KProcess* process) {
|
||||
std::vector<SessionId> session_ids;
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
for (const auto& session : impl->sessions) {
|
||||
if (!session.is_active || !IsSameProcess(session.process, process)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
session_ids.push_back(session.id);
|
||||
}
|
||||
|
||||
return session_ids;
|
||||
}
|
||||
|
||||
std::vector<SessionId> Container::GetActiveSessionIds() const {
|
||||
std::vector<SessionId> session_ids;
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
for (const auto& session : impl->sessions) {
|
||||
if (session.is_active) {
|
||||
session_ids.push_back(session.id);
|
||||
}
|
||||
}
|
||||
|
||||
return session_ids;
|
||||
}
|
||||
|
||||
bool Container::IsSessionActive(SessionId session_id) const {
|
||||
std::scoped_lock lk(impl->session_guard);
|
||||
return session_id.id < impl->sessions.size() && impl->sessions[session_id.id].is_active;
|
||||
}
|
||||
|
||||
Session* Container::GetSession(SessionId session_id) {
|
||||
std::atomic_thread_fence(std::memory_order_acquire);
|
||||
return &impl->sessions[session_id.id];
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <cstddef>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
#include <vector>
|
||||
|
||||
#include "core/device_memory_manager.h"
|
||||
#include "core/hle/service/nvdrv/nvdata.h"
|
||||
@@ -59,6 +62,10 @@ public:
|
||||
|
||||
SessionId OpenSession(Kernel::KProcess* process);
|
||||
void CloseSession(SessionId id);
|
||||
size_t CloseSessions(std::span<const SessionId> session_ids);
|
||||
std::vector<SessionId> GetSessionIdsForProcess(Kernel::KProcess* process);
|
||||
std::vector<SessionId> GetActiveSessionIds() const;
|
||||
bool IsSessionActive(SessionId id) const;
|
||||
|
||||
Session* GetSession(SessionId id);
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/logging.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/core/heap_mapper.h"
|
||||
#include "core/hle/service/nvdrv/core/nvmap.h"
|
||||
@@ -326,19 +328,65 @@ std::optional<NvMap::FreeInfo> NvMap::FreeHandle(Handle::Id handle, bool interna
|
||||
}
|
||||
|
||||
void NvMap::UnmapAllHandles(NvCore::SessionId session_id) {
|
||||
auto handles_copy = [&] {
|
||||
auto* session = core.GetSession(session_id);
|
||||
auto* process = session != nullptr ? session->process : nullptr;
|
||||
auto handle_ids = [&] {
|
||||
std::scoped_lock lk{handles_lock};
|
||||
return handles;
|
||||
std::vector<Handle::Id> ids;
|
||||
ids.reserve(handles.size());
|
||||
|
||||
for (const auto& entry : handles) {
|
||||
ids.push_back(entry.first);
|
||||
}
|
||||
|
||||
return ids;
|
||||
}();
|
||||
|
||||
for (auto& [id, handle] : handles_copy) {
|
||||
{
|
||||
std::scoped_lock lk{handle->mutex};
|
||||
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
|
||||
continue;
|
||||
for (const auto id : handle_ids) {
|
||||
bool unlocked_pages = false;
|
||||
while (true) {
|
||||
bool last_user_reference = false;
|
||||
VAddr address = 0;
|
||||
size_t size = 0;
|
||||
{
|
||||
const auto handle = GetHandle(id);
|
||||
if (!handle) {
|
||||
break;
|
||||
}
|
||||
|
||||
std::scoped_lock lk{handle->mutex};
|
||||
if (handle->session_id.id != session_id.id || handle->dupes <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
last_user_reference = handle->dupes == 1;
|
||||
address = handle->address;
|
||||
size = handle->size;
|
||||
}
|
||||
|
||||
const auto free_info = FreeHandle(id, false);
|
||||
if (!free_info) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!unlocked_pages && process != nullptr && address != 0 && size != 0 &&
|
||||
(free_info->can_unlock || last_user_reference)) {
|
||||
const auto unlock_result =
|
||||
process->GetPageTable().UnlockForDeviceAddressSpace(address, size);
|
||||
if (unlock_result.IsError()) {
|
||||
LOG_WARNING(Service_NVDRV,
|
||||
"NextLoad: nvmap session cleanup unlock failed, "
|
||||
"handle={}, session={}, address=0x{:016X}, size=0x{:X}, "
|
||||
"result={:#X}",
|
||||
id, session_id.id, address, size, unlock_result.raw);
|
||||
}
|
||||
unlocked_pages = true;
|
||||
}
|
||||
|
||||
if (last_user_reference) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FreeHandle(id, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,17 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
|
||||
// SPDX-FileCopyrightText: 2021 Skyline Team and Contributors
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <fmt/ranges.h>
|
||||
#include "core/core.h"
|
||||
#include "core/hle/kernel/k_event.h"
|
||||
#include "core/hle/kernel/k_process.h"
|
||||
#include "core/hle/service/ipc_helpers.h"
|
||||
#include "core/hle/service/nvdrv/core/container.h"
|
||||
#include "core/hle/service/nvdrv/devices/nvdevice.h"
|
||||
@@ -133,6 +138,9 @@ DeviceFD Module::Open(const std::string& device_name, NvCore::SessionId session_
|
||||
auto device = builder(fd)->second;
|
||||
|
||||
device->OnOpen(session_id, fd);
|
||||
if (container.IsSessionActive(session_id)) {
|
||||
open_file_sessions.emplace(fd, session_id);
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
@@ -204,6 +212,7 @@ NvResult Module::Close(DeviceFD fd) {
|
||||
itr->second->OnClose(fd);
|
||||
|
||||
open_files.erase(itr);
|
||||
open_file_sessions.erase(fd);
|
||||
|
||||
return NvResult::Success;
|
||||
}
|
||||
@@ -228,4 +237,98 @@ NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) {
|
||||
return NvResult::Success;
|
||||
}
|
||||
|
||||
static bool ContainsSession(std::span<const NvCore::SessionId> session_ids,
|
||||
NvCore::SessionId session_id) {
|
||||
return std::ranges::any_of(session_ids, [session_id](const auto candidate) {
|
||||
return candidate.id == session_id.id;
|
||||
});
|
||||
}
|
||||
|
||||
static void AppendUniqueSession(std::vector<NvCore::SessionId>& session_ids,
|
||||
NvCore::SessionId session_id) {
|
||||
if (!ContainsSession(session_ids, session_id)) {
|
||||
session_ids.push_back(session_id);
|
||||
}
|
||||
}
|
||||
|
||||
size_t Module::CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids) {
|
||||
std::vector<DeviceFD> fds;
|
||||
fds.reserve(open_file_sessions.size());
|
||||
|
||||
for (const auto& [fd, session_id] : open_file_sessions) {
|
||||
if (ContainsSession(session_ids, session_id)) {
|
||||
fds.push_back(fd);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto fd : fds) {
|
||||
Close(fd);
|
||||
}
|
||||
|
||||
return fds.size();
|
||||
}
|
||||
|
||||
void Module::CloseSession(NvCore::SessionId session_id) {
|
||||
container.CloseSession(session_id);
|
||||
}
|
||||
|
||||
void Module::TrackSessionAruid(NvCore::SessionId session_id, u64 aruid) {
|
||||
const bool active = container.IsSessionActive(session_id);
|
||||
if (active) {
|
||||
session_aruids[session_id.id] = aruid;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<NvCore::SessionId> Module::GetSessionIdsForAruid(u64 aruid) const {
|
||||
std::vector<NvCore::SessionId> session_ids;
|
||||
for (const auto& [session_id, session_aruid] : session_aruids) {
|
||||
if (session_aruid == aruid) {
|
||||
session_ids.push_back(NvCore::SessionId{session_id});
|
||||
}
|
||||
}
|
||||
|
||||
return session_ids;
|
||||
}
|
||||
|
||||
size_t Module::ResetForProcess(Kernel::KProcess* process) {
|
||||
const auto process_id = process != nullptr ? process->GetProcessId() : 0;
|
||||
auto session_ids = container.GetSessionIdsForProcess(process);
|
||||
|
||||
if (process_id != 0) {
|
||||
for (const auto session_id : GetSessionIdsForAruid(process_id)) {
|
||||
AppendUniqueSession(session_ids, session_id);
|
||||
}
|
||||
}
|
||||
|
||||
const auto active_session_ids = container.GetActiveSessionIds();
|
||||
const auto active_before = active_session_ids.size();
|
||||
const bool has_active_candidate =
|
||||
std::ranges::any_of(session_ids, [this](const auto session_id) {
|
||||
return container.IsSessionActive(session_id);
|
||||
});
|
||||
bool used_active_sessions = false;
|
||||
if (!has_active_candidate && !active_session_ids.empty()) {
|
||||
for (const auto session_id : active_session_ids) {
|
||||
AppendUniqueSession(session_ids, session_id);
|
||||
}
|
||||
used_active_sessions = true;
|
||||
}
|
||||
|
||||
const auto closed_files = CloseFilesForSessions(session_ids);
|
||||
const auto closed_sessions = container.CloseSessions(session_ids);
|
||||
for (const auto session_id : session_ids) {
|
||||
if (!container.IsSessionActive(session_id)) {
|
||||
session_aruids.erase(session_id.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (used_active_sessions) {
|
||||
LOG_WARNING(Service_NVDRV,
|
||||
"NextLoad: NVDRV reset used active sessions because process-owned "
|
||||
"sessions were not found, process_id={}, sessions={}, files={}, active_before={}",
|
||||
process_id, closed_sessions, closed_files, active_before);
|
||||
}
|
||||
return closed_sessions;
|
||||
}
|
||||
|
||||
} // namespace Service::Nvidia
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <memory>
|
||||
#include <span>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <ankerl/unordered_dense.h>
|
||||
|
||||
#include "common/common_types.h"
|
||||
@@ -26,6 +27,7 @@ class System;
|
||||
|
||||
namespace Kernel {
|
||||
class KEvent;
|
||||
class KProcess;
|
||||
}
|
||||
|
||||
namespace Service::Nvidia {
|
||||
@@ -89,6 +91,9 @@ public:
|
||||
NvResult Close(DeviceFD fd);
|
||||
|
||||
NvResult QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event);
|
||||
void CloseSession(NvCore::SessionId session_id);
|
||||
void TrackSessionAruid(NvCore::SessionId session_id, u64 aruid);
|
||||
size_t ResetForProcess(Kernel::KProcess* process);
|
||||
|
||||
NvCore::Container& GetContainer() {
|
||||
return container;
|
||||
@@ -106,12 +111,17 @@ private:
|
||||
using FilesContainerType = ankerl::unordered_dense::map<DeviceFD, std::shared_ptr<Devices::nvdevice>>;
|
||||
/// Mapping of file descriptors to the devices they reference.
|
||||
FilesContainerType open_files;
|
||||
ankerl::unordered_dense::map<DeviceFD, NvCore::SessionId> open_file_sessions;
|
||||
ankerl::unordered_dense::map<size_t, u64> session_aruids;
|
||||
|
||||
KernelHelpers::ServiceContext service_context;
|
||||
|
||||
EventInterface events_interface;
|
||||
|
||||
ankerl::unordered_dense::map<std::string, std::function<FilesContainerType::iterator(DeviceFD)>> builders;
|
||||
|
||||
size_t CloseFilesForSessions(std::span<const NvCore::SessionId> session_ids);
|
||||
std::vector<NvCore::SessionId> GetSessionIdsForAruid(u64 aruid) const;
|
||||
};
|
||||
|
||||
void LoopProcess(Core::System& system);
|
||||
|
||||
@@ -212,7 +212,10 @@ void NVDRV::QueryEvent(HLERequestContext& ctx) {
|
||||
void NVDRV::SetAruid(HLERequestContext& ctx) {
|
||||
IPC::RequestParser rp{ctx};
|
||||
pid = rp.Pop<u64>();
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#x}", pid);
|
||||
LOG_WARNING(Service_NVDRV, "(STUBBED) called, pid={:#X}", pid);
|
||||
if (is_initialized) {
|
||||
nvdrv->TrackSessionAruid(session_id, pid);
|
||||
}
|
||||
|
||||
IPC::ResponseBuilder rb{ctx, 3};
|
||||
rb.Push(ResultSuccess);
|
||||
|
||||
Reference in New Issue
Block a user