diff --git a/CHANGELOG.md b/CHANGELOG.md index 16c7943c..a13a12b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * The AIS optimized IO path will automatically fallback to the POSIX IO path if a failure occurs and the compatibility mode has not been disabled. * Added check in the Fastpath/AIS backend to ensure the HIP Runtime is initialized. This avoids causing a segfault in the HIP Runtime. * The default CMake build type was changed from `Debug` to `RelWithDebInfo` +* Added file type and file system validation in Fastpath. Fastpath will only accept IO targeting block devices or regular files backed by xfs or ext4 with ordered journaling mode. Other file systems can be explicitly allowed via the `HIPFILE_UNSUPPORTED_FILE_SYSTEMS` environment variable. ### Removed * The rocFile library has been completely removed and the code is now a part of hipFile. diff --git a/src/amd_detail/backend/fallback.cpp b/src/amd_detail/backend/fallback.cpp index 13c4b036..b82da993 100644 --- a/src/amd_detail/backend/fallback.cpp +++ b/src/amd_detail/backend/fallback.cpp @@ -97,7 +97,7 @@ Fallback::_io_impl(IoType type, std::shared_ptr file, std::shared_ptr::get()->pread(file->getBufferedFd(), bounce_buffer.get(), count, offset); + Context::get()->pread(file->bufferedFd(), bounce_buffer.get(), count, offset); if (io_bytes > 0) { Context::get()->hipMemcpy(device_buffer_position, bounce_buffer.get(), static_cast(io_bytes), hipMemcpyHostToDevice); @@ -107,9 +107,9 @@ Fallback::_io_impl(IoType type, std::shared_ptr file, std::shared_ptr::get()->hipMemcpy(bounce_buffer.get(), device_buffer_position, count, hipMemcpyDeviceToHost); Context::get()->hipStreamSynchronize(nullptr); - io_bytes = Context::get()->pwrite(file->getBufferedFd(), bounce_buffer.get(), count, - offset); - Context::get()->fdatasync(file->getBufferedFd()); + io_bytes = + Context::get()->pwrite(file->bufferedFd(), bounce_buffer.get(), count, offset); + Context::get()->fdatasync(file->bufferedFd()); break; default: throw std::runtime_error("Invalid IO type"); @@ -274,11 +274,11 @@ async_io_cpu_copy(void *userargs) try { switch (op->io_type) { case IoType::Read: - ret = Context::get()->pread(op->file->getBufferedFd(), cur_buf_position, + ret = Context::get()->pread(op->file->bufferedFd(), cur_buf_position, remaining_bytes, cur_file_offset); break; case IoType::Write: - ret = Context::get()->pwrite(op->file->getBufferedFd(), cur_buf_position, + ret = Context::get()->pwrite(op->file->bufferedFd(), cur_buf_position, remaining_bytes, cur_file_offset); break; default: diff --git a/src/amd_detail/backend/fastpath.cpp b/src/amd_detail/backend/fastpath.cpp index 60671b21..3ea3fe1f 100644 --- a/src/amd_detail/backend/fastpath.cpp +++ b/src/amd_detail/backend/fastpath.cpp @@ -136,28 +136,28 @@ Fastpath::score(shared_ptr file, shared_ptr buffer, size_t size, accept_io &= Context::get()->fastpath(); - accept_io &= file->getUnbufferedFd().has_value(); + accept_io &= file->unbufferedFd().has_value(); accept_io &= buffer->getType() == hipMemoryTypeDevice; accept_io &= 0 <= file_offset; accept_io &= 0 <= buffer_offset; - uint64_t mem_align_mask{4096 - 1}; - uint64_t offset_align_mask{4096 - 1}; + bool is_regular_file{file->isRegularFile()}; + bool is_block_device{file->isBlockDevice()}; + bool on_ext4_ordered{file->onExt4Ordered()}; + bool on_xfs{file->onXfs()}; + bool unsupported_file_systems{Context::get()->unsupportedFileSystems()}; + accept_io &= + is_block_device || (is_regular_file && (unsupported_file_systems || on_ext4_ordered || on_xfs)); -#if defined(STATX_DIOALIGN) - const struct statx &stx{file->getStatx()}; - accept_io &= !!(stx.stx_mask & STATX_DIOALIGN); - accept_io &= stx.stx_dio_offset_align && stx.stx_dio_mem_align; - mem_align_mask = stx.stx_dio_mem_align - 1; - offset_align_mask = stx.stx_dio_offset_align - 1; -#endif + const uint32_t dio_offset_align{file->dioOffsetAlign()}; + accept_io &= dio_offset_align && !(file_offset & (dio_offset_align - 1)); + accept_io &= dio_offset_align && !(size & (dio_offset_align - 1)); - accept_io &= !(size & offset_align_mask); - accept_io &= !(file_offset & static_cast(offset_align_mask)); - auto buffer_address{reinterpret_cast(buffer->getBuffer())}; - accept_io &= !((buffer_address + buffer_offset) & static_cast(mem_align_mask)); + const uint32_t dio_mem_align{file->dioMemAlign()}; + const auto mem_addr{reinterpret_cast(buffer->getBuffer()) + buffer_offset}; + accept_io &= dio_mem_align && !(mem_addr & (dio_mem_align - 1)); return accept_io ? 100 : -1; } @@ -174,7 +174,7 @@ Fastpath::_io_impl(IoType type, shared_ptr file, shared_ptr buff hipAmdFileHandle_t handle{}; size_t nbytes{}; - handle.fd = file->getUnbufferedFd().value(); + handle.fd = file->unbufferedFd().value(); if (!paramsValid(buffer, size, file_offset, buffer_offset)) { throw std::invalid_argument("The selected file or buffer region is invalid"); diff --git a/src/amd_detail/batch/batch.cpp b/src/amd_detail/batch/batch.cpp index 1c228735..9b4eaa12 100644 --- a/src/amd_detail/batch/batch.cpp +++ b/src/amd_detail/batch/batch.cpp @@ -56,7 +56,7 @@ BatchOperation::BatchOperation(std::unique_ptr params, } // Check File parameters - if (io_params->fh != file->getHandle()) { + if (io_params->fh != file->handle()) { throw std::invalid_argument("File does not match handle specified in io_params."); } if (io_params->u.batch.file_offset < 0) { diff --git a/src/amd_detail/configuration.cpp b/src/amd_detail/configuration.cpp index 97b88e19..427e8622 100644 --- a/src/amd_detail/configuration.cpp +++ b/src/amd_detail/configuration.cpp @@ -46,3 +46,10 @@ Configuration::statsLevel() const noexcept HIPFILE_STATIC unsigned int stats_level_env{Environment::stats_level().value_or(0)}; return stats_level_env; } + +bool +Configuration::unsupportedFileSystems() const noexcept +{ + HIPFILE_STATIC bool unsupported_file_systems_env{Environment::unsupported_file_systems().value_or(false)}; + return unsupported_file_systems_env; +} diff --git a/src/amd_detail/configuration.h b/src/amd_detail/configuration.h index 8648a62d..c8a7eab1 100644 --- a/src/amd_detail/configuration.h +++ b/src/amd_detail/configuration.h @@ -37,6 +37,11 @@ class Configuration { /// @brief Shows the level of detail for stats collection /// @return 0 if stats collection disabled, higher levels of detail as value increases virtual unsigned int statsLevel() const noexcept; + + /// @brief Checks if unsupported file systems are allowed in the fastpath backend + /// @return true if unsupported file systems are allowed, false if only supported file systems are + /// permitted (default) + virtual bool unsupportedFileSystems() const noexcept; }; } diff --git a/src/amd_detail/environment.cpp b/src/amd_detail/environment.cpp index 1f6d15ed..efbf1608 100644 --- a/src/amd_detail/environment.cpp +++ b/src/amd_detail/environment.cpp @@ -64,6 +64,12 @@ Environment::force_compat_mode() return Environment::get(Environment::FORCE_COMPAT_MODE); } +optional +Environment::unsupported_file_systems() +{ + return Environment::get(Environment::UNSUPPORTED_FILE_SYSTEMS); +} + optional Environment::stats_level() { diff --git a/src/amd_detail/environment.h b/src/amd_detail/environment.h index dc6f63d9..374b85ea 100644 --- a/src/amd_detail/environment.h +++ b/src/amd_detail/environment.h @@ -47,6 +47,19 @@ class Environment { static constexpr const char *const STATS_LEVEL{"HIPFILE_STATS_LEVEL"}; static std::optional stats_level(); + + /// @brief Allow unsupported file systems in the fastpath backend + /// + /// If enabled, the fastpath backend will allow I/O on file systems other than + /// ext4 (with ordered journaling) and xfs. If disabled (default), only supported + /// file systems are permitted. + static constexpr const char *const UNSUPPORTED_FILE_SYSTEMS{"HIPFILE_UNSUPPORTED_FILE_SYSTEMS"}; + + /// @brief Get the value of HIPFILE_UNSUPPORTED_FILE_SYSTEMS from the environment + /// @return An optional boolean value if HIPFILE_UNSUPPORTED_FILE_SYSTEMS was set, + /// nullopt if HIPFILE_UNSUPPORTED_FILE_SYSTEMS was unset or had a value other than + /// true or false. + static std::optional unsupported_file_systems(); }; } diff --git a/src/amd_detail/file.cpp b/src/amd_detail/file.cpp index f2c3f452..b66985f8 100644 --- a/src/amd_detail/file.cpp +++ b/src/amd_detail/file.cpp @@ -16,6 +16,7 @@ #include #include #include +#include using namespace std; @@ -31,7 +32,13 @@ UnregisteredFile::UnregisteredFile(int fd) #endif )}, flags{Context::get()->fcntl(fd, F_GETFL, 0)}, - mountinfo{Context::get()->getMountInfo(makedev(stx.stx_dev_major, stx.stx_dev_minor))} + mountinfo{Context::get()->getMountInfo(makedev(stx.stx_dev_major, stx.stx_dev_minor))}, +#if defined(STATX_DIOALIGN) + m_dio_mem_align{stx.stx_mask & STATX_DIOALIGN ? stx.stx_dio_mem_align : 4096}, + m_dio_offset_align{stx.stx_mask & STATX_DIOALIGN ? stx.stx_dio_offset_align : 4096} +#else + m_dio_mem_align{4096}, m_dio_offset_align{4096} +#endif { std::string path = "/proc/self/fd/" + std::to_string(fd); @@ -50,60 +57,86 @@ UnregisteredFile::UnregisteredFile(int fd) if (e.code().value() != EINVAL) { throw; } - unbuffered_fd = nullopt; + unbuffered_fd = nullopt; + m_dio_mem_align = 0; + m_dio_offset_align = 0; } } } hipFileHandle_t -IFile::getHandle() const +IFile::handle() const noexcept { return reinterpret_cast(const_cast(this)); } File::File(UnregisteredFile &&uf, const PassKey &) - : client_fd{std::move(uf.client_fd)}, buffered_fd{std::move(uf.buffered_fd)}, - unbuffered_fd{std::move(uf.unbuffered_fd)}, stx{uf.stx}, status_flags{uf.flags}, mountinfo{uf.mountinfo} + : m_client_fd{std::move(uf.client_fd)}, m_buffered_fd{std::move(uf.buffered_fd)}, + m_unbuffered_fd{std::move(uf.unbuffered_fd)}, m_dio_mem_align{uf.m_dio_mem_align}, + m_dio_offset_align{uf.m_dio_offset_align}, + m_is_block_device{(uf.stx.stx_mask & STATX_TYPE) && S_ISBLK(uf.stx.stx_mode)}, + m_is_regular_file{(uf.stx.stx_mask & STATX_TYPE) && S_ISREG(uf.stx.stx_mode)}, + m_on_ext4_ordered{uf.mountinfo && uf.mountinfo->type == FilesystemType::ext4 && + uf.mountinfo->options.ext4.journaling_mode == ExtJournalingMode::ordered}, + m_on_xfs{uf.mountinfo && uf.mountinfo->type == FilesystemType::xfs} { } int -File::getClientFd() const +File::clientFd() const noexcept { - return client_fd.get(); + return m_client_fd.get(); } int -File::getBufferedFd() const +File::bufferedFd() const noexcept { - return buffered_fd.get(); + return m_buffered_fd.get(); } optional -File::getUnbufferedFd() const +File::unbufferedFd() const noexcept { - if (unbuffered_fd) { - return unbuffered_fd.value().get(); + if (m_unbuffered_fd) { + return m_unbuffered_fd.value().get(); } return nullopt; } -const struct statx & -File::getStatx() const noexcept +uint32_t +File::dioMemAlign() const noexcept { - return stx; + return m_dio_mem_align; } -int -File::getStatusFlags() const +uint32_t +File::dioOffsetAlign() const noexcept +{ + return m_dio_offset_align; +} + +bool +File::isBlockDevice() const noexcept +{ + return m_is_block_device; +} + +bool +File::isRegularFile() const noexcept +{ + return m_is_regular_file; +} + +bool +File::onExt4Ordered() const noexcept { - return status_flags; + return m_on_ext4_ordered; } -optional -File::getMountInfo() const +bool +File::onXfs() const noexcept { - return mountinfo; + return m_on_xfs; } shared_ptr @@ -124,11 +157,11 @@ FileMap::registerFile(UnregisteredFile &&uf) throw FileAlreadyRegistered(); } - auto file = std::shared_ptr(new File(std::move(uf), PassKey{})); - from_fd[file->getClientFd()] = file; - from_fh[file->getHandle()] = file; + auto file = std::shared_ptr(new File(std::move(uf), PassKey{})); + from_fd[file->clientFd()] = file; + from_fh[file->handle()] = file; - return file->getHandle(); + return file->handle(); } void @@ -144,7 +177,7 @@ FileMap::deregisterFile(hipFileHandle_t fh) throw FileOperationsOutstanding(); } - from_fd.erase(itr->second->getClientFd()); + from_fd.erase(itr->second->clientFd()); from_fh.erase(fh); } diff --git a/src/amd_detail/file.h b/src/amd_detail/file.h index eaafdb17..07b48514 100644 --- a/src/amd_detail/file.h +++ b/src/amd_detail/file.h @@ -67,6 +67,15 @@ class UnregisteredFile { /// @brief Information obtained from /proc/self/mountinfo std::optional mountinfo; + + /// @brief Memory alignment (in bytes) requirement for direct IO. If the file does not support direct IO, + /// this will be 0. If alignment information is not available from statx, this will be set to 4096. + uint32_t m_dio_mem_align; + + /// @brief Alignment (in bytes) required for file offsets and IO segment lengths for direct IO. If the + /// file does not support direct IO, this will be 0. If alignment information is not available from statx, + /// this will be set to 4096. + uint32_t m_dio_offset_align; }; class IFile { @@ -75,14 +84,17 @@ class IFile { /// @brief Get the handle for this file /// @return The handle for this file - virtual hipFileHandle_t getHandle() const; - - virtual int getClientFd() const = 0; - virtual int getBufferedFd() const = 0; - virtual std::optional getUnbufferedFd() const = 0; - virtual const struct statx &getStatx() const noexcept = 0; - virtual int getStatusFlags() const = 0; - virtual std::optional getMountInfo() const = 0; + virtual hipFileHandle_t handle() const noexcept; + + virtual int clientFd() const noexcept = 0; + virtual int bufferedFd() const noexcept = 0; + virtual std::optional unbufferedFd() const noexcept = 0; + virtual uint32_t dioMemAlign() const noexcept = 0; + virtual uint32_t dioOffsetAlign() const noexcept = 0; + virtual bool isBlockDevice() const noexcept = 0; + virtual bool isRegularFile() const noexcept = 0; + virtual bool onExt4Ordered() const noexcept = 0; + virtual bool onXfs() const noexcept = 0; }; class FileMap; @@ -100,12 +112,47 @@ class File : public IFile { File(File &&) = delete; File &operator=(File &&) = delete; - virtual int getClientFd() const override; - virtual int getBufferedFd() const override; - virtual std::optional getUnbufferedFd() const override; - virtual const struct statx &getStatx() const noexcept override; - virtual int getStatusFlags() const override; - virtual std::optional getMountInfo() const override; + virtual int clientFd() const noexcept override; + + /// @brief Get the buffered file descriptor (!O_DIRECT) + /// @return The buffered file descriptor + virtual int bufferedFd() const noexcept override; + + /// @brief Get the unbuffered file descriptor for this file. Returns nullopt if the file does not support + /// direct IO or if there was an error obtaining the unbuffered fd. + /// @return The unbuffered file descriptor, or nullopt if not available + virtual std::optional unbufferedFd() const noexcept override; + + /// @brief Get the memory (in bytes) alignment requirement for direct IO on this file. If the file does + /// not support direct IO, this will return 0. + /// @return The memory alignment (in bytes) requirement for direct IO, 0 if direct IO is not supported + virtual uint32_t dioMemAlign() const noexcept override; + + /// @brief The alignment (in bytes) required for file offsets and I/O segment lengths for direct I/O + /// (O_DIRECT) on this file, or 0 if direct I/O is not supported on this file + /// @return The alignment (in bytes) required for file offsets and I/O segment lengths for direct I/O, 0 + /// if direct I/O is not supported + virtual uint32_t dioOffsetAlign() const noexcept override; + + /// @brief Whether this file is a block device. If statx did not return type information, this will return + /// false. + /// @return True if the file is a block device, false otherwise + virtual bool isBlockDevice() const noexcept override; + + /// @brief Whether this file is a regular file. If statx did not return type information, this will return + /// false. + /// @return True if the file is a regular file, false otherwise + virtual bool isRegularFile() const noexcept override; + + /// @brief Whether this file is on an ext4 filesystem with ordered journaling mode. If mountinfo is not + /// available, this will return false. + /// @return True if the file is on an ext4 filesystem with ordered journaling mode, false otherwise + virtual bool onExt4Ordered() const noexcept override; + + /// @brief Whether this file is on an xfs filesystem. If mountinfo is not available, this will return + /// false. + /// @return True if the file is on an xfs filesystem, false otherwise + virtual bool onXfs() const noexcept override; /// @brief Construct a registered file /// @param uf An unregistered file @@ -114,24 +161,33 @@ class File : public IFile { private: /// @brief The file descriptor provided by the client - FileDescriptor client_fd; + FileDescriptor m_client_fd; /// @brief Buffered file descriptor (!O_DIRECT) - FileDescriptor buffered_fd; + FileDescriptor m_buffered_fd; /// @brief Unbuffered file descriptor (O_DIRECT) - std::optional unbuffered_fd; + std::optional m_unbuffered_fd; - /// @brief File status information obtained from statx (2) - struct statx stx; + /// @brief Memory alignment (in bytes) requirement for direct IO. If the file does not support direct IO, + /// this will be 0. + uint32_t m_dio_mem_align; - /// @brief The file's status flags. See fcntl(2) - /// - /// Used to determine if the O_DIRECT flag is set - int status_flags; + /// @brief Alignment (in bytes) required for file offsets and I/O segment lengths for direct I/O + /// (O_DIRECT). + uint32_t m_dio_offset_align; - /// @brief Mount information for the filesystem backing fd - std::optional mountinfo; + /// @brief Whether statx reported that the file is a block device + bool m_is_block_device; + + /// @brief Whether statx reported that the file is a regular file + bool m_is_regular_file; + + /// @brief Whether the file is on an ext4 filesystem with ordered journaling mode + bool m_on_ext4_ordered; + + /// @brief Whether the file is on an xfs filesystem + bool m_on_xfs; }; class FileMap { diff --git a/test/amd_detail/async.cpp b/test/amd_detail/async.cpp index f1801c6c..5749b5c9 100644 --- a/test/amd_detail/async.cpp +++ b/test/amd_detail/async.cpp @@ -665,7 +665,7 @@ TEST_P(AsyncIoOpWithParams, cpuIOReturnsFullSize) else { EXPECT_CALL(msys, pwrite).WillOnce(Return(size)); } - EXPECT_CALL(*mfile, getBufferedFd); + EXPECT_CALL(*mfile, bufferedFd); async_io_cpu_copy(op.get()); ASSERT_EQ(op->bytes_transferred_internal, size); } @@ -678,7 +678,7 @@ TEST_P(AsyncIoOpWithParams, cpuCopyReadPreadPwriteErrorReturnsError) else { EXPECT_CALL(msys, pwrite).WillOnce(Throw(std::system_error(3, std::generic_category()))); } - EXPECT_CALL(*mfile, getBufferedFd); + EXPECT_CALL(*mfile, bufferedFd); async_io_cpu_copy(op.get()); ASSERT_EQ(op->bytes_transferred_internal, -1); } @@ -695,7 +695,7 @@ TEST_P(AsyncIoOpWithParams, cpuCopyReadPreadPwriteRetriesOnEINTR) .WillOnce(Throw(std::system_error(EINTR, std::generic_category()))) .WillOnce(Return(size)); } - EXPECT_CALL(*mfile, getBufferedFd).Times(2); + EXPECT_CALL(*mfile, bufferedFd).Times(2); async_io_cpu_copy(op.get()); ASSERT_EQ(op->bytes_transferred_internal, size); diff --git a/test/amd_detail/batch/batch.cpp b/test/amd_detail/batch/batch.cpp index 1c1c0044..ef0c1494 100644 --- a/test/amd_detail/batch/batch.cpp +++ b/test/amd_detail/batch/batch.cpp @@ -47,7 +47,7 @@ struct HipFileBatch : public HipFileUnopened { EXPECT_CALL(*default_mock_buffer, getLength).WillRepeatedly(Return(1)); default_mock_file = std::make_shared>(); - EXPECT_CALL(*default_mock_file, getHandle).WillRepeatedly(Return(file_handle)); + EXPECT_CALL(*default_mock_file, handle).WillRepeatedly(Return(file_handle)); io_params = std::make_unique(); io_params->u.batch.devPtr_base = const_cast(buffer_pointer); @@ -86,8 +86,7 @@ TEST_F(HipFileBatch, CreateOperationBadBuffer) TEST_F(HipFileBatch, CreateOperationBadFileHandle) { - EXPECT_CALL(*default_mock_file, getHandle) - .WillOnce(Return(reinterpret_cast(0xFACEFEED))); + EXPECT_CALL(*default_mock_file, handle).WillOnce(Return(reinterpret_cast(0xFACEFEED))); EXPECT_THROW(BatchOperation(std::move(io_params), default_mock_buffer, default_mock_file), std::invalid_argument); } @@ -242,13 +241,13 @@ struct HipFileBatchContext : public HipFileUnopened { EXPECT_CALL(*default_mock_buffer, getLength).WillRepeatedly(Return(default_mock_buffer_length)); default_mock_file = std::make_shared>(); - EXPECT_CALL(*default_mock_file, getHandle).WillRepeatedly(Return(default_mock_file.get())); + EXPECT_CALL(*default_mock_file, handle).WillRepeatedly(Return(default_mock_file.get())); file_buffer_pair default_fb_pair = {default_mock_file, default_mock_buffer}; io_params.u.batch.devPtr_base = default_mock_buffer->getBuffer(); io_params.u.batch.size = 1; - io_params.fh = default_mock_file->getHandle(); + io_params.fh = default_mock_file->handle(); io_params.mode = hipFileBatch; io_params.opcode = hipFileBatchRead; diff --git a/test/amd_detail/configuration.cpp b/test/amd_detail/configuration.cpp index e465f99f..3a719940 100644 --- a/test/amd_detail/configuration.cpp +++ b/test/amd_detail/configuration.cpp @@ -48,6 +48,12 @@ struct HipFileConfiguration : public Test { EXPECT_CALL(msys, getenv(StrEq(hipFile::Environment::STATS_LEVEL))) .WillOnce(Return(const_cast(hipfile_stats_level))); } + + void expect_configuration_unsupported_file_systems(const char *hipfile_unsupported_file_systems) + { + EXPECT_CALL(msys, getenv(StrEq(hipFile::Environment::UNSUPPORTED_FILE_SYSTEMS))) + .WillOnce(Return(const_cast(hipfile_unsupported_file_systems))); + } }; TEST_F(HipFileConfiguration, FastpathEnabledIfForceCompatModeEnvironmentVariableIsNotSet) @@ -194,4 +200,28 @@ TEST_F(HipFileConfiguration, StatsLevelEnvironmentVariableIsSet) ASSERT_EQ(1, Configuration().statsLevel()); } +TEST_F(HipFileConfiguration, UnsupportedFilesystemsDisabledIfEnvironmentVariableIsNotSet) +{ + expect_configuration_unsupported_file_systems(nullptr); + ASSERT_FALSE(Configuration().unsupportedFileSystems()); +} + +TEST_F(HipFileConfiguration, UnsupportedFilesystemsDisabledIfEnvironmentVariableIsInvalid) +{ + expect_configuration_unsupported_file_systems("not-a-bool"); + ASSERT_FALSE(Configuration().unsupportedFileSystems()); +} + +TEST_F(HipFileConfiguration, UnsupportedFilesystemsEnabledIfEnvironmentVariableIsTrue) +{ + expect_configuration_unsupported_file_systems("true"); + ASSERT_TRUE(Configuration().unsupportedFileSystems()); +} + +TEST_F(HipFileConfiguration, UnsupportedFilesystemsDisabledIfEnvironmentVariableIsFalse) +{ + expect_configuration_unsupported_file_systems("false"); + ASSERT_FALSE(Configuration().unsupportedFileSystems()); +} + HIPFILE_WARN_NO_GLOBAL_CTOR_ON diff --git a/test/amd_detail/fastpath.cpp b/test/amd_detail/fastpath.cpp index d02aa31c..31a1e659 100644 --- a/test/amd_detail/fastpath.cpp +++ b/test/amd_detail/fastpath.cpp @@ -42,11 +42,11 @@ static int SCORE_ACCEPT{100}; static const int SCORE_REJECT{-1}; #if defined(STATX_DIOALIGN) -static uint32_t DEFAULT_MEM_ALIGN{4}; -static uint32_t DEFAULT_OFFSET_ALIGN{512}; +static const uint32_t DEFAULT_MEM_ALIGN{4}; +static const uint32_t DEFAULT_OFFSET_ALIGN{512}; #else -static uint32_t DEFAULT_MEM_ALIGN{4096}; -static uint32_t DEFAULT_OFFSET_ALIGN{4096}; +static const uint32_t DEFAULT_MEM_ALIGN{4096}; +static const uint32_t DEFAULT_OFFSET_ALIGN{4096}; #endif namespace hipFile { @@ -58,26 +58,22 @@ operator==(const hipAmdFileHandle_t &lhs, const hipAmdFileHandle_t &rhs) } // Provide default values for variables used in fastpath tests -struct FastpathTestBase { - const bool DEFAULT_ENABLE{true}; - const size_t DEFAULT_IO_SIZE{1024 * 1024}; - void *const DEFAULT_BUFFER_ADDR{reinterpret_cast(0xABAD'CAFE'0000'0000)}; - const off_t DEFAULT_BUFFER_OFFSET{DEFAULT_MEM_ALIGN}; - const size_t DEFAULT_BUFFER_LENGTH{DEFAULT_IO_SIZE + static_cast(DEFAULT_BUFFER_OFFSET)}; - const hipMemoryType DEFAULT_BUFFER_TYPE{hipMemoryTypeDevice}; - const optional DEFAULT_UNBUFFERED_FD{7}; - const off_t DEFAULT_FILE_OFFSET{8192}; - const struct statx DEFAULT_STATX { - []() { - struct statx stx {}; -#if defined(STATX_DIOALIGN) - stx.stx_mask = STATX_DIOALIGN; - stx.stx_dio_mem_align = DEFAULT_MEM_ALIGN; - stx.stx_dio_offset_align = DEFAULT_OFFSET_ALIGN; -#endif - return stx; - }() - }; +class FastpathTestBase { +public: + static constexpr bool DEFAULT_ENABLE{true}; + static constexpr size_t DEFAULT_IO_SIZE{1024 * 1024}; + static constexpr uintptr_t DEFAULT_BUFFER_ADDR{0xABAD'CAFE'0000'0000}; + static constexpr off_t DEFAULT_BUFFER_OFFSET{DEFAULT_MEM_ALIGN}; + static constexpr size_t DEFAULT_BUFFER_LENGTH{DEFAULT_IO_SIZE + + static_cast(DEFAULT_BUFFER_OFFSET)}; + static constexpr hipMemoryType DEFAULT_BUFFER_TYPE{hipMemoryTypeDevice}; + static constexpr optional DEFAULT_UNBUFFERED_FD{7}; + static constexpr off_t DEFAULT_FILE_OFFSET{8192}; + static constexpr bool DEFAULT_IS_REGULAR_FILE{true}; + static constexpr bool DEFAULT_IS_BLOCK_DEVICE{false}; + static constexpr bool DEFAULT_ON_EXT4_ORDERED{true}; + static constexpr bool DEFAULT_ON_XFS{false}; + static constexpr bool DEFAULT_UNSUPPORTED_FILE_SYSTEMS{false}; // Buffer and file mocks used to setup expectations shared_ptr> mfile{make_shared>()}; @@ -86,6 +82,122 @@ struct FastpathTestBase { StrictMock mcfg{}; }; +class FastpathScoreExpectations; + +class FastpathScoreExpectationsBuilder { +public: + StrictMock &m_mcfg; + shared_ptr> m_mfile; + shared_ptr> m_mbuffer; + + optional m_fastpath_enabled; + optional m_buffer_addr; + optional m_buffer_type; + optional> m_unbuffered_fd; + optional m_is_regular_file; + optional m_is_block_device; + optional m_on_ext4_ordered; + optional m_on_xfs; + optional m_unsupported_file_systems; + + FastpathScoreExpectationsBuilder(StrictMock &mcfg, shared_ptr> mfile, + shared_ptr> mbuffer) + : m_mcfg(mcfg), m_mfile(mfile), m_mbuffer(mbuffer) + { + } + + FastpathScoreExpectationsBuilder &fastpathEnabled(bool enabled) + { + m_fastpath_enabled = enabled; + return *this; + } + + FastpathScoreExpectationsBuilder &bufferAddr(void *addr) + { + m_buffer_addr = addr; + return *this; + } + + FastpathScoreExpectationsBuilder &bufferType(hipMemoryType type) + { + m_buffer_type = type; + return *this; + } + + FastpathScoreExpectationsBuilder &unbufferedFd(optional fd) + { + m_unbuffered_fd = fd; + return *this; + } + + FastpathScoreExpectationsBuilder &isRegularFile(bool is_regular_file) + { + m_is_regular_file = is_regular_file; + return *this; + } + + FastpathScoreExpectationsBuilder &isBlockDevice(bool is_block_device) + { + m_is_block_device = is_block_device; + return *this; + } + + FastpathScoreExpectationsBuilder &onExt4Ordered(bool on_ext4_ordered) + { + m_on_ext4_ordered = on_ext4_ordered; + return *this; + } + + FastpathScoreExpectationsBuilder &onXfs(bool on_xfs) + { + m_on_xfs = on_xfs; + return *this; + } + + FastpathScoreExpectationsBuilder &unsupportedFileSystems(bool unsupported_file_systems) + { + m_unsupported_file_systems = unsupported_file_systems; + return *this; + } + + FastpathScoreExpectations build(); +}; + +class FastpathScoreExpectations { +public: + FastpathScoreExpectations(const FastpathScoreExpectationsBuilder &builder) + { + EXPECT_CALL(builder.m_mcfg, fastpath()) + .WillOnce(Return(builder.m_fastpath_enabled.value_or(FastpathTestBase::DEFAULT_ENABLE))); + EXPECT_CALL(*builder.m_mfile, unbufferedFd) + .WillOnce(Return(builder.m_unbuffered_fd.value_or(FastpathTestBase::DEFAULT_UNBUFFERED_FD))); + EXPECT_CALL(*builder.m_mbuffer, getType) + .WillOnce(Return(builder.m_buffer_type.value_or(FastpathTestBase::DEFAULT_BUFFER_TYPE))); + EXPECT_CALL(*builder.m_mfile, isRegularFile) + .WillOnce(Return(builder.m_is_regular_file.value_or(FastpathTestBase::DEFAULT_IS_REGULAR_FILE))); + EXPECT_CALL(*builder.m_mfile, onExt4Ordered) + .WillOnce(Return(builder.m_on_ext4_ordered.value_or(FastpathTestBase::DEFAULT_ON_EXT4_ORDERED))); + EXPECT_CALL(*builder.m_mfile, onXfs) + .WillOnce(Return(builder.m_on_xfs.value_or(FastpathTestBase::DEFAULT_ON_XFS))); + EXPECT_CALL(builder.m_mcfg, unsupportedFileSystems) + .WillOnce(Return(builder.m_unsupported_file_systems.value_or( + FastpathTestBase::DEFAULT_UNSUPPORTED_FILE_SYSTEMS))); + EXPECT_CALL(*builder.m_mfile, isBlockDevice) + .WillOnce(Return(builder.m_is_block_device.value_or(FastpathTestBase::DEFAULT_IS_BLOCK_DEVICE))); + EXPECT_CALL(*builder.m_mfile, dioOffsetAlign).WillOnce(Return(DEFAULT_OFFSET_ALIGN)); + EXPECT_CALL(*builder.m_mbuffer, getBuffer) + .WillOnce(Return(builder.m_buffer_addr.value_or( + reinterpret_cast(FastpathTestBase::DEFAULT_BUFFER_ADDR)))); + EXPECT_CALL(*builder.m_mfile, dioMemAlign).WillOnce(Return(DEFAULT_MEM_ALIGN)); + } +}; + +FastpathScoreExpectations +FastpathScoreExpectationsBuilder::build() +{ + return FastpathScoreExpectations(*this); +} + struct FastpathTest : public FastpathTestBase, public Test {}; TEST_F(FastpathTest, TestDefaults) @@ -95,21 +207,19 @@ TEST_F(FastpathTest, TestDefaults) ASSERT_TRUE(DEFAULT_MEM_ALIGN > 1); ASSERT_FALSE((DEFAULT_OFFSET_ALIGN & (DEFAULT_OFFSET_ALIGN - 1))); ASSERT_TRUE(DEFAULT_OFFSET_ALIGN > 1); - ASSERT_FALSE((reinterpret_cast(DEFAULT_BUFFER_ADDR) & (DEFAULT_MEM_ALIGN - 1))); + ASSERT_FALSE((DEFAULT_BUFFER_ADDR & (DEFAULT_MEM_ALIGN - 1))); ASSERT_FALSE((DEFAULT_BUFFER_OFFSET & (DEFAULT_MEM_ALIGN - 1))); ASSERT_FALSE((DEFAULT_IO_SIZE & (DEFAULT_OFFSET_ALIGN - 1))); ASSERT_FALSE((DEFAULT_FILE_OFFSET & (DEFAULT_OFFSET_ALIGN - 1))); + ASSERT_TRUE(DEFAULT_IS_REGULAR_FILE); + ASSERT_FALSE(DEFAULT_IS_BLOCK_DEVICE); + ASSERT_TRUE(DEFAULT_ON_EXT4_ORDERED); + ASSERT_FALSE(DEFAULT_ON_XFS); } TEST_F(FastpathTest, ScoreAcceptsIoWithDefaults) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_ACCEPT); @@ -117,13 +227,7 @@ TEST_F(FastpathTest, ScoreAcceptsIoWithDefaults) TEST_F(FastpathTest, ScoreRejectsIoIfFastpathIsDisabled) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(false)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).fastpathEnabled(false).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_REJECT); @@ -131,13 +235,7 @@ TEST_F(FastpathTest, ScoreRejectsIoIfFastpathIsDisabled) TEST_F(FastpathTest, ScoreRejectsIoIfUnbufferedFdNotAvailable) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(nullopt)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).unbufferedFd(nullopt).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_REJECT); @@ -145,13 +243,7 @@ TEST_F(FastpathTest, ScoreRejectsIoIfUnbufferedFdNotAvailable) TEST_F(FastpathTest, ScoreRejectsIoWithNegativeAlignedFileOffset) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, -static_cast(DEFAULT_OFFSET_ALIGN), DEFAULT_BUFFER_OFFSET), @@ -160,13 +252,7 @@ TEST_F(FastpathTest, ScoreRejectsIoWithNegativeAlignedFileOffset) TEST_F(FastpathTest, ScoreRejectsIoWithNegativeAlignedBufferOffset) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, -static_cast(DEFAULT_MEM_ALIGN)), @@ -175,34 +261,79 @@ TEST_F(FastpathTest, ScoreRejectsIoWithNegativeAlignedBufferOffset) TEST_F(FastpathTest, ScoreRejectsIoIfBufferAddressPlusBufferOffsetIsUnaligned) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif // The DEFAULT_BUFFER_ADDR is DEFAULT_MEM_ALIGN aligned. Ensure that this // test's buffer is not DEFAULT_MEM_ALIGN aligned. - EXPECT_CALL(*mbuffer, getBuffer) - .WillOnce(Return(reinterpret_cast(reinterpret_cast(DEFAULT_BUFFER_ADDR) + - (DEFAULT_MEM_ALIGN >> 1)))); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer) + .bufferAddr(reinterpret_cast(DEFAULT_BUFFER_ADDR + (DEFAULT_MEM_ALIGN >> 1))) + .build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, static_cast(DEFAULT_MEM_ALIGN)), SCORE_REJECT); } +TEST_F(FastpathTest, ScoreAcceptsIoIfFileIsRegularAndOnExt4Ordered) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).isRegularFile(true).onExt4Ordered(true).build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_ACCEPT); +} + +TEST_F(FastpathTest, ScoreAcceptsIoIfFileIsRegularAndOnXfs) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).isRegularFile(true).onXfs(true).build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_ACCEPT); +} + +TEST_F(FastpathTest, ScoreRejectsIoIfFileIsRegularAndNotOnExt4OrderedNorXfs) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer) + .isRegularFile(true) + .onExt4Ordered(false) + .onXfs(false) + .build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_REJECT); +} + +TEST_F(FastpathTest, ScoreAcceptsIoIfFileIsRegularNotOnExt4OrderedNorXfsIfUnsupportedFileSystemsIsTrue) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer) + .isRegularFile(true) + .onExt4Ordered(false) + .onXfs(false) + .unsupportedFileSystems(true) + .build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_ACCEPT); +} + +TEST_F(FastpathTest, ScoreAcceptsIoIfFileIsBlockDevice) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).isRegularFile(false).isBlockDevice(true).build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_ACCEPT); +} + +TEST_F(FastpathTest, ScoreRejectsIoIfFileIsNotRegularFileOrBlockDevice) +{ + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).isRegularFile(false).isBlockDevice(false).build(); + + ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), + SCORE_REJECT); +} + struct FastpathSupportedHipMemoryParam : public FastpathTestBase, public TestWithParam {}; TEST_P(FastpathSupportedHipMemoryParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(GetParam())); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).bufferType(GetParam()).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_ACCEPT); @@ -214,13 +345,7 @@ struct FastpathUnsupportedHipMemoryParam : public FastpathTestBase, public TestW TEST_P(FastpathUnsupportedHipMemoryParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(GetParam())); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).bufferType(GetParam()).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_REJECT); @@ -233,13 +358,7 @@ struct FastpathAlignedIoSizesParam : public FastpathTestBase, public TestWithPar TEST_P(FastpathAlignedIoSizesParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, GetParam(), DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_ACCEPT); @@ -253,13 +372,7 @@ struct FastpathUnalignedIoSizesParam : public FastpathTestBase, public TestWithP TEST_P(FastpathUnalignedIoSizesParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, GetParam(), DEFAULT_FILE_OFFSET, DEFAULT_BUFFER_OFFSET), SCORE_REJECT); @@ -273,13 +386,7 @@ struct FastpathAlignedFileOffsetsParam : public FastpathTestBase, public TestWit TEST_P(FastpathAlignedFileOffsetsParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, GetParam(), DEFAULT_BUFFER_OFFSET), GetParam() >= 0 ? SCORE_ACCEPT : SCORE_REJECT); @@ -295,13 +402,7 @@ struct FastpathUnalignedFileOffsetsParam : public FastpathTestBase, public TestW TEST_P(FastpathUnalignedFileOffsetsParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, GetParam(), DEFAULT_BUFFER_OFFSET), SCORE_REJECT); @@ -319,13 +420,7 @@ struct FastpathAlignedBufferOffsetsParam : public FastpathTestBase, public TestW TEST_P(FastpathAlignedBufferOffsetsParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, GetParam()), GetParam() >= 0 ? SCORE_ACCEPT : SCORE_REJECT); @@ -342,13 +437,7 @@ struct FastpathUnalignedBufferOffsetsParam : public FastpathTestBase, public Tes TEST_P(FastpathUnalignedBufferOffsetsParam, Score) { - EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); - EXPECT_CALL(*mbuffer, getType).WillOnce(Return(DEFAULT_BUFFER_TYPE)); -#if defined(STATX_DIOALIGN) - EXPECT_CALL(*mfile, getStatx).WillOnce(ReturnRef(DEFAULT_STATX)); -#endif - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + FastpathScoreExpectationsBuilder(mcfg, mfile, mbuffer).build(); ASSERT_EQ(Fastpath().score(mfile, mbuffer, DEFAULT_IO_SIZE, DEFAULT_FILE_OFFSET, GetParam()), SCORE_REJECT); @@ -370,9 +459,9 @@ struct FastpathIoParam : public FastpathTestBase, public TestWithParam { void expect_validate() { EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); } // Setup expectations on the mocks called to validate IO arguments and @@ -389,7 +478,7 @@ struct FastpathIoParam : public FastpathTestBase, public TestWithParam { EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(DEFAULT_ENABLE)); EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(bufptr)); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(buflen)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(fd)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(fd)); } // Setup expectations on the mocks called to validate IO arguments and @@ -587,7 +676,7 @@ TEST_P(FastpathIoParam, IoSizeIsTruncatedToMaxRWCount) const size_t buffer_size{SIZE_MAX}; const size_t io_size{SIZE_MAX}; - expect_io(DEFAULT_UNBUFFERED_FD, DEFAULT_BUFFER_ADDR, buffer_size); + expect_io(DEFAULT_UNBUFFERED_FD, reinterpret_cast(DEFAULT_BUFFER_ADDR), buffer_size); switch (GetParam()) { case IoType::Read: EXPECT_CALL(mhip, hipAmdFileRead(_, _, MAX_RW_COUNT, _)).WillOnce(Return(MAX_RW_COUNT)); @@ -612,9 +701,9 @@ TEST_P(FastpathIoParam, IoWithFallbackThrowsAFallbackIneligibleException) EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(true)); EXPECT_CALL(mhip, hipInit).WillRepeatedly(Return()); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); switch (GetParam()) { case IoType::Read: @@ -641,9 +730,9 @@ TEST_P(FastpathIoParam, IoWithFallbackThrowsHipRuntimeException) EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(true)); EXPECT_CALL(mhip, hipInit).WillOnce(Return()); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); switch (GetParam()) { case IoType::Read: @@ -668,10 +757,10 @@ TEST_P(FastpathIoParam, IoThrowsAFallbackEligibleENODEV) backend->register_fallback_backend(m_fallback); EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(true)); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); EXPECT_CALL(mhip, hipInit).WillOnce(Return()); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); switch (GetParam()) { case IoType::Read: @@ -699,10 +788,10 @@ TEST_P(FastpathIoParam, IoThrowsAFallbackEligibleEREMOTEIO) backend->register_fallback_backend(m_fallback); EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(true)); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); EXPECT_CALL(mhip, hipInit).WillOnce(Return()); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); switch (GetParam()) { case IoType::Read: @@ -737,9 +826,9 @@ TEST_P(FastpathIoParam, FallbackRejectsIoRequest) EXPECT_CALL(mcfg, fastpath()).WillOnce(Return(true)); EXPECT_CALL(mhip, hipInit).WillRepeatedly(Return()); - EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(DEFAULT_BUFFER_ADDR)); + EXPECT_CALL(*mbuffer, getBuffer).WillOnce(Return(reinterpret_cast(DEFAULT_BUFFER_ADDR))); EXPECT_CALL(*mbuffer, getLength).WillOnce(Return(DEFAULT_BUFFER_LENGTH)); - EXPECT_CALL(*mfile, getUnbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); + EXPECT_CALL(*mfile, unbufferedFd).WillOnce(Return(DEFAULT_UNBUFFERED_FD)); EXPECT_CALL(*m_fallback, score).WillOnce(Return(SCORE_REJECT)); switch (GetParam()) { diff --git a/test/amd_detail/handle.cpp b/test/amd_detail/handle.cpp index a067a5c1..2dda8231 100644 --- a/test/amd_detail/handle.cpp +++ b/test/amd_detail/handle.cpp @@ -168,8 +168,15 @@ TEST_F(HipFileHandle, file_initialization) { int fd{0x12345678}; int fd_flags{~O_DIRECT}; // All flags, except O_DIRECT, set - struct statx stxbuf; - memset(&stxbuf, 0xA5, sizeof(stxbuf)); + struct statx stxbuf {}; +#if defined(STATX_DIOALIGN) + stxbuf.stx_mask = STATX_TYPE | STATX_MODE | STATX_DIOALIGN; + stxbuf.stx_dio_mem_align = 4; + stxbuf.stx_dio_offset_align = 512; +#else + stxbuf.stx_mask = STATX_TYPE | STATX_MODE; +#endif + stxbuf.stx_mode = S_IFREG; // In this test, the registered file is destroyed _after_ the mocks are // destroyed. Use an eventfd so that when FileDescriptor calls close, it @@ -190,16 +197,21 @@ TEST_F(HipFileHandle, file_initialization) auto fh{Context::get()->registerFile(fd)}; auto file{Context::get()->getFile(fh)}; - EXPECT_EQ(fh, file->getHandle()); - EXPECT_EQ(fd, file->getClientFd()); - EXPECT_EQ(fd, file->getBufferedFd()); - EXPECT_EQ(open_fd, file->getUnbufferedFd()); - auto file_stx{file->getStatx()}; - EXPECT_EQ(0, memcmp(&file_stx, &stxbuf, sizeof(stxbuf))); - EXPECT_EQ(fd_flags, file->getStatusFlags()); - EXPECT_EQ(mountinfo.type, file->getMountInfo().value().type); - EXPECT_EQ(mountinfo.options.ext4.journaling_mode, - file->getMountInfo().value().options.ext4.journaling_mode); + EXPECT_EQ(fh, file->handle()); + EXPECT_EQ(fd, file->clientFd()); + EXPECT_EQ(fd, file->bufferedFd()); + EXPECT_EQ(open_fd, file->unbufferedFd()); +#if defined(STATX_DIOALIGN) + EXPECT_EQ(file->dioMemAlign(), stxbuf.stx_dio_mem_align); + EXPECT_EQ(file->dioOffsetAlign(), stxbuf.stx_dio_offset_align); +#else + EXPECT_EQ(file->dioMemAlign(), 4096); + EXPECT_EQ(file->dioOffsetAlign(), 4096); +#endif + EXPECT_FALSE(file->isBlockDevice()); + EXPECT_TRUE(file->isRegularFile()); + EXPECT_TRUE(file->onExt4Ordered()); + EXPECT_FALSE(file->onXfs()); } TEST_F(HipFileHandle, register_handle_internal_linux_fd_already_registered) @@ -445,4 +457,343 @@ TEST_F(HipFileHandle, UnregisteredFileConstructorThrowsOnErrOtherThanEinval) ASSERT_THROW(UnregisteredFile{777777}, std::system_error); } +#if defined(STATX_DIOALIGN) +TEST_F(HipFileHandle, UnregisteredFileDioMemAlignMatchesStatxDioMemAlign) +{ + int open_fd{888888}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE | STATX_MODE | STATX_DIOALIGN; + stxbuf.stx_dio_mem_align = 1234; + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .open_fd(open_fd) + .statx(stxbuf) + .build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_mem_align, 1234); + + EXPECT_CALL(msys, close(open_fd)); +} +#endif + +#if defined(STATX_DIOALIGN) +TEST_F(HipFileHandle, UnregisteredFileDioOffsetAlignMatchesStatxDioOffsetAlign) +{ + int open_fd{888888}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE | STATX_MODE | STATX_DIOALIGN; + stxbuf.stx_dio_offset_align = 1234; + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .open_fd(open_fd) + .statx(stxbuf) + .build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_offset_align, 1234); + + EXPECT_CALL(msys, close(open_fd)); +} +#endif + +TEST_F(HipFileHandle, UnregisteredFileDioMemAlignIsPageSizeIfStatxDoesntHaveDioAlign) +{ + int open_fd{888888}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE | STATX_MODE; + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .open_fd(open_fd) + .statx(stxbuf) + .build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_mem_align, 4096); + + EXPECT_CALL(msys, close(open_fd)); +} + +TEST_F(HipFileHandle, UnregisteredFileDioOffsetAlignIsPageSizeIfStatxDoesntHaveDioAlign) +{ + int open_fd{888888}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE | STATX_MODE; + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .open_fd(open_fd) + .statx(stxbuf) + .build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_offset_align, 4096); + + EXPECT_CALL(msys, close(open_fd)); +} + +TEST_F(HipFileHandle, UnregisteredFileDioMemAlignIsZeroIfUnableToOpenUnbufferedFd) +{ + ExpectUnregisteredFileBuilder(msys, mlibmounthelper).fd_flags(~O_DIRECT).open_throws(EINVAL).build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_mem_align, 0); +} + +TEST_F(HipFileHandle, UnregisteredFileDioOffsetAlignIsZeroIfUnableToOpenUnbufferedFd) +{ + ExpectUnregisteredFileBuilder(msys, mlibmounthelper).fd_flags(~O_DIRECT).open_throws(EINVAL).build(); + UnregisteredFile uf{777777}; + ASSERT_EQ(uf.m_dio_offset_align, 0); +} + +TEST_F(HipFileHandle, IsBlockDeviceReturnsTrueForBlockDevice) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE; + stxbuf.stx_mode = S_IFBLK; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_TRUE(file->isBlockDevice()); +} + +TEST_F(HipFileHandle, IsBlockDeviceReturnsFalseForRegularFile) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE; + stxbuf.stx_mode = S_IFREG; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->isBlockDevice()); +} + +TEST_F(HipFileHandle, IsBlockDeviceReturnsFalseWhenStatxTypeNotAvailable) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = 0; + stxbuf.stx_mode = S_IFBLK; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->isBlockDevice()); +} + +TEST_F(HipFileHandle, IsRegularFileReturnsTrueForRegularFile) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE; + stxbuf.stx_mode = S_IFREG; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_TRUE(file->isRegularFile()); +} + +TEST_F(HipFileHandle, IsRegularFileReturnsFalseForBlockDevice) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = STATX_TYPE; + stxbuf.stx_mode = S_IFBLK; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->isRegularFile()); +} + +TEST_F(HipFileHandle, IsRegularFileReturnsFalseWhenStatxTypeNotAvailable) +{ + int fd{0xBADF00D}; + struct statx stxbuf {}; + stxbuf.stx_mask = 0; + stxbuf.stx_mode = S_IFBLK; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .statx(stxbuf) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->isRegularFile()); +} + +TEST_F(HipFileHandle, OnExt4OrderedReturnsTrueForExt4Ordered) +{ + int fd{0xBADF00D}; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + MountInfo mountinfo; + mountinfo.type = FilesystemType::ext4; + mountinfo.options.ext4.journaling_mode = ExtJournalingMode::ordered; + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .mountinfo(mountinfo) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_TRUE(file->onExt4Ordered()); +} + +TEST_F(HipFileHandle, OnExt4OrderedReturnsFalseForExt4Journal) +{ + int fd{0xBADF00D}; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + MountInfo mountinfo; + mountinfo.type = FilesystemType::ext4; + mountinfo.options.ext4.journaling_mode = ExtJournalingMode::journal; + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .mountinfo(mountinfo) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->onExt4Ordered()); +} + +TEST_F(HipFileHandle, OnExt4OrderedReturnsFalseForOtherFileSystem) +{ + int fd{0xBADF00D}; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + MountInfo mountinfo; + mountinfo.type = FilesystemType::other; + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .mountinfo(mountinfo) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->onExt4Ordered()); +} + +TEST_F(HipFileHandle, OnXfsReturnsTrueForXfs) +{ + int fd{0xBADF00D}; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + MountInfo mountinfo; + mountinfo.type = FilesystemType::xfs; + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .mountinfo(mountinfo) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_TRUE(file->onXfs()); +} + +TEST_F(HipFileHandle, OnXfsReturnsFalseForOtherFileSystem) +{ + int fd{0xBADF00D}; + + // Return an eventfd as the file descriptor for the bufferd fd open so that when + // FileDescriptor calls close, it has a valid file descriptor to close. + int open_fd{eventfd(0, 0)}; + ASSERT_NE(open_fd, -1); + + MountInfo mountinfo; + mountinfo.type = FilesystemType::other; + + ExpectUnregisteredFileBuilder(msys, mlibmounthelper) + .fd_flags(O_DIRECT) + .mountinfo(mountinfo) + .open_fd(open_fd) + .build(); + auto fh{Context::get()->registerFile(fd)}; + auto file{Context::get()->getFile(fh)}; + + EXPECT_FALSE(file->onXfs()); +} + HIPFILE_WARN_NO_GLOBAL_CTOR_ON diff --git a/test/amd_detail/mconfiguration.h b/test/amd_detail/mconfiguration.h index fe910247..1194ad74 100644 --- a/test/amd_detail/mconfiguration.h +++ b/test/amd_detail/mconfiguration.h @@ -22,6 +22,7 @@ struct MConfiguration : Configuration { MOCK_METHOD(bool, fallback, (), (const, noexcept, override)); MOCK_METHOD(void, fallback, (bool), (noexcept, override)); MOCK_METHOD(unsigned int, statsLevel, (), (const, noexcept, override)); + MOCK_METHOD(bool, unsupportedFileSystems, (), (const, noexcept, override)); }; } diff --git a/test/amd_detail/mfile.h b/test/amd_detail/mfile.h index 4a8a50dc..f351bfa4 100644 --- a/test/amd_detail/mfile.h +++ b/test/amd_detail/mfile.h @@ -18,13 +18,16 @@ namespace hipFile { class MFile : public IFile { public: - MOCK_METHOD(hipFileHandle_t, getHandle, (), (const, override)); - MOCK_METHOD(int, getClientFd, (), (const, override)); - MOCK_METHOD(int, getBufferedFd, (), (const, override)); - MOCK_METHOD(std::optional, getUnbufferedFd, (), (const, override)); - MOCK_METHOD(const struct statx &, getStatx, (), (const, noexcept, override)); - MOCK_METHOD(int, getStatusFlags, (), (const, override)); - MOCK_METHOD(std::optional, getMountInfo, (), (const, override)); + MOCK_METHOD(hipFileHandle_t, handle, (), (const, noexcept, override)); + MOCK_METHOD(int, clientFd, (), (const, noexcept, override)); + MOCK_METHOD(int, bufferedFd, (), (const, noexcept, override)); + MOCK_METHOD(std::optional, unbufferedFd, (), (const, noexcept, override)); + MOCK_METHOD(uint32_t, dioMemAlign, (), (const, noexcept, override)); + MOCK_METHOD(uint32_t, dioOffsetAlign, (), (const, noexcept, override)); + MOCK_METHOD(bool, isBlockDevice, (), (const, noexcept, override)); + MOCK_METHOD(bool, isRegularFile, (), (const, noexcept, override)); + MOCK_METHOD(bool, onExt4Ordered, (), (const, noexcept, override)); + MOCK_METHOD(bool, onXfs, (), (const, noexcept, override)); }; class MFileMap : public FileMap {