Merge pull request #4396 from FearlessTobi/open-delays
fs_user: Add a delay for each file open
This commit is contained in:
		| @@ -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<DefaultDelayGenerator>(); | ||||
|         return delay_generator->GetOpenDelayNs(); | ||||
|     } | ||||
|  | ||||
| protected: | ||||
|     std::unique_ptr<DelayGenerator> delay_generator; | ||||
| }; | ||||
|  | ||||
| class ArchiveFactory : NonCopyable { | ||||
|   | ||||
| @@ -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<u64>(static_cast<u64>(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<DelayGenerator> 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<std::unique_ptr<ArchiveBackend>> ArchiveFactory_ExtSaveData::Open(cons | ||||
|             return ERR_NOT_FORMATTED; | ||||
|         } | ||||
|     } | ||||
|     auto archive = std::make_unique<ExtSaveDataArchive>(fullpath); | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<ExtSaveDataDelayGenerator>(); | ||||
|     auto archive = std::make_unique<ExtSaveDataArchive>(fullpath, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -29,6 +29,14 @@ public: | ||||
|         u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(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<std::unique_ptr<FileBackend>> SDMCArchive::OpenFile(const Path& path, | ||||
| @@ -378,7 +386,8 @@ bool ArchiveFactory_SDMC::Initialize() { | ||||
|  | ||||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMC::Open(const Path& path, | ||||
|                                                                      u64 program_id) { | ||||
|     auto archive = std::make_unique<SDMCArchive>(sdmc_directory); | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = std::make_unique<SDMCDelayGenerator>(); | ||||
|     auto archive = std::make_unique<SDMCArchive>(sdmc_directory, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<DelayGenerator> delay_generator_) | ||||
|         : mount_point(mount_point_) { | ||||
|         delay_generator = std::move(delay_generator_); | ||||
|     } | ||||
|  | ||||
|     std::string GetName() const override { | ||||
|         return "SDMCArchive: " + mount_point; | ||||
|   | ||||
| @@ -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<u64>(static_cast<u64>(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<std::unique_ptr<FileBackend>> SDMCWriteOnlyArchive::OpenFile(const Path& path, | ||||
|                                                                        const Mode& mode) const { | ||||
|     if (mode.read_flag) { | ||||
| @@ -51,7 +73,10 @@ bool ArchiveFactory_SDMCWriteOnly::Initialize() { | ||||
|  | ||||
| ResultVal<std::unique_ptr<ArchiveBackend>> ArchiveFactory_SDMCWriteOnly::Open(const Path& path, | ||||
|                                                                               u64 program_id) { | ||||
|     auto archive = std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory); | ||||
|     std::unique_ptr<DelayGenerator> delay_generator = | ||||
|         std::make_unique<SDMCWriteOnlyDelayGenerator>(); | ||||
|     auto archive = | ||||
|         std::make_unique<SDMCWriteOnlyArchive>(sdmc_directory, std::move(delay_generator)); | ||||
|     return MakeResult<std::unique_ptr<ArchiveBackend>>(std::move(archive)); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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<DelayGenerator> delay_generator_) | ||||
|         : SDMCArchive(mount_point, std::move(delay_generator_)) {} | ||||
|  | ||||
|     std::string GetName() const override { | ||||
|         return "SDMCWriteOnlyArchive: " + mount_point; | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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 | ||||
|   | ||||
| @@ -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<DefaultDelayGenerator>(); | ||||
|         return delay_generator->GetOpenDelayNs(); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Get the size of the file in bytes | ||||
|      * @return Size of the file in bytes | ||||
|   | ||||
| @@ -14,7 +14,11 @@ | ||||
|  | ||||
| namespace FileSys { | ||||
|  | ||||
| IVFCArchive::IVFCArchive(std::shared_ptr<RomFSReader> file) : romfs_file(std::move(file)) {} | ||||
| IVFCArchive::IVFCArchive(std::shared_ptr<RomFSReader> file, | ||||
|                          std::unique_ptr<DelayGenerator> delay_generator_) | ||||
|     : romfs_file(std::move(file)) { | ||||
|     delay_generator = std::move(delay_generator_); | ||||
| } | ||||
|  | ||||
| std::string IVFCArchive::GetName() const { | ||||
|     return "IVFC"; | ||||
|   | ||||
| @@ -31,6 +31,13 @@ class IVFCDelayGenerator : public DelayGenerator { | ||||
|         u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(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<u64>(static_cast<u64>(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<u64>(static_cast<u64>(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<RomFSReader> file); | ||||
|     IVFCArchive(std::shared_ptr<RomFSReader> file, | ||||
|                 std::unique_ptr<DelayGenerator> delay_generator_); | ||||
|  | ||||
|     std::string GetName() const override; | ||||
|  | ||||
|   | ||||
| @@ -25,6 +25,14 @@ public: | ||||
|         u64 IPCDelayNanoseconds = std::max<u64>(static_cast<u64>(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<std::unique_ptr<FileBackend>> SaveDataArchive::OpenFile(const Path& path, | ||||
|   | ||||
| @@ -77,19 +77,21 @@ ResultCode ArchiveManager::RegisterArchiveType(std::unique_ptr<FileSys::ArchiveF | ||||
|     return RESULT_SUCCESS; | ||||
| } | ||||
|  | ||||
| ResultVal<std::shared_ptr<File>> ArchiveManager::OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                                      const FileSys::Path& path, | ||||
|                                                                      const FileSys::Mode mode) { | ||||
| std::tuple<ResultVal<std::shared_ptr<File>>, 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<std::chrono::nanoseconds>(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<File>(new File(system, std::move(backend).Unwrap(), path)); | ||||
|     return MakeResult<std::shared_ptr<File>>(std::move(file)); | ||||
|     return std::make_tuple(MakeResult<std::shared_ptr<File>>(std::move(file)), open_timeout_ns); | ||||
| } | ||||
|  | ||||
| ResultCode ArchiveManager::DeleteFileFromArchive(ArchiveHandle archive_handle, | ||||
|   | ||||
| @@ -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<std::shared_ptr<File>> OpenFileFromArchive(ArchiveHandle archive_handle, | ||||
|                                                          const FileSys::Path& path, | ||||
|                                                          const FileSys::Mode mode); | ||||
|     std::tuple<ResultVal<std::shared_ptr<File>>, std::chrono::nanoseconds> OpenFileFromArchive( | ||||
|         ArchiveHandle archive_handle, const FileSys::Path& path, const FileSys::Mode mode); | ||||
|  | ||||
|     /** | ||||
|      * Delete a File from an Archive | ||||
|   | ||||
| @@ -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<Kernel::Thread> thread, | ||||
|                              Kernel::HLERequestContext& ctx, Kernel::ThreadWakeupReason reason) { | ||||
|                               // Nothing to do here | ||||
|                           }); | ||||
|     ctx.SleepClientThread( | ||||
|         system.Kernel().GetThreadManager().GetCurrentThread(), "file::read", read_timeout_ns, | ||||
|         [](Kernel::SharedPtr<Kernel::Thread> /*thread*/, Kernel::HLERequestContext& /*ctx*/, | ||||
|            Kernel::ThreadWakeupReason /*reason*/) { | ||||
|             // Nothing to do here | ||||
|         }); | ||||
| } | ||||
|  | ||||
| void File::Write(Kernel::HLERequestContext& ctx) { | ||||
|   | ||||
| @@ -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<std::shared_ptr<File>> 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<Kernel::Object>(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<Kernel::Thread> /*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<std::shared_ptr<File>> 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<Kernel::Thread> /*thread*/, | ||||
|                              Kernel::HLERequestContext& /*ctx*/, | ||||
|                              Kernel::ThreadWakeupReason /*reason*/) { | ||||
|                               // Nothing to do here | ||||
|                           }); | ||||
| } | ||||
|  | ||||
| void FS_USER::DeleteFile(Kernel::HLERequestContext& ctx) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user