diff --git a/src/core/file_sys/archive_backend.h b/src/core/file_sys/archive_backend.h index 1a57e12d3..259ece84a 100644 --- a/src/core/file_sys/archive_backend.h +++ b/src/core/file_sys/archive_backend.h @@ -11,6 +11,7 @@ #include "common/bit_field.h" #include "common/common_types.h" #include "common/swap.h" +#include "core/file_sys/delay_generator.h" #include "core/hle/result.h" namespace FileSys { @@ -153,6 +154,18 @@ public: * @return The number of free bytes in the archive */ virtual u64 GetFreeBytes() const = 0; + + u64 GetOpenDelayNs() { + if (delay_generator != nullptr) { + return delay_generator->GetOpenDelayNs(); + } + LOG_ERROR(Service_FS, "Delay generator was not initalized. Using default"); + delay_generator = std::make_unique(); + return delay_generator->GetOpenDelayNs(); + } + +protected: + std::unique_ptr delay_generator; }; class ArchiveFactory : NonCopyable { diff --git a/src/core/file_sys/archive_extsavedata.cpp b/src/core/file_sys/archive_extsavedata.cpp index 2839483f5..efcdb2d7f 100644 --- a/src/core/file_sys/archive_extsavedata.cpp +++ b/src/core/file_sys/archive_extsavedata.cpp @@ -59,7 +59,7 @@ private: class ExtSaveDataDelayGenerator : public DelayGenerator { public: u64 GetReadDelayNs(std::size_t length) override { - // This is the delay measured for a savedate read, + // This is the delay measured for a savedata read, // not for extsaveData // For now we will take that static constexpr u64 slope(183); @@ -69,6 +69,14 @@ public: std::max(static_cast(length) * slope + offset, minimum); return ipc_delay_nanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured on N3DS with + // https://gist.github.com/FearlessTobi/929b68489f4abb2c6cf81d56970a20b4 + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(3085068); + return IPCDelayNanoseconds; + } }; /** @@ -80,7 +88,11 @@ public: */ class ExtSaveDataArchive : public SaveDataArchive { public: - explicit ExtSaveDataArchive(const std::string& mount_point) : SaveDataArchive(mount_point) {} + explicit ExtSaveDataArchive(const std::string& mount_point, + std::unique_ptr delay_generator_) + : SaveDataArchive(mount_point) { + delay_generator = std::move(delay_generator_); + } std::string GetName() const override { return "ExtSaveDataArchive: " + mount_point; @@ -232,7 +244,8 @@ ResultVal> ArchiveFactory_ExtSaveData::Open(cons return ERR_NOT_FORMATTED; } } - auto archive = std::make_unique(fullpath); + std::unique_ptr delay_generator = std::make_unique(); + auto archive = std::make_unique(fullpath, std::move(delay_generator)); return MakeResult>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.cpp b/src/core/file_sys/archive_sdmc.cpp index cff4cd395..d2269fe7c 100644 --- a/src/core/file_sys/archive_sdmc.cpp +++ b/src/core/file_sys/archive_sdmc.cpp @@ -29,6 +29,14 @@ public: u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); return IPCDelayNanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/FearlessTobi/c37e143c314789251f98f2c45cd706d2 + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(269082); + return IPCDelayNanoseconds; + } }; ResultVal> SDMCArchive::OpenFile(const Path& path, @@ -378,7 +386,8 @@ bool ArchiveFactory_SDMC::Initialize() { ResultVal> ArchiveFactory_SDMC::Open(const Path& path, u64 program_id) { - auto archive = std::make_unique(sdmc_directory); + std::unique_ptr delay_generator = std::make_unique(); + auto archive = std::make_unique(sdmc_directory, std::move(delay_generator)); return MakeResult>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmc.h b/src/core/file_sys/archive_sdmc.h index 637d4dc16..41d7b7c59 100644 --- a/src/core/file_sys/archive_sdmc.h +++ b/src/core/file_sys/archive_sdmc.h @@ -17,7 +17,11 @@ namespace FileSys { /// Archive backend for SDMC archive class SDMCArchive : public ArchiveBackend { public: - explicit SDMCArchive(const std::string& mount_point_) : mount_point(mount_point_) {} + explicit SDMCArchive(const std::string& mount_point_, + std::unique_ptr delay_generator_) + : mount_point(mount_point_) { + delay_generator = std::move(delay_generator_); + } std::string GetName() const override { return "SDMCArchive: " + mount_point; diff --git a/src/core/file_sys/archive_sdmcwriteonly.cpp b/src/core/file_sys/archive_sdmcwriteonly.cpp index b9534f12d..74552d751 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.cpp +++ b/src/core/file_sys/archive_sdmcwriteonly.cpp @@ -15,6 +15,28 @@ namespace FileSys { +class SDMCWriteOnlyDelayGenerator : public DelayGenerator { +public: + u64 GetReadDelayNs(std::size_t length) override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/B3n30/ac40eac20603f519ff106107f4ac9182 + // from the results the average of each length was taken. + static constexpr u64 slope(183); + static constexpr u64 offset(524879); + static constexpr u64 minimum(631826); + u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); + return IPCDelayNanoseconds; + } + + u64 GetOpenDelayNs() override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/FearlessTobi/c37e143c314789251f98f2c45cd706d2 + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(269082); + return IPCDelayNanoseconds; + } +}; + ResultVal> SDMCWriteOnlyArchive::OpenFile(const Path& path, const Mode& mode) const { if (mode.read_flag) { @@ -51,7 +73,10 @@ bool ArchiveFactory_SDMCWriteOnly::Initialize() { ResultVal> ArchiveFactory_SDMCWriteOnly::Open(const Path& path, u64 program_id) { - auto archive = std::make_unique(sdmc_directory); + std::unique_ptr delay_generator = + std::make_unique(); + auto archive = + std::make_unique(sdmc_directory, std::move(delay_generator)); return MakeResult>(std::move(archive)); } diff --git a/src/core/file_sys/archive_sdmcwriteonly.h b/src/core/file_sys/archive_sdmcwriteonly.h index 050fc19c8..8191f053f 100644 --- a/src/core/file_sys/archive_sdmcwriteonly.h +++ b/src/core/file_sys/archive_sdmcwriteonly.h @@ -19,7 +19,9 @@ namespace FileSys { */ class SDMCWriteOnlyArchive : public SDMCArchive { public: - explicit SDMCWriteOnlyArchive(const std::string& mount_point) : SDMCArchive(mount_point) {} + explicit SDMCWriteOnlyArchive(const std::string& mount_point, + std::unique_ptr delay_generator_) + : SDMCArchive(mount_point, std::move(delay_generator_)) {} std::string GetName() const override { return "SDMCWriteOnlyArchive: " + mount_point; diff --git a/src/core/file_sys/delay_generator.cpp b/src/core/file_sys/delay_generator.cpp index 654550061..04f877f83 100644 --- a/src/core/file_sys/delay_generator.cpp +++ b/src/core/file_sys/delay_generator.cpp @@ -19,4 +19,11 @@ u64 DefaultDelayGenerator::GetReadDelayNs(std::size_t length) { return IPCDelayNanoseconds; } +u64 DefaultDelayGenerator::GetOpenDelayNs() { + // This is the delay measured for a romfs open. + // For now we will take that as a default + static constexpr u64 IPCDelayNanoseconds(9438006); + return IPCDelayNanoseconds; +} + } // namespace FileSys diff --git a/src/core/file_sys/delay_generator.h b/src/core/file_sys/delay_generator.h index 06ef97281..d530f2ee2 100644 --- a/src/core/file_sys/delay_generator.h +++ b/src/core/file_sys/delay_generator.h @@ -13,6 +13,7 @@ class DelayGenerator { public: virtual ~DelayGenerator(); virtual u64 GetReadDelayNs(std::size_t length) = 0; + virtual u64 GetOpenDelayNs() = 0; // TODO (B3N30): Add getter for all other file/directory io operations }; @@ -20,6 +21,7 @@ public: class DefaultDelayGenerator : public DelayGenerator { public: u64 GetReadDelayNs(std::size_t length) override; + u64 GetOpenDelayNs() override; }; } // namespace FileSys diff --git a/src/core/file_sys/file_backend.h b/src/core/file_sys/file_backend.h index 103b81d60..c865c98e8 100644 --- a/src/core/file_sys/file_backend.h +++ b/src/core/file_sys/file_backend.h @@ -55,6 +55,15 @@ public: return delay_generator->GetReadDelayNs(length); } + u64 GetOpenDelayNs() { + if (delay_generator != nullptr) { + return delay_generator->GetOpenDelayNs(); + } + LOG_ERROR(Service_FS, "Delay generator was not initalized. Using default"); + delay_generator = std::make_unique(); + return delay_generator->GetOpenDelayNs(); + } + /** * Get the size of the file in bytes * @return Size of the file in bytes diff --git a/src/core/file_sys/ivfc_archive.cpp b/src/core/file_sys/ivfc_archive.cpp index 5a11e786d..ba16f8cd8 100644 --- a/src/core/file_sys/ivfc_archive.cpp +++ b/src/core/file_sys/ivfc_archive.cpp @@ -14,7 +14,11 @@ namespace FileSys { -IVFCArchive::IVFCArchive(std::shared_ptr file) : romfs_file(std::move(file)) {} +IVFCArchive::IVFCArchive(std::shared_ptr file, + std::unique_ptr delay_generator_) + : romfs_file(std::move(file)) { + delay_generator = std::move(delay_generator_); +} std::string IVFCArchive::GetName() const { return "IVFC"; diff --git a/src/core/file_sys/ivfc_archive.h b/src/core/file_sys/ivfc_archive.h index ded0b9075..8168e04f4 100644 --- a/src/core/file_sys/ivfc_archive.h +++ b/src/core/file_sys/ivfc_archive.h @@ -31,6 +31,13 @@ class IVFCDelayGenerator : public DelayGenerator { u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); return IPCDelayNanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured for a romfs open. + // For now we will take that as a default + static constexpr u64 IPCDelayNanoseconds(9438006); + return IPCDelayNanoseconds; + } }; class RomFSDelayGenerator : public DelayGenerator { @@ -45,6 +52,14 @@ public: u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); return IPCDelayNanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/FearlessTobi/eb1d70619c65c7e6f02141d71e79a36e + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(9438006); + return IPCDelayNanoseconds; + } }; class ExeFSDelayGenerator : public DelayGenerator { @@ -59,6 +74,14 @@ public: u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); return IPCDelayNanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/FearlessTobi/eb1d70619c65c7e6f02141d71e79a36e + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(9438006); + return IPCDelayNanoseconds; + } }; /** @@ -68,7 +91,8 @@ public: */ class IVFCArchive : public ArchiveBackend { public: - IVFCArchive(std::shared_ptr file); + IVFCArchive(std::shared_ptr file, + std::unique_ptr delay_generator_); std::string GetName() const override; diff --git a/src/core/file_sys/savedata_archive.cpp b/src/core/file_sys/savedata_archive.cpp index b2569e498..8d7830468 100644 --- a/src/core/file_sys/savedata_archive.cpp +++ b/src/core/file_sys/savedata_archive.cpp @@ -25,6 +25,14 @@ public: u64 IPCDelayNanoseconds = std::max(static_cast(length) * slope + offset, minimum); return IPCDelayNanoseconds; } + + u64 GetOpenDelayNs() override { + // This is the delay measured on O3DS and O2DS with + // https://gist.github.com/FearlessTobi/c37e143c314789251f98f2c45cd706d2 + // from the results the average of each length was taken. + static constexpr u64 IPCDelayNanoseconds(269082); + return IPCDelayNanoseconds; + } }; ResultVal> SaveDataArchive::OpenFile(const Path& path, diff --git a/src/core/hle/service/fs/archive.cpp b/src/core/hle/service/fs/archive.cpp index 184c319f0..6e179978b 100644 --- a/src/core/hle/service/fs/archive.cpp +++ b/src/core/hle/service/fs/archive.cpp @@ -77,19 +77,21 @@ ResultCode ArchiveManager::RegisterArchiveType(std::unique_ptr> ArchiveManager::OpenFileFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path, - const FileSys::Mode mode) { +std::tuple>, std::chrono::nanoseconds> +ArchiveManager::OpenFileFromArchive(ArchiveHandle archive_handle, const FileSys::Path& path, + const FileSys::Mode mode) { ArchiveBackend* archive = GetArchive(archive_handle); if (archive == nullptr) - return FileSys::ERR_INVALID_ARCHIVE_HANDLE; + return std::make_tuple(FileSys::ERR_INVALID_ARCHIVE_HANDLE, + static_cast(0)); + std::chrono::nanoseconds open_timeout_ns{archive->GetOpenDelayNs()}; auto backend = archive->OpenFile(path, mode); if (backend.Failed()) - return backend.Code(); + return std::make_tuple(backend.Code(), open_timeout_ns); auto file = std::shared_ptr(new File(system, std::move(backend).Unwrap(), path)); - return MakeResult>(std::move(file)); + return std::make_tuple(MakeResult>(std::move(file)), open_timeout_ns); } ResultCode ArchiveManager::DeleteFileFromArchive(ArchiveHandle archive_handle, diff --git a/src/core/hle/service/fs/archive.h b/src/core/hle/service/fs/archive.h index c1dd2bd1b..862e74980 100644 --- a/src/core/hle/service/fs/archive.h +++ b/src/core/hle/service/fs/archive.h @@ -77,11 +77,10 @@ public: * @param archive_handle Handle to an open Archive object * @param path Path to the File inside of the Archive * @param mode Mode under which to open the File - * @return The opened File object + * @return Tuple of the opened File object and the open delay */ - ResultVal> OpenFileFromArchive(ArchiveHandle archive_handle, - const FileSys::Path& path, - const FileSys::Mode mode); + std::tuple>, std::chrono::nanoseconds> OpenFileFromArchive( + ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); /** * Delete a File from an Archive diff --git a/src/core/hle/service/fs/file.cpp b/src/core/hle/service/fs/file.cpp index d4beda2c4..0edd93644 100644 --- a/src/core/hle/service/fs/file.cpp +++ b/src/core/hle/service/fs/file.cpp @@ -71,12 +71,12 @@ void File::Read(Kernel::HLERequestContext& ctx) { rb.PushMappedBuffer(buffer); std::chrono::nanoseconds read_timeout_ns{backend->GetReadDelayNs(length)}; - ctx.SleepClientThread(system.Kernel().GetThreadManager().GetCurrentThread(), "file::read", - read_timeout_ns, - [](Kernel::SharedPtr thread, - Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { - // Nothing to do here - }); + ctx.SleepClientThread( + system.Kernel().GetThreadManager().GetCurrentThread(), "file::read", read_timeout_ns, + [](Kernel::SharedPtr /*thread*/, Kernel::HLERequestContext& /*ctx*/, + Kernel::ThreadWakeupReason /*reason*/) { + // Nothing to do here + }); } void File::Write(Kernel::HLERequestContext& ctx) { diff --git a/src/core/hle/service/fs/fs_user.cpp b/src/core/hle/service/fs/fs_user.cpp index bbcc2e6fd..51bc18374 100644 --- a/src/core/hle/service/fs/fs_user.cpp +++ b/src/core/hle/service/fs/fs_user.cpp @@ -16,6 +16,7 @@ #include "core/hle/ipc_helpers.h" #include "core/hle/kernel/client_port.h" #include "core/hle/kernel/client_session.h" +#include "core/hle/kernel/event.h" #include "core/hle/kernel/process.h" #include "core/hle/kernel/server_session.h" #include "core/hle/result.h" @@ -59,7 +60,7 @@ void FS_USER::OpenFile(Kernel::HLERequestContext& ctx) { LOG_DEBUG(Service_FS, "path={}, mode={} attrs={}", file_path.DebugStr(), mode.hex, attributes); - ResultVal> file_res = + const auto [file_res, open_timeout_ns] = archives.OpenFileFromArchive(archive_handle, file_path, mode); IPC::RequestBuilder rb = rp.MakeBuilder(1, 2); rb.Push(file_res.Code()); @@ -70,6 +71,13 @@ void FS_USER::OpenFile(Kernel::HLERequestContext& ctx) { rb.PushMoveObjects(nullptr); LOG_ERROR(Service_FS, "failed to get a handle for file {}", file_path.DebugStr()); } + + ctx.SleepClientThread( + system.Kernel().GetThreadManager().GetCurrentThread(), "fs_user::open", open_timeout_ns, + [](Kernel::SharedPtr /*thread*/, Kernel::HLERequestContext& /*ctx*/, + Kernel::ThreadWakeupReason /*reason*/) { + // Nothing to do here + }); } void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) { @@ -110,7 +118,7 @@ void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) { } SCOPE_EXIT({ archives.CloseArchive(*archive_handle); }); - ResultVal> file_res = + const auto [file_res, open_timeout_ns] = archives.OpenFileFromArchive(*archive_handle, file_path, mode); rb.Push(file_res.Code()); if (file_res.Succeeded()) { @@ -121,6 +129,14 @@ void FS_USER::OpenFileDirectly(Kernel::HLERequestContext& ctx) { LOG_ERROR(Service_FS, "failed to get a handle for file {} mode={} attributes={}", file_path.DebugStr(), mode.hex, attributes); } + + ctx.SleepClientThread(system.Kernel().GetThreadManager().GetCurrentThread(), + "fs_user::open_directly", open_timeout_ns, + [](Kernel::SharedPtr /*thread*/, + Kernel::HLERequestContext& /*ctx*/, + Kernel::ThreadWakeupReason /*reason*/) { + // Nothing to do here + }); } void FS_USER::DeleteFile(Kernel::HLERequestContext& ctx) {