From af35dbcf633d35450b333eb33334b3dd1bc050a1 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Fri, 5 Nov 2021 01:44:11 +0100 Subject: [PATCH] NVDRV: Fix Open/Close and make sure each device is correctly created. --- .../hle/service/nvdrv/devices/nvhost_ctrl.cpp | 178 ++++++++++++----- .../hle/service/nvdrv/devices/nvhost_ctrl.h | 42 ++++ .../service/nvdrv/devices/nvhost_ctrl_gpu.cpp | 9 +- .../hle/service/nvdrv/devices/nvhost_gpu.cpp | 12 +- .../service/nvdrv/devices/nvhost_nvdec.cpp | 2 + .../hle/service/nvdrv/devices/nvhost_nvdec.h | 2 +- .../nvdrv/devices/nvhost_nvdec_common.cpp | 2 + .../nvdrv/devices/nvhost_nvdec_common.h | 2 +- .../hle/service/nvdrv/devices/nvhost_vic.cpp | 3 + .../hle/service/nvdrv/devices/nvhost_vic.h | 2 +- src/core/hle/service/nvdrv/nvdrv.cpp | 182 +++++++----------- src/core/hle/service/nvdrv/nvdrv.h | 56 ++---- src/core/hle/service/nvflinger/nvflinger.cpp | 7 +- src/core/hle/service/nvflinger/nvflinger.h | 1 + 14 files changed, 296 insertions(+), 204 deletions(-) diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 51c40f620..122c1d5e1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp @@ -3,9 +3,11 @@ // SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 // or any later version Refer to the license.txt file included. +#include #include #include +#include #include "common/assert.h" #include "common/logging/log.h" #include "common/scope_exit.h" @@ -22,8 +24,19 @@ namespace Service::Nvidia::Devices { nvhost_ctrl::nvhost_ctrl(Core::System& system_, EventInterface& events_interface_, NvCore::Container& core_) : nvdevice{system_}, events_interface{events_interface_}, core{core_}, - syncpoint_manager{core_.GetSyncpointManager()} {} -nvhost_ctrl::~nvhost_ctrl() = default; + syncpoint_manager{core_.GetSyncpointManager()} { + events_interface.RegisterForSignal(this); +} + +nvhost_ctrl::~nvhost_ctrl() { + events_interface.UnregisterForSignal(this); + for (auto& event : events) { + if (!event.registered) { + continue; + } + events_interface.FreeEvent(event.kevent); + } +} NvResult nvhost_ctrl::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { @@ -87,7 +100,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector SCOPE_EXIT({ std::memcpy(output.data(), ¶ms, sizeof(params)); if (must_unmark_fail) { - events_interface.fails[event_id] = 0; + events[event_id].fails = 0; } }); @@ -116,12 +129,12 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector auto& gpu = system.GPU(); const u32 target_value = params.fence.value; - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); u32 slot = [&]() { if (is_allocation) { params.value.raw = 0; - return events_interface.FindFreeEvent(fence_id); + return FindFreeNvEvent(fence_id); } else { return params.value.raw; } @@ -130,7 +143,7 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector must_unmark_fail = true; const auto check_failing = [&]() { - if (events_interface.fails[slot] > 2) { + if (events[slot].fails > 2) { { auto lk = system.StallProcesses(); gpu.WaitFence(fence_id, target_value); @@ -142,6 +155,10 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return false; }; + if (slot >= MaxNvEvents) { + return NvResult::BadParameter; + } + if (params.timeout == 0) { if (check_failing()) { return NvResult::Success; @@ -149,17 +166,13 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector return NvResult::Timeout; } - if (slot >= MaxNvEvents) { + auto& event = events[slot]; + + if (!event.registered) { return NvResult::BadParameter; } - auto* event = events_interface.events[slot]; - - if (!event) { - return NvResult::BadParameter; - } - - if (events_interface.IsBeingUsed(slot)) { + if (event.IsBeingUsed()) { return NvResult::BadParameter; } @@ -169,9 +182,9 @@ NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector& input, std::vector params.value.raw = 0; - events_interface.status[slot].store(EventState::Waiting, std::memory_order_release); - events_interface.assigned_syncpt[slot] = fence_id; - events_interface.assigned_value[slot] = target_value; + event.status.store(EventState::Waiting, std::memory_order_release); + event.assigned_syncpt = fence_id; + event.assigned_value = target_value; if (is_allocation) { params.value.syncpoint_id_for_allocation.Assign(static_cast(fence_id)); params.value.event_allocated.Assign(1); @@ -189,15 +202,17 @@ NvResult nvhost_ctrl::FreeEvent(u32 slot) { return NvResult::BadParameter; } - if (!events_interface.registered[slot]) { + auto& event = events[slot]; + + if (!event.registered) { return NvResult::Success; } - if (events_interface.IsBeingUsed(slot)) { + if (event.IsBeingUsed()) { return NvResult::Busy; } - events_interface.Free(slot); + FreeNvEvent(slot); return NvResult::Success; } @@ -210,15 +225,15 @@ NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector& input, std::ve return NvResult::BadParameter; } - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.registered[event_id]) { + if (events[event_id].registered) { const auto result = FreeEvent(event_id); if (result != NvResult::Success) { return result; } } - events_interface.Create(event_id); + CreateNvEvent(event_id); return NvResult::Success; } @@ -229,7 +244,7 @@ NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector& input, const u32 event_id = params.user_event_id & 0x00FF; LOG_DEBUG(Service_NVDRV, " called, user_event_id: {:X}", event_id); - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); return FreeEvent(event_id); } @@ -244,44 +259,121 @@ NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector& input, std::v return NvResult::BadParameter; } - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.status[event_id].exchange( - EventState::Cancelling, std::memory_order_acq_rel) == EventState::Waiting) { - system.GPU().CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id], - events_interface.assigned_value[event_id]); - syncpoint_manager.RefreshSyncpoint(events_interface.assigned_syncpt[event_id]); + auto& event = events[event_id]; + if (event.status.exchange(EventState::Cancelling, std::memory_order_acq_rel) == + EventState::Waiting) { + system.GPU().CancelSyncptInterrupt(event.assigned_syncpt, event.assigned_value); + syncpoint_manager.RefreshSyncpoint(event.assigned_syncpt); } - events_interface.fails[event_id]++; - events_interface.status[event_id].store(EventState::Cancelled, std::memory_order_release); - events_interface.events[event_id]->GetWritableEvent().Clear(); + event.fails++; + event.status.store(EventState::Cancelled, std::memory_order_release); + event.kevent->GetWritableEvent().Clear(); return NvResult::Success; } Kernel::KEvent* nvhost_ctrl::QueryEvent(u32 event_id) { - const auto event = SyncpointEventValue{.raw = event_id}; + const auto desired_event = SyncpointEventValue{.raw = event_id}; - const bool allocated = event.event_allocated.Value() != 0; - const u32 slot{allocated ? event.partial_slot.Value() : static_cast(event.slot)}; + const bool allocated = desired_event.event_allocated.Value() != 0; + const u32 slot{allocated ? desired_event.partial_slot.Value() + : static_cast(desired_event.slot)}; if (slot >= MaxNvEvents) { ASSERT(false); return nullptr; } - const u32 syncpoint_id{allocated ? event.syncpoint_id_for_allocation.Value() - : event.syncpoint_id.Value()}; + const u32 syncpoint_id{allocated ? desired_event.syncpoint_id_for_allocation.Value() + : desired_event.syncpoint_id.Value()}; - auto lock = events_interface.Lock(); + auto lock = NvEventsLock(); - if (events_interface.registered[slot] && - events_interface.assigned_syncpt[slot] == syncpoint_id) { - ASSERT(events_interface.events[slot]); - return events_interface.events[slot]; + auto& event = events[slot]; + if (event.registered && event.assigned_syncpt == syncpoint_id) { + ASSERT(event.kevent); + return event.kevent; } // Is this possible in hardware? ASSERT_MSG(false, "Slot:{}, SyncpointID:{}, requested", slot, syncpoint_id); return nullptr; } +std::unique_lock nvhost_ctrl::NvEventsLock() { + return std::unique_lock(events_mutex); +} + +void nvhost_ctrl::CreateNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(!event.kevent); + ASSERT(!event.registered); + ASSERT(!event.IsBeingUsed()); + event.kevent = events_interface.CreateEvent(fmt::format("NVCTRL::NvEvent_{}", event_id)); + event.status = EventState::Available; + event.registered = true; + const u64 mask = 1ULL << event_id; + event.fails = 0; + events_mask |= mask; + event.assigned_syncpt = 0; +} + +void nvhost_ctrl::FreeNvEvent(u32 event_id) { + auto& event = events[event_id]; + ASSERT(event.kevent); + ASSERT(event.registered); + ASSERT(!event.IsBeingUsed()); + events_interface.FreeEvent(event.kevent); + event.kevent = nullptr; + event.status = EventState::Available; + event.registered = false; + const u64 mask = ~(1ULL << event_id); + events_mask &= mask; +} + +u32 nvhost_ctrl::FindFreeNvEvent(u32 syncpoint_id) { + u32 slot{MaxNvEvents}; + u32 free_slot{MaxNvEvents}; + for (u32 i = 0; i < MaxNvEvents; i++) { + auto& event = events[i]; + if (event.registered) { + if (!event.IsBeingUsed()) { + slot = i; + if (event.assigned_syncpt == syncpoint_id) { + return slot; + } + } + } else if (free_slot == MaxNvEvents) { + free_slot = i; + } + } + if (free_slot < MaxNvEvents) { + CreateNvEvent(free_slot); + return free_slot; + } + + if (slot < MaxNvEvents) { + return slot; + } + + LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); + return 0; +} + +void nvhost_ctrl::SignalNvEvent(u32 syncpoint_id, u32 value) { + const u32 max = MaxNvEvents - std::countl_zero(events_mask); + const u32 min = std::countr_zero(events_mask); + for (u32 i = min; i < max; i++) { + auto& event = events[i]; + if (event.assigned_syncpt != syncpoint_id || event.assigned_value != value) { + continue; + } + if (event.status.exchange(EventState::Signalling, std::memory_order_acq_rel) == + EventState::Waiting) { + event.kevent->GetWritableEvent().Signal(); + } + event.status.store(EventState::Signalled, std::memory_order_release); + } +} + } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h index 9fd46ea5f..f2fc5d047 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h @@ -53,7 +53,49 @@ public: }; static_assert(sizeof(SyncpointEventValue) == sizeof(u32)); + void SignalNvEvent(u32 syncpoint_id, u32 value); + private: + struct InternalEvent { + // Mask representing registered events + + // Each kernel event associated to an NV event + Kernel::KEvent* kevent{}; + // The status of the current NVEvent + std::atomic status{}; + + // Tells the NVEvent that it has failed. + u32 fails{}; + // When an NVEvent is waiting on GPU interrupt, this is the sync_point + // associated with it. + u32 assigned_syncpt{}; + // This is the value of the GPU interrupt for which the NVEvent is waiting + // for. + u32 assigned_value{}; + + // Tells if an NVEvent is registered or not + bool registered{}; + + bool IsBeingUsed() { + const auto current_status = status.load(std::memory_order_acquire); + return current_status == EventState::Waiting || + current_status == EventState::Cancelling || + current_status == EventState::Signalling; + } + }; + + std::unique_lock NvEventsLock(); + + void CreateNvEvent(u32 event_id); + + void FreeNvEvent(u32 event_id); + + u32 FindFreeNvEvent(u32 syncpoint_id); + + std::array events{}; + std::mutex events_mutex; + u64 events_mask{}; + struct IocSyncptReadParams { u32_le id{}; u32_le value{}; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp index e353408eb..ced57dfe6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl_gpu.cpp @@ -13,10 +13,13 @@ namespace Service::Nvidia::Devices { nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system_, EventInterface& events_interface_) : nvdevice{system_}, events_interface{events_interface_} { - error_notifier_event = events_interface.CreateNonCtrlEvent("CtrlGpuErrorNotifier"); - unknown_event = events_interface.CreateNonCtrlEvent("CtrlGpuUknownEvent"); + error_notifier_event = events_interface.CreateEvent("CtrlGpuErrorNotifier"); + unknown_event = events_interface.CreateEvent("CtrlGpuUknownEvent"); +} +nvhost_ctrl_gpu::~nvhost_ctrl_gpu() { + events_interface.FreeEvent(error_notifier_event); + events_interface.FreeEvent(unknown_event); } -nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default; NvResult nvhost_ctrl_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp index e7921ade2..cb54ee5a4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp @@ -30,13 +30,17 @@ nvhost_gpu::nvhost_gpu(Core::System& system_, EventInterface& events_interface_, channel_fence.id = syncpoint_manager.AllocateSyncpoint(); channel_fence.value = system_.GPU().GetSyncpointValue(channel_fence.id); sm_exception_breakpoint_int_report_event = - events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointInt"); + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointInt"); sm_exception_breakpoint_pause_report_event = - events_interface.CreateNonCtrlEvent("GpuChannelSMExceptionBreakpointPause"); - error_notifier_event = events_interface.CreateNonCtrlEvent("GpuChannelErrorNotifier"); + events_interface.CreateEvent("GpuChannelSMExceptionBreakpointPause"); + error_notifier_event = events_interface.CreateEvent("GpuChannelErrorNotifier"); } -nvhost_gpu::~nvhost_gpu() = default; +nvhost_gpu::~nvhost_gpu() { + events_interface.FreeEvent(sm_exception_breakpoint_int_report_event); + events_interface.FreeEvent(sm_exception_breakpoint_pause_report_event); + events_interface.FreeEvent(error_notifier_event); +} NvResult nvhost_gpu::Ioctl1(DeviceFD fd, Ioctl command, const std::vector& input, std::vector& output) { diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp index aa1a00832..00947ea19 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp @@ -10,6 +10,8 @@ namespace Service::Nvidia::Devices { +u32 nvhost_nvdec::next_id{}; + nvhost_nvdec::nvhost_nvdec(Core::System& system_, NvCore::Container& core) : nvhost_nvdec_common{system_, core} {} nvhost_nvdec::~nvhost_nvdec() = default; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h index fef4b3216..3261ce1d4 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h @@ -24,7 +24,7 @@ public: void OnClose(DeviceFD fd) override; private: - u32 next_id{}; + static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp index e76c9e5ed..77e6a1cd6 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp @@ -45,6 +45,8 @@ std::size_t WriteVectors(std::vector& dst, const std::vector& src, std::s } } // Anonymous namespace +std::unordered_map nvhost_nvdec_common::fd_to_id{}; + nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system_, NvCore::Container& core_) : nvdevice{system_}, core{core_}, syncpoint_manager{core.GetSyncpointManager()}, nvmap{core.GetNvMapFile()} {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h index 74231d5c5..53029af6a 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h @@ -115,7 +115,7 @@ protected: Kernel::KEvent* QueryEvent(u32 event_id) override; - std::unordered_map fd_to_id{}; + static std::unordered_map fd_to_id; s32_le nvmap_fd{}; u32_le submit_timeout{}; NvCore::Container& core; diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp index 358e89aa8..c89ff6b27 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp @@ -8,6 +8,9 @@ #include "video_core/renderer_base.h" namespace Service::Nvidia::Devices { + +u32 nvhost_vic::next_id{}; + nvhost_vic::nvhost_vic(Core::System& system_, NvCore::Container& core) : nvhost_nvdec_common{system_, core} {} diff --git a/src/core/hle/service/nvdrv/devices/nvhost_vic.h b/src/core/hle/service/nvdrv/devices/nvhost_vic.h index 252b1e6f2..59e23b41e 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h +++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h @@ -23,6 +23,6 @@ public: void OnClose(DeviceFD fd) override; private: - u32 next_id{}; + static u32 next_id; }; } // namespace Service::Nvidia::Devices diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp index f4914d539..ff8c7c13c 100644 --- a/src/core/hle/service/nvdrv/nvdrv.cpp +++ b/src/core/hle/service/nvdrv/nvdrv.cpp @@ -3,7 +3,6 @@ // SPDX-License-Identifier: GPL-3.0-or-later Licensed under GPLv3 // or any later version Refer to the license.txt file included. -#include #include #include @@ -30,101 +29,39 @@ namespace Service::Nvidia { -EventInterface::EventInterface(Module& module_) : module{module_} { - events_mask = 0; - for (u32 i = 0; i < MaxNvEvents; i++) { - status[i] = EventState::Available; - events[i] = nullptr; - registered[i] = false; +EventInterface::EventInterface(Module& module_) : module{module_} {} + +EventInterface::~EventInterface() = default; + +void EventInterface::RegisterForSignal(Devices::nvhost_ctrl* device) { + std::unique_lock lk(guard); + on_signal.push_back(device); +} + +void EventInterface::UnregisterForSignal(Devices::nvhost_ctrl* device) { + std::unique_lock lk(guard); + auto it = std::find(on_signal.begin(), on_signal.end(), device); + if (it != on_signal.end()) { + on_signal.erase(it); } } -EventInterface::~EventInterface() { - auto lk = Lock(); - for (u32 i = 0; i < MaxNvEvents; i++) { - if (registered[i]) { - module.service_context.CloseEvent(events[i]); - events[i] = nullptr; - registered[i] = false; - } - } - for (auto* event : basic_events) { - module.service_context.CloseEvent(event); +void EventInterface::Signal(u32 syncpoint_id, u32 value) { + std::unique_lock lk(guard); + for (auto* device : on_signal) { + device->SignalNvEvent(syncpoint_id, value); } } -std::unique_lock EventInterface::Lock() { - return std::unique_lock(events_mutex); -} - -void EventInterface::Signal(u32 event_id) { - if (status[event_id].exchange(EventState::Signalling, std::memory_order_acq_rel) == - EventState::Waiting) { - events[event_id]->GetWritableEvent().Signal(); - } - status[event_id].store(EventState::Signalled, std::memory_order_release); -} - -void EventInterface::Create(u32 event_id) { - ASSERT(!events[event_id]); - ASSERT(!registered[event_id]); - ASSERT(!IsBeingUsed(event_id)); - events[event_id] = - module.service_context.CreateEvent(fmt::format("NVDRV::NvEvent_{}", event_id)); - status[event_id] = EventState::Available; - registered[event_id] = true; - const u64 mask = 1ULL << event_id; - fails[event_id] = 0; - events_mask |= mask; - assigned_syncpt[event_id] = 0; -} - -void EventInterface::Free(u32 event_id) { - ASSERT(events[event_id]); - ASSERT(registered[event_id]); - ASSERT(!IsBeingUsed(event_id)); - module.service_context.CloseEvent(events[event_id]); - events[event_id] = nullptr; - status[event_id] = EventState::Available; - registered[event_id] = false; - const u64 mask = ~(1ULL << event_id); - events_mask &= mask; -} - -u32 EventInterface::FindFreeEvent(u32 syncpoint_id) { - u32 slot{MaxNvEvents}; - u32 free_slot{MaxNvEvents}; - for (u32 i = 0; i < MaxNvEvents; i++) { - if (registered[i]) { - if (!IsBeingUsed(i)) { - slot = i; - if (assigned_syncpt[i] == syncpoint_id) { - return slot; - } - } - } else if (free_slot == MaxNvEvents) { - free_slot = i; - } - } - if (free_slot < MaxNvEvents) { - Create(free_slot); - return free_slot; - } - - if (slot < MaxNvEvents) { - return slot; - } - - LOG_CRITICAL(Service_NVDRV, "Failed to allocate an event"); - return 0; -} - -Kernel::KEvent* EventInterface::CreateNonCtrlEvent(std::string name) { +Kernel::KEvent* EventInterface::CreateEvent(std::string name) { Kernel::KEvent* new_event = module.service_context.CreateEvent(std::move(name)); - basic_events.push_back(new_event); return new_event; } +void EventInterface::FreeEvent(Kernel::KEvent* event) { + module.service_context.CloseEvent(event); +} + void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger& nvflinger, Core::System& system) { auto module_ = std::make_shared(system); @@ -138,18 +75,50 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger Module::Module(Core::System& system) : service_context{system, "nvdrv"}, events_interface{*this}, container{system.GPU()} { - devices["/dev/nvhost-as-gpu"] = std::make_shared(system, container); - devices["/dev/nvhost-gpu"] = - std::make_shared(system, events_interface, container); - devices["/dev/nvhost-ctrl-gpu"] = - std::make_shared(system, events_interface); - devices["/dev/nvmap"] = std::make_shared(system, container); - devices["/dev/nvdisp_disp0"] = std::make_shared(system, container); - devices["/dev/nvhost-ctrl"] = - std::make_shared(system, events_interface, container); - devices["/dev/nvhost-nvdec"] = std::make_shared(system, container); - devices["/dev/nvhost-nvjpg"] = std::make_shared(system); - devices["/dev/nvhost-vic"] = std::make_shared(system, container); + builders["/dev/nvhost-as-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl-gpu"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvmap"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvdisp_disp0"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-ctrl"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, events_interface, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvdec"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-nvjpg"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = std::make_shared(system); + return open_files.emplace(fd, device).first; + }; + builders["/dev/nvhost-vic"] = [this, &system](DeviceFD fd) { + std::shared_ptr device = + std::make_shared(system, container); + return open_files.emplace(fd, device).first; + }; } Module::~Module() = default; @@ -169,18 +138,18 @@ NvResult Module::VerifyFD(DeviceFD fd) const { } DeviceFD Module::Open(const std::string& device_name) { - if (devices.find(device_name) == devices.end()) { + auto it = builders.find(device_name); + if (it == builders.end()) { LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name); return INVALID_NVDRV_FD; } - auto device = devices[device_name]; const DeviceFD fd = next_fd++; + auto& builder = it->second; + auto device = builder(fd)->second; device->OnOpen(fd); - open_files[fd] = std::move(device); - return fd; } @@ -256,14 +225,7 @@ NvResult Module::Close(DeviceFD fd) { } void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) { - const u32 max = MaxNvEvents - std::countl_zero(events_interface.events_mask); - const u32 min = std::countr_zero(events_interface.events_mask); - for (u32 i = min; i < max; i++) { - if (events_interface.assigned_syncpt[i] == syncpoint_id && - events_interface.assigned_value[i] == value) { - events_interface.Signal(i); - } - } + events_interface.Signal(syncpoint_id, value); } NvResult Module::QueryEvent(DeviceFD fd, u32 event_id, Kernel::KEvent*& event) { diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h index 96adf2ffb..3983794bb 100644 --- a/src/core/hle/service/nvdrv/nvdrv.h +++ b/src/core/hle/service/nvdrv/nvdrv.h @@ -5,6 +5,7 @@ #pragma once +#include #include #include #include @@ -38,7 +39,8 @@ class SyncpointManager; namespace Devices { class nvdevice; -} +class nvhost_ctrl; +} // namespace Devices class Module; @@ -47,47 +49,19 @@ public: EventInterface(Module& module_); ~EventInterface(); - // Mask representing registered events - u64 events_mask{}; - // Each kernel event associated to an NV event - std::array events{}; - // The status of the current NVEvent - std::array, MaxNvEvents> status{}; - // Tells if an NVEvent is registered or not - std::array registered{}; - // Tells the NVEvent that it has failed. - std::array fails{}; - // When an NVEvent is waiting on GPU interrupt, this is the sync_point - // associated with it. - std::array assigned_syncpt{}; - // This is the value of the GPU interrupt for which the NVEvent is waiting - // for. - std::array assigned_value{}; - // Constant to denote an unasigned syncpoint. - static constexpr u32 unassigned_syncpt = 0xFFFFFFFF; + void RegisterForSignal(Devices::nvhost_ctrl*); + void UnregisterForSignal(Devices::nvhost_ctrl*); - bool IsBeingUsed(u32 event_id) { - const auto current_status = status[event_id].load(std::memory_order_acquire); - return current_status == EventState::Waiting || current_status == EventState::Cancelling || - current_status == EventState::Signalling; - } + void Signal(u32 syncpoint_id, u32 value); - std::unique_lock Lock(); + Kernel::KEvent* CreateEvent(std::string name); - void Signal(u32 event_id); - - void Create(u32 event_id); - - void Free(u32 event_id); - - u32 FindFreeEvent(u32 syncpoint_id); - - Kernel::KEvent* CreateNonCtrlEvent(std::string name); + void FreeEvent(Kernel::KEvent* event); private: - std::mutex events_mutex; Module& module; - std::vector basic_events; + std::mutex guard; + std::list on_signal; }; class Module final { @@ -97,9 +71,9 @@ public: /// Returns a pointer to one of the available devices, identified by its name. template - std::shared_ptr GetDevice(const std::string& name) { - auto itr = devices.find(name); - if (itr == devices.end()) + std::shared_ptr GetDevice(DeviceFD fd) { + auto itr = open_files.find(fd); + if (itr == open_files.end()) return nullptr; return std::static_pointer_cast(itr->second); } @@ -132,8 +106,9 @@ private: /// Id to use for the next open file descriptor. DeviceFD next_fd = 1; + using FilesContainerType = std::unordered_map>; /// Mapping of file descriptors to the devices they reference. - std::unordered_map> open_files; + FilesContainerType open_files; /// Mapping of device node names to their implementation. std::unordered_map> devices; @@ -147,6 +122,7 @@ private: void CreateEvent(u32 event_id); void FreeEvent(u32 event_id); + std::unordered_map> builders; }; /// Registers all NVDRV services with the specified service manager. diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp index 4246e5e25..8c3013f83 100644 --- a/src/core/hle/service/nvflinger/nvflinger.cpp +++ b/src/core/hle/service/nvflinger/nvflinger.cpp @@ -105,10 +105,15 @@ NVFlinger::~NVFlinger() { display.GetLayer(layer).Core().NotifyShutdown(); } } + + if (nvdrv) { + nvdrv->Close(disp_fd); + } } void NVFlinger::SetNVDrvInstance(std::shared_ptr instance) { nvdrv = std::move(instance); + disp_fd = nvdrv->Open("/dev/nvdisp_disp0"); } std::optional NVFlinger::OpenDisplay(std::string_view name) { @@ -276,7 +281,7 @@ void NVFlinger::Compose() { // Now send the buffer to the GPU for drawing. // TODO(Subv): Support more than just disp0. The display device selection is probably based // on which display we're drawing (Default, Internal, External, etc) - auto nvdisp = nvdrv->GetDevice("/dev/nvdisp_disp0"); + auto nvdisp = nvdrv->GetDevice(disp_fd); ASSERT(nvdisp); Common::Rectangle crop_rect{ diff --git a/src/core/hle/service/nvflinger/nvflinger.h b/src/core/hle/service/nvflinger/nvflinger.h index 3bbe5d92b..b62615de2 100644 --- a/src/core/hle/service/nvflinger/nvflinger.h +++ b/src/core/hle/service/nvflinger/nvflinger.h @@ -116,6 +116,7 @@ private: void SplitVSync(std::stop_token stop_token); std::shared_ptr nvdrv; + s32 disp_fd; std::list displays;