diff --git a/.github/workflows/ci-build-sw.yml b/.github/workflows/ci-build-sw.yml new file mode 100644 index 0000000..3fd5f6e --- /dev/null +++ b/.github/workflows/ci-build-sw.yml @@ -0,0 +1,47 @@ +# ################################################################################################## +# The MIT License (MIT) +# 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. +# ################################################################################################## + +name: CI Build SW + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-22.04 + permissions: { contents: read } + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install build tools + run: | + sudo apt-get update -qq + sudo apt-get install -y build-essential linux-headers-$(uname -r) libxml2-dev libzmq3-dev libjsoncpp-dev + + - name: Build hotplug driver + if: ${{ false }} + run: | + cd ${{ github.workspace }}/submodules/pcie-hotplug-drv/ && make + + - name: Build linker + run: | + cd ${{ github.workspace }}/submodules/v80-vitis-flow && cmake . && make diff --git a/deploy/base_pdi/build.py b/deploy/base_pdi/build.py index 85f7e0e..28b99d7 100644 --- a/deploy/base_pdi/build.py +++ b/deploy/base_pdi/build.py @@ -26,7 +26,7 @@ ROOT_PATH = os.path.realpath(".") DESIGN_PDI_PATH = os.path.join(ROOT_PATH, "..") -NOC_SOLUTION_PATH = os.path.realpath("../../submodules/v80-vitis-flow/resources/") +RESOURCES_PATH = os.path.realpath("../../submodules/v80-vitis-flow/resources/") COMPUTE_EXAMPLE_DIR = os.path.realpath("../../examples/05_perf/") DEPLOY_PROJECT_COMPUTE = os.path.join(os.getcwd(), "deploy_project_compute") HLS_DIR_COMPUTE = os.path.join(DEPLOY_PROJECT_COMPUTE, "hls/") @@ -103,6 +103,7 @@ def linker_step(platform): sol1_paths = [os.path.join(d, "sol1") for d in build_dirs] run_linker(CONFIG_FILE_PATH_COMPUTE, sol1_paths) shutil.copy(os.path.join(LINKER_BUILD_DIR_COMPUTE, "run_pre.tcl"), AVED_SRC_DIR_COMPUTE) + shutil.copy(os.path.join(RESOURCES_PATH, "run_post.tcl"), AVED_SRC_DIR_COMPUTE) elif platform == "eth": print("Eth mode not supported yet.") else: @@ -131,7 +132,7 @@ def generate_noc_solution_step(platform): if platform == "compute": os.chdir(AVED_ROOT_DIR_COMPUTE) subprocess.run(["vivado", "-mode", "tcl", "-source", "src/export_noc.tcl"], check=True) - shutil.copy(os.path.join(AVED_ROOT_DIR_COMPUTE, "noc_sol.ncr"), os.path.join(NOC_SOLUTION_PATH, "noc_sol_compute.ncr")) + shutil.copy(os.path.join(AVED_ROOT_DIR_COMPUTE, "noc_sol.ncr"), os.path.join(RESOURCES_PATH, "noc_sol_compute.ncr")) elif platform == "eth": print("Eth mode not supported yet.") else: @@ -215,7 +216,7 @@ def step_index(step_id): # shutil.copy(os.path.join(AVED_ROOT_DIR_COMPUTE, "design.pdi"), os.path.join(ROOT_PATH, "design.pdi")) # # Generate NoC solution from the vivado project # subprocess.run(["vivado", "-mode", "batch", "-source", "export_noc.tcl"], check=True) - # shutil.copy(os.path.join(AVED_ROOT_DIR_COMPUTE, "noc_sol.ncr"), os.path.join(NOC_SOLUTION_PATH, "noc_sol_compute.ncr")) + # shutil.copy(os.path.join(AVED_ROOT_DIR_COMPUTE, "noc_sol.ncr"), os.path.join(RESOURCES_PATH, "noc_sol_compute.ncr")) # elif args.platform == "eth": # print("Eth mode not supported yet.") diff --git a/deploy/base_pdi/tcl/export_noc.tcl b/deploy/base_pdi/tcl/export_noc.tcl index 685c299..d8dc8d4 100644 --- a/deploy/base_pdi/tcl/export_noc.tcl +++ b/deploy/base_pdi/tcl/export_noc.tcl @@ -20,7 +20,7 @@ set build_dir "build" open_project $build_dir/prj.xpr -open_run impl_1 +open_run synth_1 set_property lock true [get_noc_net_routes -of [get_noc_logical_paths -filter {initial_boot == 1}]] write_noc_solution -file noc_sol.ncr exit \ No newline at end of file diff --git a/smi/include/commands/inspect_command.hpp b/smi/include/commands/inspect_command.hpp index 0fac18d..1dd329f 100644 --- a/smi/include/commands/inspect_command.hpp +++ b/smi/include/commands/inspect_command.hpp @@ -28,8 +28,8 @@ #include "utils/vrtbin.hpp" -#define INSPECT_SYSTEM_MAP_PATH "/tmp/system_map.xml" ///< Path to the system map XML file -#define INSPECT_VERSION_PATH "/tmp/version.json" ///< Path to the version JSON file +#define INSPECT_SYSTEM_MAP_PATH ((FilesystemCache::getCachePath() / "system_map.xml").c_str()) ///< Path to the system map XML file +#define INSPECT_VERSION_PATH ((FilesystemCache::getCachePath() / "version.json").c_str()) ///< Path to the version JSON file /** * @brief Class for inspecting VRTBIN files. diff --git a/smi/include/utils/filesystem_cache.hpp b/smi/include/utils/filesystem_cache.hpp new file mode 100644 index 0000000..372de0f --- /dev/null +++ b/smi/include/utils/filesystem_cache.hpp @@ -0,0 +1,77 @@ +/** + * The MIT License (MIT) + * 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 FILESYSTEM_CACHE_HPP +#define FILESYSTEM_CACHE_HPP + +#include + +/** + * @brief Static class for managing the filesystem cache - i.e. where trivial temporary files can be stored. + * + * There are two directories with different pourposes: + * 1. Runtime directory, generally stored as tmpfs on Linux (in RAM). Locks or really small files go here. + * 2. Cache directory, generally stored on disk. + * + * For more information see: https://specifications.freedesktop.org/basedir-spec/0.8/ + * + * SLASH uses subdirectories within each of the main directories, and smi uses a subdirectory within the SLASH + * directory. + * + * See each function for a description of paths used and what enviornment variables to set to override these choices. + * However, the default behaviour (where the user sets nothing extra) should be reasonable for most Linux installations. + */ +class FilesystemCache { + static void ensureDirExists(const std::filesystem::path& path); + public: + /** + * @brief Disable construction of static class. + */ + FilesystemCache() = delete; + + /** + * @brief Get path to the cache directory. + * + * This function creates the cache directory if it does not exist. + * + * The following paths are used: + * + * 1. $SLASH_CACHE_PATH/smi, if $SLASH_CACHE_PATH is set in the environment. + * 2. $XDG_CACHE_HOME/SLASH/smi, if $XDG_CACHE_HOME is set. + * 3. $HOME/.cache/SLASH/smi, if $HOME is set. + * 4. /tmp/SLASH-cache-/smi, as a final fallback. + */ + static std::filesystem::path getCachePath(); + + /** + * @brief Get path to the runtime directory. + * + * This function creates the runtime directory if it does not exist. + * + * The following paths are used: + * + * 1. $SLASH_RUNTIME_PATH/smi, if $SLASH_RUNTIME_PATH is set in the environment. + * 2. $XDG_RUNTIME_DIR/SLASH/smi, if $XDG_RUNTIME_DIR is set. + * 3. /tmp/SLASH-run-/smi, as a final fallback. + */ + static std::filesystem::path getRuntimePath(); +}; + +#endif // FILESYSTEM_CACHE_HPP diff --git a/smi/src/commands/inspect_command.cpp b/smi/src/commands/inspect_command.cpp index bc7dbd0..242e409 100644 --- a/smi/src/commands/inspect_command.cpp +++ b/smi/src/commands/inspect_command.cpp @@ -19,11 +19,12 @@ */ #include "commands/inspect_command.hpp" +#include "utils/filesystem_cache.hpp" InspectCommand::InspectCommand(const std::string& image_path) : imagePath(image_path) {} void InspectCommand::execute() { - Vrtbin::extract(this->imagePath, "/tmp"); + Vrtbin::extract(this->imagePath, FilesystemCache::getCachePath()); queryMetadata(); } diff --git a/smi/src/commands/partial_program_command.cpp b/smi/src/commands/partial_program_command.cpp index 9e85188..f813e39 100644 --- a/smi/src/commands/partial_program_command.cpp +++ b/smi/src/commands/partial_program_command.cpp @@ -20,6 +20,8 @@ #include "commands/partial_program_command.hpp" +#include "utils/filesystem_cache.hpp" + PartialProgramCommand::PartialProgramCommand(const std::string& device, const std::string& image_path) { this->device = device; @@ -45,15 +47,15 @@ void PartialProgramCommand::execute() { ami_dev_get_pci_bdf(dev, &dev_bdf); if (ArgParser::endsWith(this->imagePath, ".vrtbin")) { - Vrtbin::extract(this->imagePath, "/tmp"); + Vrtbin::extract(this->imagePath, FilesystemCache::getCachePath()); std::string ami_path = std::string(std::getenv("AMI_HOME")); std::string create_path = "mkdir -p " + ami_path + "/" + device + ":00.0/"; std::string basePath = ami_path + "/" + device + ":00.0/"; system(create_path.c_str()); - Vrtbin::copy("/tmp/system_map.xml", basePath + "system_map.xml"); - Vrtbin::copy("/tmp/version.json", basePath + "version.json"); - Vrtbin::copy("/tmp/report_utilization.xml", basePath + "report_utilization.xml"); - imagePath = "/tmp/design.pdi"; + Vrtbin::copy(FilesystemCache::getCachePath() / "system_map.xml", basePath + "system_map.xml"); + Vrtbin::copy(FilesystemCache::getCachePath() / "version.json", basePath + "version.json"); + Vrtbin::copy(FilesystemCache::getCachePath() / "report_utilization.xml", basePath + "report_utilization.xml"); + imagePath = FilesystemCache::getCachePath() / "design.pdi"; } int ret = ami_prog_device_boot(&dev, 1); // segmented PDI is on partition 1 diff --git a/smi/src/commands/program_command.cpp b/smi/src/commands/program_command.cpp index cdfb4eb..eed5f63 100644 --- a/smi/src/commands/program_command.cpp +++ b/smi/src/commands/program_command.cpp @@ -19,6 +19,7 @@ */ #include "commands/program_command.hpp" +#include "utils/filesystem_cache.hpp" ProgramCommand::ProgramCommand(const std::string& device, const std::string& image_path, uint8_t partition) { @@ -42,15 +43,15 @@ void ProgramCommand::execute() { ArgParser::endsWith(this->imagePath, ".pdi") ? ImageType::PDI : ImageType::VRTBIN; if (extension == ImageType::VRTBIN) { - Vrtbin::extract(this->imagePath, "/tmp"); + Vrtbin::extract(this->imagePath, FilesystemCache::getCachePath()); std::string ami_path = std::string(std::getenv("AMI_HOME")); std::string create_path = "mkdir -p " + ami_path + "/" + device + ":00.0/"; std::string basePath = ami_path + "/" + device + ":00.0/"; system(create_path.c_str()); - Vrtbin::copy("/tmp/system_map.xml", basePath + "system_map.xml"); - Vrtbin::copy("/tmp/version.json", basePath + "version.json"); - Vrtbin::copy("/tmp/report_utilization.xml", basePath + "report_utilization.xml"); - imagePath = "/tmp/design.pdi"; + Vrtbin::copy(FilesystemCache::getCachePath() / "system_map.xml", basePath + "system_map.xml"); + Vrtbin::copy(FilesystemCache::getCachePath() / "version.json", basePath + "version.json"); + Vrtbin::copy(FilesystemCache::getCachePath() / "report_utilization.xml", basePath + "report_utilization.xml"); + imagePath = FilesystemCache::getCachePath() / "design.pdi"; } uint16_t dev_bdf; diff --git a/smi/src/utils/filesystem_cache.cpp b/smi/src/utils/filesystem_cache.cpp new file mode 100644 index 0000000..31b59a2 --- /dev/null +++ b/smi/src/utils/filesystem_cache.cpp @@ -0,0 +1,84 @@ +/** + * The MIT License (MIT) + * 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 "utils/filesystem_cache.hpp" + +#include +#include +#include + +std::filesystem::path FilesystemCache::getCachePath() { + std::filesystem::path path; + + // 1. Try $SLASH_CACHE_PATH + if (const char* slashCache = std::getenv("SLASH_CACHE_PATH")) { + path = slashCache; + } + + // 2. Try $XDG_CACHE_HOME/SLASH + else if (const char* xdgCache = std::getenv("XDG_CACHE_HOME")) { + path = std::filesystem::path(xdgCache) / "SLASH" / "smi"; + } + + // 3. Try $HOME/.cache/SLASH + else if (const char* home = std::getenv("HOME")) { + path = std::filesystem::path(home) / ".cache" / "SLASH" / "smi"; + } + + // 4. Fallback: /tmp/SLASH-cache- + else { + path = "/tmp/SLASH-cache-" + std::to_string(getuid()) + "/smi"; + } + + ensureDirExists(path); + + return path; +} + +std::filesystem::path FilesystemCache::getRuntimePath() { + std::filesystem::path path; + + // 1. Try $SLASH_RUNTIME_PATH + if (const char* slashCache = std::getenv("SLASH_RUNTIME_PATH")) { + path = slashCache; + } + + // 2. Try XDG_RUNTIME_DIR/SLASH + else if (const char* xdgCache = std::getenv("XDG_RUNTIME_DIR")) { + path = std::filesystem::path(xdgCache) / "SLASH" / "smi"; + } + + // 3. Fallback: /tmp/SLASH-run- + else { + path = "/tmp/SLASH-run-" + std::to_string(getuid()) + "/smi"; + } + + ensureDirExists(path); + + return path; +} + +void FilesystemCache::ensureDirExists(const std::filesystem::path& path) { + std::error_code ec; + std::filesystem::create_directories(path, ec); + if (ec) { + throw std::system_error(ec); + } +} diff --git a/smi/src/utils/vrtbin.cpp b/smi/src/utils/vrtbin.cpp index 58f42a3..b5e3e45 100644 --- a/smi/src/utils/vrtbin.cpp +++ b/smi/src/utils/vrtbin.cpp @@ -19,6 +19,7 @@ */ #include "utils/vrtbin.hpp" +#include "utils/filesystem_cache.hpp" void Vrtbin::extract(std::string source, std::string destination) { std::string command = "tar -xvf " + source + " -C " + destination + " 2>&1"; @@ -63,7 +64,7 @@ void Vrtbin::copy(const std::string& source, const std::string& destination) { std::string Vrtbin::extractUUID() { std::string uuid; - std::ifstream jsonFile("/tmp/version.json"); + std::ifstream jsonFile(FilesystemCache::getCachePath() / "version.json"); if (!jsonFile.is_open()) { uuid = ""; } diff --git a/submodules/v80-vitis-flow/include/sw_emu/arg.hpp b/submodules/v80-vitis-flow/include/sw_emu/arg.hpp index 2dab39c..f574ed4 100644 --- a/submodules/v80-vitis-flow/include/sw_emu/arg.hpp +++ b/submodules/v80-vitis-flow/include/sw_emu/arg.hpp @@ -22,6 +22,7 @@ #define ARG_HPP #include +#include /** * @brief Class representing a function argument. @@ -63,4 +64,4 @@ class Arg { std::string getArgType(); }; -#endif // ARG_HPP \ No newline at end of file +#endif // ARG_HPP diff --git a/submodules/v80-vitis-flow/src/bd_builder/bd_builder.cpp b/submodules/v80-vitis-flow/src/bd_builder/bd_builder.cpp index 7395e22..f401881 100644 --- a/submodules/v80-vitis-flow/src/bd_builder/bd_builder.cpp +++ b/submodules/v80-vitis-flow/src/bd_builder/bd_builder.cpp @@ -179,7 +179,14 @@ void BdBuilder::buildBlockDesign() { // blockDesignFile << assignQdmaLogicGpioAddr() << std::endl; blockDesignFile << "assign_bd_address" << std::endl; - if (segmented) blockDesignFile << setSegmented() << std::endl; + if (segmented) { + blockDesignFile << "set_property segmented_configuration true [current_project]\n"; + try { + blockDesignFile << setSegmented() << std::endl; + } catch (...){ + utils::Logger::log(utils::LogLevel::INFO, __PRETTY_FUNCTION__, "Segmented not set"); + } + } blockDesignFile << printFooter(); blockDesignFile.close(); @@ -859,6 +866,7 @@ std::string BdBuilder::setupSysRst() { } std::string BdBuilder::printFooter() { + utils::Logger::log(utils::LogLevel::INFO, __PRETTY_FUNCTION__, "Print TCL footer"); std::stringstream ss; ss << "}\n"; return ss.str(); @@ -920,8 +928,7 @@ std::string BdBuilder::setSegmented() { throw std::runtime_error("Failed to resolve path to " + std::string(NOC_SOLUTION)); } std::stringstream ss; - ss << "set_property segmented_configuration true [current_project]\n" - << "set_property NOC_SOLUTION_FILE " << std::string(resolvedPath) << " [get_runs impl_1]\n"; + ss << "set_property NOC_SOLUTION_FILE " << std::string(resolvedPath) << " [get_runs impl_1]\n"; return ss.str(); } diff --git a/vrt/include/api/vrtbin.hpp b/vrt/include/api/vrtbin.hpp index 60981b5..eecca0b 100644 --- a/vrt/include/api/vrtbin.hpp +++ b/vrt/include/api/vrtbin.hpp @@ -32,6 +32,7 @@ #include "parser/xml_parser.hpp" #include "utils/logger.hpp" #include "utils/platform.hpp" +#include "utils/filesystem_cache.hpp" namespace vrt { @@ -39,15 +40,15 @@ namespace vrt { * @brief Class for handling VRTBIN operations. */ class Vrtbin { - std::string vrtbinPath; ///< Path to the VRTBIN file - std::string systemMapPath; ///< Path to the system map file - std::string versionPath; ///< Path to the version file - std::string pdiPath; ///< Path to the PDI file - std::string uuid; ///< UUID of the VRTBIN - std::string tempExtractPath = "/tmp"; ///< Temporary extraction path - std::string emulationExecPath; ///< Path to the emulation executable - std::string simulationExecPath; ///< Path to the simulation executable - Platform platform; ///< Platform type + std::string vrtbinPath; ///< Path to the VRTBIN file + std::string systemMapPath; ///< Path to the system map file + std::string versionPath; ///< Path to the version file + std::string pdiPath; ///< Path to the PDI file + std::string uuid; ///< UUID of the VRTBIN + std::string tempExtractPath = FilesystemCache::getCachePath(); ///< Temporary extraction path + std::string emulationExecPath; ///< Path to the emulation executable + std::string simulationExecPath; ///< Path to the simulation executable + Platform platform; ///< Platform type /** * @brief Copies a file from source to destination. * @param source The source file path. diff --git a/vrt/include/utils/filesystem_cache.hpp b/vrt/include/utils/filesystem_cache.hpp new file mode 100644 index 0000000..cee8b30 --- /dev/null +++ b/vrt/include/utils/filesystem_cache.hpp @@ -0,0 +1,77 @@ +/** + * The MIT License (MIT) + * 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 FILESYSTEM_CACHE_HPP +#define FILESYSTEM_CACHE_HPP + +#include + +/** + * @brief Static class for managing the filesystem cache - i.e. where trivial temporary files can be stored. + * + * There are two directories with different pourposes: + * 1. Runtime directory, generally stored as tmpfs on Linux (in RAM). Locks or really small files go here. + * 2. Cache directory, generally stored on disk. + * + * For more information see: https://specifications.freedesktop.org/basedir-spec/0.8/ + * + * SLASH uses subdirectories within each of the main directories, and vrt uses a subdirectory within the SLASH + * directory. + * + * See each function for a description of paths used and what enviornment variables to set to override these choices. + * However, the default behaviour (where the user sets nothing extra) should be reasonable for most Linux installations. + */ +class FilesystemCache { + static void ensureDirExists(const std::filesystem::path& path); + public: + /** + * @brief Disable construction of static class. + */ + FilesystemCache() = delete; + + /** + * @brief Get path to the cache directory. + * + * This function creates the cache directory if it does not exist. + * + * The following paths are used: + * + * 1. $SLASH_CACHE_PATH/vrt, if $SLASH_CACHE_PATH is set in the environment. + * 2. $XDG_CACHE_HOME/SLASH/vrt, if $XDG_CACHE_HOME is set. + * 3. $HOME/.cache/SLASH/vrt, if $HOME is set. + * 4. /tmp/SLASH-cache-/vrt, as a final fallback. + */ + static std::filesystem::path getCachePath(); + + /** + * @brief Get path to the runtime directory. + * + * This function creates the runtime directory if it does not exist. + * + * The following paths are used: + * + * 1. $SLASH_RUNTIME_PATH/vrt, if $SLASH_RUNTIME_PATH is set in the environment. + * 2. $XDG_RUNTIME_DIR/SLASH/vrt, if $XDG_RUNTIME_DIR is set. + * 3. /tmp/SLASH-run-/vrt, as a final fallback. + */ + static std::filesystem::path getRuntimePath(); +}; + +#endif // FILESYSTEM_CACHE_HPP diff --git a/vrt/src/api/device.cpp b/vrt/src/api/device.cpp index ca1202c..46983c8 100644 --- a/vrt/src/api/device.cpp +++ b/vrt/src/api/device.cpp @@ -20,6 +20,8 @@ #include "api/device.hpp" +#include "utils/filesystem_cache.hpp" + namespace vrt { Device::Device(const std::string& bdf, const std::string& vrtbinPath, bool program, @@ -376,7 +378,7 @@ Allocator* Device::getAllocator() { return allocator; } std::vector Device::getQdmaInterfaces() { return qdmaIntfs; } void Device::lockPcieDevice(const std::string& bdf) { - std::string lockFile = "/tmp/pcie_device_" + bdf + ".lock"; + std::string lockFile = FilesystemCache::getRuntimePath() / ("pcie_device_" + bdf + ".lock"); int fd = open(lockFile.c_str(), O_CREAT | O_WRONLY, 0666); if (fd == -1) { throw std::runtime_error("Failed to lock PCIe device " + bdf); @@ -389,7 +391,7 @@ void Device::lockPcieDevice(const std::string& bdf) { } void Device::unlockPcieDevice(const std::string& bdf) { - std::string lockFile = "/tmp/pcie_device_" + bdf + ".lock"; + std::string lockFile = FilesystemCache::getRuntimePath() / ("pcie_device_" + bdf + ".lock"); int fd = open(lockFile.c_str(), O_WRONLY, 0666); if (fd == -1) { throw std::runtime_error("Failed to lock PCIe device " + bdf); diff --git a/vrt/src/utils/filesystem_cache.cpp b/vrt/src/utils/filesystem_cache.cpp new file mode 100644 index 0000000..aaf5423 --- /dev/null +++ b/vrt/src/utils/filesystem_cache.cpp @@ -0,0 +1,84 @@ +/** + * The MIT License (MIT) + * 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 "utils/filesystem_cache.hpp" + +#include +#include +#include + +std::filesystem::path FilesystemCache::getCachePath() { + std::filesystem::path path; + + // 1. Try $SLASH_CACHE_PATH + if (const char* slashCache = std::getenv("SLASH_CACHE_PATH")) { + path = slashCache; + } + + // 2. Try $XDG_CACHE_HOME/SLASH + else if (const char* xdgCache = std::getenv("XDG_CACHE_HOME")) { + path = std::filesystem::path(xdgCache) / "SLASH" / "vrt"; + } + + // 3. Try $HOME/.cache/SLASH + else if (const char* home = std::getenv("HOME")) { + path = std::filesystem::path(home) / ".cache" / "SLASH" / "vrt"; + } + + // 4. Fallback: /tmp/SLASH-cache- + else { + path = "/tmp/SLASH-cache-" + std::to_string(getuid()) + "/vrt"; + } + + ensureDirExists(path); + + return path; +} + +std::filesystem::path FilesystemCache::getRuntimePath() { + std::filesystem::path path; + + // 1. Try $SLASH_RUNTIME_PATH + if (const char* slashCache = std::getenv("SLASH_RUNTIME_PATH")) { + path = slashCache; + } + + // 2. Try XDG_RUNTIME_DIR/SLASH + else if (const char* xdgCache = std::getenv("XDG_RUNTIME_DIR")) { + path = std::filesystem::path(xdgCache) / "SLASH" / "vrt"; + } + + // 3. Fallback: /tmp/SLASH-run- + else { + path = "/tmp/SLASH-run-" + std::to_string(getuid()) + "/vrt"; + } + + ensureDirExists(path); + + return path; +} + +void FilesystemCache::ensureDirExists(const std::filesystem::path& path) { + std::error_code ec; + std::filesystem::create_directories(path, ec); + if (ec) { + throw std::system_error(ec); + } +}