CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
206 lines
6.0 KiB
C++
206 lines
6.0 KiB
C++
/* Copyright (c) Advanced Micro Devices, Inc. All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: MIT
|
|
*/
|
|
|
|
/* aiscp - AMD Infinity Storage Copy
|
|
*
|
|
* Usage: ./aiscp SOURCE DEST
|
|
*
|
|
* This _very_basic_ program copies SOURCE to DEST via GPU memory.
|
|
*/
|
|
|
|
#include <hipfile.h>
|
|
#include <hip/hip_runtime_api.h>
|
|
|
|
#include <algorithm>
|
|
#include <cerrno>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fcntl.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <unistd.h>
|
|
|
|
/// @brief AISCP_CHUNK_SIZE defines the maximum size of each aiscp IO operation.
|
|
/// Currently AISCP_CHUNK_SIZE is defined to match the maximum IO size on Linux
|
|
/// (MAX_RW_COUNT).
|
|
#ifndef AISCP_CHUNK_SIZE
|
|
#define AISCP_CHUNK_SIZE 0x7ffff000LU
|
|
#endif
|
|
|
|
/// @brief Open and register a file
|
|
/// @param path [in] Path to the file
|
|
/// @param flags [in] flags: Flags to pass to open (2)
|
|
/// @param mode [in] mode: Mode to pass to open (2)
|
|
/// @param fd [out] fd: The file descriptor of the opened file
|
|
/// @param handle [out] handle: The handle to use with hipFile APIs
|
|
/// @return zero on success, non-zero on failure
|
|
static int
|
|
open_file(const char *path, int flags, mode_t mode, int *fd, hipFileHandle_t *handle)
|
|
{
|
|
hipFileError_t hipfile_err;
|
|
hipFileDescr_t descr;
|
|
|
|
*fd = open(path, flags | O_DIRECT, mode);
|
|
if (-1 == *fd) {
|
|
fprintf(stderr, "Could not open %s (%s)\n", path, strerror(errno));
|
|
return 1;
|
|
}
|
|
|
|
descr.type = hipFileHandleTypeOpaqueFD;
|
|
descr.handle.fd = *fd;
|
|
|
|
hipfile_err = hipFileHandleRegister(handle, &descr);
|
|
if (hipFileSuccess != hipfile_err.err) {
|
|
fprintf(stderr, "Could not register %s (%s)\n", path, hipFileGetOpErrorString(hipfile_err.err));
|
|
close(*fd);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/// @brief Unregister and close a file
|
|
/// @param path [in] Path to the file
|
|
/// @param fd [in] The file descriptor of the opened file
|
|
/// @param handle [in] The handle of the opened file
|
|
/// @return zero on success, non-zero on failure
|
|
static int
|
|
close_file(const char *path, int fd, hipFileHandle_t handle)
|
|
{
|
|
hipFileHandleDeregister(handle);
|
|
if (-1 == close(fd)) {
|
|
fprintf(stderr, "Could not close %s (%s)\n", path, strerror(errno));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/// @brief Round value to the next multiple of align. Align _must_ be a power of 2.
|
|
/// @param value The value to round up.
|
|
/// @param align Value will be rounded up to a multiple of align
|
|
/// @return Value rounded up to a multiple of align.
|
|
static inline size_t
|
|
align_up(size_t value, size_t align)
|
|
{
|
|
return (value + align - 1) & ~(align - 1);
|
|
}
|
|
|
|
/// @brief Determines if value is a power of two
|
|
/// @param value The value to inspect
|
|
/// @return True if value is a power of two, false otherwise
|
|
static inline bool
|
|
is_power_of_two(size_t value)
|
|
{
|
|
return (value > 0) && ((value & (value - 1)) == 0);
|
|
}
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
const char *src_path, *dst_path;
|
|
int src_fd, dst_fd;
|
|
hipFileHandle_t src_handle, dst_handle;
|
|
void *devbuf;
|
|
hipError_t hip_err;
|
|
int exit_status = EXIT_FAILURE;
|
|
size_t buffer_size, file_size, block_size;
|
|
ssize_t nwrite{}, nread{}, nbytes{};
|
|
hoff_t file_offset{};
|
|
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage: %s SOURCE DEST\n", argv[0]);
|
|
exit(1);
|
|
}
|
|
|
|
src_path = argv[1];
|
|
dst_path = argv[2];
|
|
|
|
{
|
|
struct stat statbuf;
|
|
if (stat(src_path, &statbuf)) {
|
|
fprintf(stderr, "Could not stat file: %s (%s)\n", src_path, strerror(errno));
|
|
goto program_exit;
|
|
}
|
|
file_size = static_cast<size_t>(statbuf.st_size);
|
|
block_size = static_cast<size_t>(statbuf.st_blksize);
|
|
if (!is_power_of_two(block_size)) {
|
|
fprintf(stderr, "Blocksize is not a power of two (%zu)", block_size);
|
|
goto program_exit;
|
|
}
|
|
}
|
|
|
|
if (open_file(dst_path, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH, &dst_fd,
|
|
&dst_handle)) {
|
|
goto program_exit;
|
|
}
|
|
|
|
if (0 == file_size) {
|
|
exit_status = EXIT_SUCCESS;
|
|
goto close_dst;
|
|
}
|
|
|
|
if (open_file(src_path, O_RDONLY, 0, &src_fd, &src_handle)) {
|
|
goto close_dst;
|
|
}
|
|
|
|
buffer_size = align_up(std::min(file_size, AISCP_CHUNK_SIZE), block_size);
|
|
hip_err = hipMalloc(&devbuf, buffer_size);
|
|
if (hipSuccess != hip_err) {
|
|
fprintf(stderr, "Could not allocate device buffer (%d)", hip_err);
|
|
goto close_src;
|
|
}
|
|
|
|
// Copy the file chunk-by-chunk until we hit EOF
|
|
do {
|
|
nread = hipFileRead(src_handle, devbuf, buffer_size, file_offset, 0);
|
|
if (nread < 0) {
|
|
fprintf(stderr, "Could not read from %s (%zd) (%s)\n", src_path, nread,
|
|
IS_HIPFILE_ERR(nread) ? HIPFILE_ERRSTR(nread) : strerror(errno));
|
|
goto free_devbuf;
|
|
}
|
|
|
|
nwrite = 0;
|
|
while (nwrite < nread) {
|
|
nbytes =
|
|
hipFileWrite(dst_handle, devbuf, align_up(static_cast<size_t>(nread - nwrite), block_size),
|
|
file_offset + nwrite, nwrite);
|
|
if (nbytes < 0) {
|
|
fprintf(stderr, "Could not write to %s (%zd) (%s)\n", dst_path, nbytes,
|
|
IS_HIPFILE_ERR(nbytes) ? HIPFILE_ERRSTR(nbytes) : strerror(errno));
|
|
goto free_devbuf;
|
|
}
|
|
nwrite += nbytes;
|
|
}
|
|
file_offset += nread;
|
|
} while (nread > 0);
|
|
|
|
if (-1 == ftruncate(dst_fd, static_cast<off_t>(file_size))) {
|
|
fprintf(stderr, "Could not truncate %s (%zu) (%s)\n", dst_path, file_size, strerror(errno));
|
|
}
|
|
|
|
exit_status = EXIT_SUCCESS;
|
|
|
|
free_devbuf:
|
|
hip_err = hipFree(devbuf);
|
|
if (hipSuccess != hip_err) {
|
|
fprintf(stderr, "Could free device buffer (%d)\n", hip_err);
|
|
exit_status = EXIT_FAILURE;
|
|
}
|
|
|
|
close_src:
|
|
if (close_file(src_path, src_fd, src_handle)) {
|
|
exit_status = EXIT_FAILURE;
|
|
}
|
|
|
|
close_dst:
|
|
if (close_file(dst_path, dst_fd, dst_handle)) {
|
|
exit_status = EXIT_FAILURE;
|
|
}
|
|
|
|
program_exit:
|
|
return exit_status;
|
|
}
|