From 135c38b41cbca7259ea8ffd130a15f49c7043154 Mon Sep 17 00:00:00 2001 From: Sam Ruscica Date: Tue, 7 Oct 2025 09:42:22 -0400 Subject: [PATCH] SWDEV-553436 Created wrapper functions for file read and file write (#935) --- .../include/hip/amd_detail/hip_storage.h | 68 ++++++++++++++++++ projects/clr/hipamd/src/CMakeLists.txt | 1 + projects/clr/hipamd/src/hip_device.cpp | 17 ++++- projects/clr/hipamd/src/hip_storage.cpp | 70 +++++++++++++++++++ projects/clr/rocclr/device/device.hpp | 32 +++++++-- projects/clr/rocclr/device/pal/paldevice.cpp | 21 ++++++ projects/clr/rocclr/device/pal/paldevice.hpp | 38 ++++++++++ projects/clr/rocclr/device/rocm/rocdevice.cpp | 34 +++++++++ projects/clr/rocclr/device/rocm/rocdevice.hpp | 22 +++++- projects/clr/rocclr/device/rocm/rocrctx.cpp | 2 + projects/clr/rocclr/device/rocm/rocrctx.hpp | 14 ++++ 11 files changed, 311 insertions(+), 8 deletions(-) create mode 100644 projects/clr/hipamd/include/hip/amd_detail/hip_storage.h create mode 100644 projects/clr/hipamd/src/hip_storage.cpp diff --git a/projects/clr/hipamd/include/hip/amd_detail/hip_storage.h b/projects/clr/hipamd/include/hip/amd_detail/hip_storage.h new file mode 100644 index 0000000000..ec66edcf6e --- /dev/null +++ b/projects/clr/hipamd/include/hip/amd_detail/hip_storage.h @@ -0,0 +1,68 @@ +/* Copyright (c) 2025 Advanced Micro Devices, Inc. + + 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. */ +#pragma once + +#include + +typedef struct hipAmdFileHandle_t { + /* + * file handle for AIS read & write. Linux will use fd. + * pad is keep the size consistent across different platforms. + */ + union { + void* handle; + int fd; + uint8_t pad[8]; + }; +} hipAmdFileHandle_t; + +/** + * @brief Read data from a file to device memory. + * + * Reads data from a file at the specified offset into a device memory buffer. + * The device memory pointer must be accessible from the host and point to + * a valid allocation. + * + * @param[IN] handle: Handle of the file to read from. + * @param[IN] devicePtr: Device memory buffer pointer to store the read data. + * @param[IN] size: Size in bytes of the data to read. + * @param[IN] file_offset: Offset in bytes into the file where data will be read from. + * @param[IN/OUT] size_copied: Actual number of bytes copied. + * @param[IN/OUT] status: Additional status if any. + */ +hipError_t hipAmdFileRead(hipAmdFileHandle_t handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status); + +/** + * Write data from device memory to a file. + * + * Writes data from device memory buffer to a file at the specified offset. + * The device memory pointer must be accessible from the host and point to + * a valid allocation. + * + * @param[IN] handle: Handle of the file to write to. + * @param[IN] devicePtr: Device memory buffer pointer containing data to write. + * @param[IN] size: Size in bytes of the data to write. + * @param[IN] file_offset: Offset in bytes into the file where data will be written. + * @param[IN/OUT] size_copied: Actual number of bytes written. + * @param[IN/OUT] status: Additional status if any. + */ +hipError_t hipAmdFileWrite(hipAmdFileHandle_t handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status); diff --git a/projects/clr/hipamd/src/CMakeLists.txt b/projects/clr/hipamd/src/CMakeLists.txt index 756dcd7956..887499b7ab 100644 --- a/projects/clr/hipamd/src/CMakeLists.txt +++ b/projects/clr/hipamd/src/CMakeLists.txt @@ -118,6 +118,7 @@ target_sources(amdhip64 PRIVATE hip_peer.cpp hip_platform.cpp hip_profile.cpp + hip_storage.cpp hip_stream_ops.cpp hip_stream.cpp hip_surface.cpp diff --git a/projects/clr/hipamd/src/hip_device.cpp b/projects/clr/hipamd/src/hip_device.cpp index 03b7d7938b..1397d06f1d 100644 --- a/projects/clr/hipamd/src/hip_device.cpp +++ b/projects/clr/hipamd/src/hip_device.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "hip_internal.hpp" #include "hip_mempool_impl.hpp" @@ -199,8 +200,7 @@ void Device::WaitActiveStreams(hip::Stream* blocking_stream, bool wait_null_stre ((active_stream->Flags() & hipStreamNonBlocking) == 0) && // and it's not the current stream (active_stream != blocking_stream)) { - ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_WAIT, "Waiting on active stream %p", - active_stream); + ClPrint(amd::LOG_DETAIL_DEBUG, amd::LOG_WAIT, "Waiting on active stream %p", active_stream); // Get the last valid command waitForStream(active_stream); } @@ -771,6 +771,7 @@ hipError_t hipGetProcAddress(const char* symbol, void** pfn, int hipVersion, uin HIP_INIT_API(hipGetProcAddress, symbol, pfn, hipVersion, flags, symbolStatus); std::string symbolString = symbol; + if (symbol == nullptr || symbolString == "" || pfn == nullptr) { HIP_RETURN(hipErrorInvalidValue); } @@ -783,6 +784,18 @@ hipError_t hipGetProcAddress(const char* symbol, void** pfn, int hipVersion, uin if (hipVersion >= 600) { symbolString = "hipChooseDeviceR0600"; } + } else if (symbolString == "hipAmdFileRead") { + *pfn = reinterpret_cast(&hipAmdFileRead); + if (symbolStatus != nullptr) { + *symbolStatus = HIP_GET_PROC_ADDRESS_SUCCESS; + } + HIP_RETURN(hipSuccess); + } else if (symbolString == "hipAmdFileWrite") { + *pfn = reinterpret_cast(&hipAmdFileWrite); + if (symbolStatus != nullptr) { + *symbolStatus = HIP_GET_PROC_ADDRESS_SUCCESS; + } + HIP_RETURN(hipSuccess); } void* handle = hip::PlatformState::instance().getDynamicLibraryHandle(); diff --git a/projects/clr/hipamd/src/hip_storage.cpp b/projects/clr/hipamd/src/hip_storage.cpp new file mode 100644 index 0000000000..601ef8f4e5 --- /dev/null +++ b/projects/clr/hipamd/src/hip_storage.cpp @@ -0,0 +1,70 @@ +/* Copyright (c) 2025 Advanced Micro Devices, Inc. + + 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 "hip_internal.hpp" + +hipError_t hipAmdFileRead(hipAmdFileHandle_t handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + if (size == 0) { + // Skip if nothing needs reading. + return hipSuccess; + } + amd::Device* device = hip::getCurrentDevice()->devices()[0]; + if (device == nullptr) { + LogError("Failed to get current device"); + return hipErrorInvalidDevice; + } +#if defined(_WIN32) + amd::Os::FileDesc opaque = handle.handle; +#else + amd::Os::FileDesc opaque = handle.fd; +#endif + if (!device->amdFileRead(opaque, devicePtr, size, file_offset, size_copied, status)) { + LogError("Failed to perform file read operation"); + return hipErrorUnknown; + } + return hipSuccess; +} + +hipError_t hipAmdFileWrite(hipAmdFileHandle_t handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + if (size == 0) { + // Skip if nothing needs writing. + return hipSuccess; + } + amd::Device* device = hip::getCurrentDevice()->devices()[0]; + if (device == nullptr) { + LogError("Failed to get current device"); + return hipErrorInvalidDevice; + } +#if defined(_WIN32) + amd::Os::FileDesc opaque = handle.handle; +#else + amd::Os::FileDesc opaque = handle.fd; +#endif + if (!device->amdFileWrite(opaque, devicePtr, size, file_offset, size_copied, status)) { + LogError("Failed to perform file write operation"); + return hipErrorUnknown; + } + return hipSuccess; +} diff --git a/projects/clr/rocclr/device/device.hpp b/projects/clr/rocclr/device/device.hpp index 6cc562556f..329542e4ae 100644 --- a/projects/clr/rocclr/device/device.hpp +++ b/projects/clr/rocclr/device/device.hpp @@ -715,7 +715,7 @@ class Settings : public amd::HeapObject { //! Virtual destructor as this class is used as a base class and is also used //! to delete the derived classes. - virtual ~Settings() {}; + virtual ~Settings(){}; //! Check the specified extension bool checkExtension(uint name) const { @@ -810,7 +810,7 @@ class Memory : public amd::HeapObject { }; //! Default destructor for the device memory object - virtual ~Memory() {}; + virtual ~Memory(){}; //! Releases virtual objects associated with this memory void releaseVirtual(); @@ -1009,7 +1009,7 @@ class Sampler : public amd::HeapObject { Sampler() : hwSrd_(0), hwState_(nullptr) {} //! Default destructor for the device memory object - virtual ~Sampler() {}; + virtual ~Sampler(){}; //! Returns device specific HW state for the sampler uint64_t hwSrd() const { return hwSrd_; } @@ -1605,7 +1605,7 @@ class Isa { uint32_t memChannelBankWidth_; //!< Memory channel bank width. uint32_t localMemSizePerCU_; //!< Local memory size per CU. uint32_t localMemBanks_; //!< Number of banks of local memory. -}; // class Isa +}; // class Isa /*! \addtogroup Runtime * @{ @@ -1806,6 +1806,30 @@ class Device : public RuntimeObject { virtual bool globalFreeMemory(size_t* freeMemory //!< Free memory information on a GPU device ) const = 0; + /** + * @brief Read data from a file to device memory. + * @param[IN] handle: file descriptor of the file to read. + * @param[IN] devicePtr: VRAM buffer pointer. + * @param[IN] size: size of read. + * @param[IN] file_offset: offset into fd where data has to be read. + * @param[IN/OUT] size_copied: actual size read. + * @param[IN/OUT] status: additional status. + */ + virtual bool amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) = 0; + + /** + * Write data from device memory to a file. + * @param[IN] handle: file descriptor of the file to write. + * @param[IN] devicePtr: VRAM buffer pointer. + * @param[IN] size: size of write. + * @param[IN] file_offset: offset into fd where data has to written. + * @param[IN/OUT] size_copied: actual size copied. + * @param[IN/OUT] status: additional status. + */ + virtual bool amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) = 0; + virtual bool importExtSemaphore(void** extSemaphore, const amd::Os::FileDesc& handle, amd::ExternalSemaphoreHandleType sem_handle_type) = 0; virtual void DestroyExtSemaphore(void* extSemaphore) = 0; diff --git a/projects/clr/rocclr/device/pal/paldevice.cpp b/projects/clr/rocclr/device/pal/paldevice.cpp index 0fba3b6efb..9a8c709f3a 100644 --- a/projects/clr/rocclr/device/pal/paldevice.cpp +++ b/projects/clr/rocclr/device/pal/paldevice.cpp @@ -2138,6 +2138,27 @@ bool Device::globalFreeMemory(size_t* freeMemory) const { return true; } +// PAL Device file I/O stubs: PAL doesn't implement AIS file I/O yet. +// Provide definitions so the vtable and linker are satisfied. +// Replace with a real implementation if/when PAL supports file I/O. +bool Device::amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + if (size_copied) { + *size_copied = 0; + } + LogError("PAL Device: amdFileRead not supported on this backend"); + return false; +} + +bool Device::amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + if (size_copied) { + *size_copied = 0; + } + LogError("PAL Device: amdFileWrite not supported on this backend"); + return false; +} + amd::Memory* Device::findMapTarget(size_t size) const { // Must be serialised for access amd::ScopedLock lk(mapCacheOps_); diff --git a/projects/clr/rocclr/device/pal/paldevice.hpp b/projects/clr/rocclr/device/pal/paldevice.hpp index 07c667205e..5461430639 100644 --- a/projects/clr/rocclr/device/pal/paldevice.hpp +++ b/projects/clr/rocclr/device/pal/paldevice.hpp @@ -135,6 +135,20 @@ class NullDevice : public amd::Device { //! Empty implementation on Null device virtual bool globalFreeMemory(size_t* freeMemory) const { return false; } + //! Empty implementation on Null device + virtual bool amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + ShouldNotReachHere(); + return false; + } + + //! Empty implementation on Null device + virtual bool amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + ShouldNotReachHere(); + return false; + } + //! Get GPU device settings const pal::Settings& settings() const { return reinterpret_cast(*settings_); } virtual void* svmAlloc(amd::Context& context, size_t size, size_t alignment, @@ -443,6 +457,30 @@ class Device : public NullDevice { //! Retrieves information about free memory on a GPU device virtual bool globalFreeMemory(size_t* freeMemory) const; + /** + * @brief Read data from a file to device memory. + * @param[IN] handle: file descriptor of the file to read. + * @param[IN] devicePtr: VRAM buffer pointer. + * @param[IN] size: size of read. + * @param[IN] file_offset: offset into fd where data has to be read. + * @param[IN/OUT] size_copied: actual size read. + * @param[IN/OUT] status: additional status. + */ + virtual bool amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status); + + /** + * Write data from device memory to a file. + * @param[IN] handle: file descriptor of the file to write. + * @param[IN] devicePtr: VRAM buffer pointer. + * @param[IN] size: size of write. + * @param[IN] file_offset: offset into fd where data has to written. + * @param[IN/OUT] size_copied: actual size copied. + * @param[IN/OUT] status: additional status. + */ + virtual bool amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status); + //! Returns a GPU memory object from AMD memory object pal::Memory* getGpuMemory(amd::Memory* mem //!< Pointer to AMD memory object ) const; diff --git a/projects/clr/rocclr/device/rocm/rocdevice.cpp b/projects/clr/rocclr/device/rocm/rocdevice.cpp index a89ca721f6..8ffcd81447 100644 --- a/projects/clr/rocclr/device/rocm/rocdevice.cpp +++ b/projects/clr/rocclr/device/rocm/rocdevice.cpp @@ -1722,6 +1722,40 @@ bool Device::globalFreeMemory(size_t* freeMemory) const { return true; } +bool Device::amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + hsa_amd_ais_file_handle_t fh{}; +#if defined(_WIN32) + fh.handle = handle; +#else + fh.fd = handle; +#endif + hsa_status_t ret = Hsa::ais_file_read(fh, + devicePtr, size, file_offset, size_copied, status); + if (HSA_STATUS_SUCCESS != ret) { + LogPrintfError("hsa_amd_ais_file_read operation failed with err 0x%xh", ret); + return false; + } + return true; +} + +bool Device::amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) { + hsa_amd_ais_file_handle_t fh{}; +#if defined(_WIN32) + fh.handle = handle; +#else + fh.fd = handle; +#endif + hsa_status_t ret = Hsa::ais_file_write(fh, + devicePtr, size, file_offset, size_copied, status); + if (HSA_STATUS_SUCCESS != ret) { + LogPrintfError("hsa_amd_ais_file_write operation failed with err 0x%xh", ret); + return false; + } + return true; +} + bool Device::bindExternalDevice(uint flags, void* const gfxDevice[], void* gfxContext, bool validateOnly) { #if defined(_WIN32) diff --git a/projects/clr/rocclr/device/rocm/rocdevice.hpp b/projects/clr/rocclr/device/rocm/rocdevice.hpp index 944505f658..02559d49a1 100644 --- a/projects/clr/rocclr/device/rocm/rocdevice.hpp +++ b/projects/clr/rocclr/device/rocm/rocdevice.hpp @@ -133,7 +133,7 @@ class Sampler : public device::Sampler { class NullDevice : public amd::Device { public: //! constructor - NullDevice() {}; + NullDevice(){}; //! create the device bool create(const amd::Isa& isa); @@ -279,6 +279,20 @@ class NullDevice : public amd::Device { return false; } + //! Empty implementation on Null device + bool amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) override { + ShouldNotReachHere(); + return false; + } + + //! Empty implementation on Null device + bool amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) override { + ShouldNotReachHere(); + return false; + } + bool SetClockMode(const cl_set_device_clock_mode_input_amd setClockModeInput, cl_set_device_clock_mode_output_amd* pSetClockModeOutput) override { return true; @@ -407,6 +421,11 @@ class Device : public NullDevice { const void* agentInfo = nullptr) const override; // nullptr uses default CPU agent virtual void hostFree(void* ptr, size_t size = 0) const; + virtual bool amdFileRead(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) override; + virtual bool amdFileWrite(amd::Os::FileDesc handle, void* devicePtr, uint64_t size, int64_t file_offset, + uint64_t* size_copied, int32_t* status) override; + bool deviceAllowAccess(void* dst) const; bool allowPeerAccess(device::Memory* memory) const; @@ -680,4 +699,3 @@ void callbackQueue(hsa_status_t status, hsa_queue_t* queue, void* data); /** * @} */ - diff --git a/projects/clr/rocclr/device/rocm/rocrctx.cpp b/projects/clr/rocclr/device/rocm/rocrctx.cpp index 63308eed8a..306993a7f7 100644 --- a/projects/clr/rocclr/device/rocm/rocrctx.cpp +++ b/projects/clr/rocclr/device/rocm/rocrctx.cpp @@ -126,6 +126,8 @@ bool Hsa::LoadLib() { GET_ROCR_SYMBOL(hsa_amd_vmem_address_reserve_align) GET_ROCR_SYMBOL(hsa_amd_enable_logging) GET_ROCR_SYMBOL(hsa_amd_memory_get_preferred_copy_engine) + GET_ROCR_SYMBOL(hsa_amd_ais_file_read) + GET_ROCR_SYMBOL(hsa_amd_ais_file_write) // Image extensions GET_ROCR_SYMBOL(hsa_ext_image_data_get_info) diff --git a/projects/clr/rocclr/device/rocm/rocrctx.hpp b/projects/clr/rocclr/device/rocm/rocrctx.hpp index 73a1cded43..aa7e841fb6 100644 --- a/projects/clr/rocclr/device/rocm/rocrctx.hpp +++ b/projects/clr/rocclr/device/rocm/rocrctx.hpp @@ -135,6 +135,8 @@ struct RocrEntryPoints { decltype(hsa_amd_vmem_address_reserve_align)* hsa_amd_vmem_address_reserve_align_; decltype(hsa_amd_enable_logging)* hsa_amd_enable_logging_; decltype(hsa_amd_memory_get_preferred_copy_engine)* hsa_amd_memory_get_preferred_copy_engine_; + decltype(hsa_amd_ais_file_read)* hsa_amd_ais_file_read_; + decltype(hsa_amd_ais_file_write)* hsa_amd_ais_file_write_; // Image extensions decltype(hsa_ext_image_data_get_info)* hsa_ext_image_data_get_info_; decltype(hsa_ext_image_create)* hsa_ext_image_create_; @@ -505,6 +507,18 @@ class Hsa : public amd::AllStatic { return ROCR_DYN(hsa_amd_memory_get_preferred_copy_engine)( dst_agent, src_agent, recommended_ids_mask); } + static hsa_status_t ais_file_read(hsa_amd_ais_file_handle_t handle, void* devicePtr, + uint64_t size, int64_t file_offset, uint64_t* size_copied, + int32_t* status) { + return ROCR_DYN(hsa_amd_ais_file_read)(handle, devicePtr, size, file_offset, size_copied, + status); + } + static hsa_status_t ais_file_write(hsa_amd_ais_file_handle_t handle, void* devicePtr, + uint64_t size, int64_t file_offset, uint64_t* size_copied, + int32_t* status) { + return ROCR_DYN(hsa_amd_ais_file_write)(handle, devicePtr, size, file_offset, size_copied, + status); + } // Image extensions static hsa_status_t image_create(hsa_agent_t agent,