From d82bb39f0f34ff1d0c63a44faad46a3de94f2b03 Mon Sep 17 00:00:00 2001 From: Harish Kasiviswanathan Date: Fri, 3 Oct 2025 14:18:35 -0400 Subject: [PATCH] kfdtest: Add simple AMD Infinity Storage (AIS) test Add AISReadTest which invokes direct transfer (read) from the filesystem to GPU VRAM. The test carries out the following: - create a file in /tmp directory. Checks if the file is an NVME drive, if not, fail. - fill the file with different patterns at different offset - direct transfer (read) from (above file) each offset to a different VRAM buffer - cross-check if each VRAM buffer contains the correct pattern Signed-off-by: Harish Kasiviswanathan --- .../libhsakmt/tests/kfdtest/CMakeLists.txt | 3 +- .../tests/kfdtest/src/KFDAISTest.cpp | 228 ++++++++++++++++++ .../tests/kfdtest/src/KFDAISTest.hpp | 64 +++++ 3 files changed, 294 insertions(+), 1 deletion(-) create mode 100644 projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.cpp create mode 100644 projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.hpp diff --git a/projects/rocr-runtime/libhsakmt/tests/kfdtest/CMakeLists.txt b/projects/rocr-runtime/libhsakmt/tests/kfdtest/CMakeLists.txt index 59ea7b092698..1fb7ea9c5ad0 100644 --- a/projects/rocr-runtime/libhsakmt/tests/kfdtest/CMakeLists.txt +++ b/projects/rocr-runtime/libhsakmt/tests/kfdtest/CMakeLists.txt @@ -214,7 +214,8 @@ set (SRC_FILES gtest-1.6.0/gtest-all.cpp src/KFDRASTest.cpp src/KFDPCSamplingTest.cpp src/KFDNegativeTest.cpp - src/RDMATest.cpp) + src/RDMATest.cpp + src/KFDAISTest.cpp) message( STATUS "PROJECT_SOURCE_DIR:" ${PROJECT_SOURCE_DIR} ) #message( STATUS "SRC_FILES: ") diff --git a/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.cpp b/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.cpp new file mode 100644 index 000000000000..197b70a4c6df --- /dev/null +++ b/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.cpp @@ -0,0 +1,228 @@ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#include +#include +#include +#include +#include "KFDAISTest.hpp" + +void KFDAISTest::SetUp() { + ROUTINE_START + + KFDBaseComponentTest::SetUp(); + + ROUTINE_END +} + +void KFDAISTest::TearDown() { + ROUTINE_START + + KFDBaseComponentTest::TearDown(); + + ROUTINE_END +} + +/* + * Currently, keep it simple and support only files backed by NVME device. + * As we know files in NVME is backed by PCI. The driver will futher check if + * PCI peer-to-peer is possible. + */ +int KFDAISTest::checkIfFileIsInNVME(int fd) +{ + struct stat st; + char path[PATH_MAX], link[PATH_MAX]; + + /* Check if the file lies in NVME*/ + if (fstat(fd, &st) == -1) { + LOG() << "Failed to stat file " << fd << std::endl; + return -1; + } + snprintf(path, sizeof(path), "/sys/dev/block/%u:%u", + major(st.st_dev), minor(st.st_dev)); + + ssize_t len = readlink(path, link, sizeof(link)-1); + if (len != -1) { + link[len] = '\0'; + if (!strstr(link, "nvme")) { + LOG() << "File doesn't exist on a NVME driver " << std::endl; + return -1; + } + } else { + LOG() << "File not backed by PCI device" << std::endl; + return -1; + } + + return 0; +} + +int KFDAISTest::createFileInNVME(void) +{ + m_fd = open(FILENAME, O_RDWR | O_CREAT | O_TRUNC | O_DIRECT, S_IRUSR | S_IWUSR); + if (m_fd < 0) { + LOG() << "Failed to create file: " << FILENAME << std::endl; + return -1; + } + + if (ftruncate(m_fd, FILE_SIZE) < 0) { + LOG() << "Failed to set file size: " << FILENAME << std::endl; + return -1; + } + lseek(m_fd, 0, SEEK_SET); + return 0; +} + +/* + * Fill the file with patterns + * [0 - BUFFER_SIZE-1] <-- START_PATTERN + * [BUFFER_SIZE - 2*BUFFER_SIZE - ]<-- START_PATTERN + 1 + * . + * so on for N_BUFFERS +*/ +int KFDAISTest::fillFileWithPatterns(void) +{ + const int START_PATTERN = 0x11111111; + ssize_t written; + + for (int i=0; i < N_BUFFERS; i++) { + m_pPatterns[i] = START_PATTERN + i; + std::vector buffer(BUFFER_SIZE / sizeof(int), m_pPatterns[i]); + + written = write(m_fd, buffer.data(), BUFFER_SIZE); + + if (written < 0) { + LOG() << "Failed to write to file: " << FILENAME << std::endl; + return -1; + } + } + + return 0; +} + +/* + * Check if the buffers copied from the file has the correct pattern + * To keep it simple only the first and the last buffer is checked + */ +int KFDAISTest::checkBuffersWithPatterns(int gpuNode) +{ + int *lbuf; + + for (int i=0; i < N_BUFFERS; i++) { + lbuf = (int *)m_pBufs[i] + BUFFER_SIZE/sizeof(int) - 1; + + if (m_pPatterns[i] != *(int *)m_pBufs[i] || + m_pPatterns[i] != *lbuf) + return -1; + } + return 0; +} + +int KFDAISTest::directReadWritefromFile(void *buf, int size, + HSAint64 file_offset, HsaAisFlags rwFlags) +{ + HSAint32 status; + HSAuint64 size_copied; + + if (hsaKmtAisReadWriteFile(buf, size, m_fd, file_offset, + rwFlags, &size_copied, &status) != HSAKMT_STATUS_SUCCESS) + return -1; + + if (status || size != size_copied) + return -1; + + return 0; +} + +/* + * Alloc N_BUFFERS of BUFFER_SIZE in VRAM +*/ +int KFDAISTest::allocBuffers(HSAuint32 gpuNode) +{ + HsaMemMapFlags mapFlags = {0}; + HsaMemFlags flags = {0}; + HSAKMT_STATUS ret; + + flags.Value = 0; + flags.ui32.PageSize = HSA_PAGE_SIZE_4KB; + flags.ui32.HostAccess = 1; + flags.ui32.NonPaged = 1; + + for (int i=0; i < N_BUFFERS; i++) { + ret = hsaKmtAllocMemory(gpuNode, BUFFER_SIZE, flags, &m_pBufs[i]); + if (ret != HSAKMT_STATUS_SUCCESS) + return ret; + + if (hsakmt_is_dgpu()) { + ret = hsaKmtMapMemoryToGPUNodes(m_pBufs[i], BUFFER_SIZE, NULL, + mapFlags, 1, reinterpret_cast(&gpuNode)); + if (ret != HSAKMT_STATUS_SUCCESS) + return ret; + } + } + + return ret; +} + +void KFDAISTest::freeBuffers(void) +{ + for (int i = 0; i < m_pBufs.size(); i++) { + if (m_pBufs[i] != NULL) { + if (hsakmt_is_dgpu()) + EXPECT_SUCCESS(hsaKmtUnmapMemoryToGPU(m_pBufs[i])); + EXPECT_SUCCESS(hsaKmtFreeMemory(m_pBufs[i], BUFFER_SIZE)); + } + } +} + +TEST_F(KFDAISTest, AISReadTest) { + TEST_REQUIRE_ENV_CAPABILITIES(ENVCAPS_64BITLINUX); + TEST_START(TESTPROFILE_RUNALL); + + HSAuint32 defaultGPUNode = m_NodeInfo.HsaDefaultGPUNode(); + ASSERT_GE(defaultGPUNode, 0) << "failed to get default GPU Node"; + + ASSERT_EQ(createFileInNVME(), 0); + + if (checkIfFileIsInNVME(m_fd) < 0) { + LOG() << "Skipping test. File not in NVME. AIS not supported " << FILENAME << std::endl; + return; + } + + /* Fill in test file with different patterns at pre-defined offset */ + ASSERT_EQ(fillFileWithPatterns(), 0); + + /* Allocate multiple buffers */ + ASSERT_EQ(allocBuffers(defaultGPUNode), 0); + + /* Read into each buffer from different pre-defined offset */ + for(int i=0; i < N_BUFFERS; i++) + ASSERT_EQ(directReadWritefromFile(m_pBufs[i], BUFFER_SIZE, BUFFER_SIZE * i, HSA_AIS_READ), 0); + + /* Check if the read data matches the expected pattern */ + ASSERT_EQ(checkBuffersWithPatterns(defaultGPUNode), 0); + + freeBuffers(); + deleteFileInNVME(); + + TEST_END +} diff --git a/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.hpp b/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.hpp new file mode 100644 index 000000000000..0f81e203fce0 --- /dev/null +++ b/projects/rocr-runtime/libhsakmt/tests/kfdtest/src/KFDAISTest.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2025 Advanced Micro Devices, Inc. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef __KFD_AIS_TEST__H__ +#define __KFD_AIS_TEST__H__ + +#include +#include "KFDBaseComponentTest.hpp" + +class KFDAISTest : public KFDBaseComponentTest { + public: + static const int BUFFER_SIZE = 32 * 4096; // 32 KB + static const int FILE_SIZE = 1024 * 1024; // 1 MB + static const int N_BUFFERS = FILE_SIZE / BUFFER_SIZE; + static constexpr const char *FILENAME = "/tmp/kfdais_test_file.bin"; + + KFDAISTest(void) : m_pBufs(N_BUFFERS, NULL), m_pPatterns(N_BUFFERS, 0), m_fd(-1) {} + + ~KFDAISTest(void) {} + protected: + virtual void SetUp(); + virtual void TearDown(); + + int createFileInNVME(void); + void deleteFileInNVME(void) { + if (m_fd >= 0) + close(m_fd); + unlink(FILENAME); + } + int fillFileWithPatterns(void); + int checkBuffersWithPatterns(int gpuNode); + int directReadWritefromFile(void *buf, int size, + HSAint64 fileOffset, HsaAisFlags rwFlags); + int allocBuffers(HSAuint32 gpuNode); + void freeBuffers(); + + int m_fd; + std::vector m_pBufs; // Vector to hold allocated buffers + std::vector m_pPatterns; // Patterns to validate read / write + + int checkIfFileIsInNVME(int fd); +}; + +#endif // __KFD_AIS_TEST__H__