Restructure the folder
Move rocm_smi related function to rocm_smi folder. Move amd_smi to top level include/ and src/ folder. Remove obsolte oam folder. Change the CMakeLists.txt to update folder locations. Change-Id: I52e6be739e49f3b0545865f25364787f5985e9c3
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include "amd_smi/impl/amd_smi_common.h"
|
||||
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <sys/types.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <xf86drm.h>
|
||||
#include <string.h>
|
||||
#include <memory>
|
||||
#include "amd_smi/impl/amd_smi_drm.h"
|
||||
#include "amd_smi/impl/amdgpu_drm.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::init() {
|
||||
// A few RAII handler
|
||||
using dir_ptr = std::unique_ptr<DIR, decltype(&closedir)>;
|
||||
using drm_version_ptr = std::unique_ptr<drmVersion,
|
||||
decltype(&drmFreeVersion)>;
|
||||
|
||||
struct dirent *dir = nullptr;
|
||||
int fd = -1;
|
||||
|
||||
amdsmi_status_t status = lib_loader_.load("libdrm.so");
|
||||
if (status != AMDSMI_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
// load symbol from libdrm
|
||||
drm_cmd_write_ = nullptr;
|
||||
status = lib_loader_.load_symbol(&drm_cmd_write_, "drmCommandWrite");
|
||||
if (status != AMDSMI_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
using drmGetVersionType = drmVersionPtr (*)(int); // drmGetVersion
|
||||
using drmFreeVersionType = void (*)(drmVersionPtr); // drmFreeVersion
|
||||
drmGetVersionType drm_get_version = nullptr;
|
||||
drmFreeVersionType drm_free_version = nullptr;
|
||||
status = lib_loader_.load_symbol(&drm_get_version, "drmGetVersion");
|
||||
if (status != AMDSMI_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
status = lib_loader_.load_symbol(&drm_free_version, "drmFreeVersion");
|
||||
if (status != AMDSMI_STATUS_SUCCESS) {
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
auto d = dir_ptr(opendir("/dev/dri/"), &closedir);
|
||||
if (d == nullptr) return AMDSMI_STATUS_NOT_INIT;
|
||||
|
||||
while ((dir = readdir(d.get())) != NULL) {
|
||||
char* name_cstr = new char[sizeof(dir->d_name) + 10];
|
||||
auto name = std::unique_ptr<char[]>(name_cstr);
|
||||
|
||||
snprintf(name.get(), sizeof(dir->d_name) + 10,
|
||||
"/dev/dri/%s", dir->d_name);
|
||||
fd = open(name.get(), O_RDWR | O_CLOEXEC);
|
||||
if (fd < 0) continue;
|
||||
|
||||
auto version = drm_version_ptr(drm_get_version(fd), drm_free_version);
|
||||
if (strcmp("amdgpu", version->name) ||
|
||||
strstr(name.get(), "render") == nullptr) {
|
||||
close(fd);
|
||||
continue;
|
||||
}
|
||||
|
||||
drm_fds_.push_back(fd);
|
||||
}
|
||||
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::cleanup() {
|
||||
for (unsigned int i=0; i < drm_fds_.size(); i++) {
|
||||
close(drm_fds_[i]);
|
||||
}
|
||||
drm_fds_.clear();
|
||||
lib_loader_.unload();
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::amdgpu_query_info(int fd, unsigned info_id,
|
||||
unsigned size, void *value) {
|
||||
if (drm_cmd_write_ == nullptr) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
std::lock_guard<std::mutex> guard(drm_mutex_);
|
||||
|
||||
struct drm_amdgpu_info request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.return_pointer = (uintptr_t)value;
|
||||
request.return_size = size;
|
||||
request.query = info_id;
|
||||
int status = drm_cmd_write_(fd, DRM_AMDGPU_INFO,
|
||||
&request, sizeof(struct drm_amdgpu_info));
|
||||
if (status == 0) return AMDSMI_STATUS_SUCCESS;
|
||||
return AMDSMI_STATUS_DRM_ERROR;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::amdgpu_query_fw(int fd, unsigned info_id,
|
||||
unsigned fw_type, unsigned size, void *value) {
|
||||
if (drm_cmd_write_ == nullptr) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
std::lock_guard<std::mutex> guard(drm_mutex_);
|
||||
|
||||
struct drm_amdgpu_info request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.return_pointer = (uintptr_t)value;
|
||||
request.return_size = size;
|
||||
request.query = info_id;
|
||||
request.query_fw.fw_type = fw_type;
|
||||
int status = drm_cmd_write_(fd, DRM_AMDGPU_INFO, &request,
|
||||
sizeof(struct drm_amdgpu_info));
|
||||
if (status == 0) return AMDSMI_STATUS_SUCCESS;
|
||||
return AMDSMI_STATUS_DRM_ERROR;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::amdgpu_query_hw_ip(int fd, unsigned info_id,
|
||||
unsigned hw_ip_type, unsigned size, void *value) {
|
||||
if (drm_cmd_write_ == nullptr) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
std::lock_guard<std::mutex> guard(drm_mutex_);
|
||||
|
||||
struct drm_amdgpu_info request;
|
||||
memset(&request, 0, sizeof(request));
|
||||
request.return_pointer = (uintptr_t)value;
|
||||
request.return_size = size;
|
||||
request.query = info_id;
|
||||
request.query_hw_ip.type = hw_ip_type;
|
||||
int status = drm_cmd_write_(fd, DRM_AMDGPU_INFO, &request,
|
||||
sizeof(struct drm_amdgpu_info));
|
||||
if (status == 0) return AMDSMI_STATUS_SUCCESS;
|
||||
return AMDSMI_STATUS_DRM_ERROR;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiDrm::amdgpu_query_vbios(int fd, void *info) {
|
||||
if (drm_cmd_write_ == nullptr) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
std::lock_guard<std::mutex> guard(drm_mutex_);
|
||||
|
||||
struct drm_amdgpu_info request;
|
||||
memset(&request, 0, sizeof request);
|
||||
request.return_pointer = (uint64_t) info;
|
||||
request.return_size = sizeof(drm_amdgpu_info_vbios);
|
||||
request.query = AMDGPU_INFO_VBIOS;
|
||||
request.vbios_info.type = AMDGPU_INFO_VBIOS_INFO;
|
||||
int status = drm_cmd_write_(fd, DRM_AMDGPU_INFO, &request,
|
||||
sizeof(struct drm_amdgpu_info));
|
||||
if (status == 0) return AMDSMI_STATUS_SUCCESS;
|
||||
return AMDSMI_STATUS_DRM_ERROR;
|
||||
}
|
||||
|
||||
|
||||
int AMDSmiDrm::get_drm_fd_by_index(uint32_t gpu_index) const {
|
||||
if (gpu_index + 1 > drm_fds_.size()) return -1;
|
||||
return drm_fds_[gpu_index];
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include "amd_smi/impl/amd_smi_gpu_device.h"
|
||||
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
uint32_t AMDSmiGPUDevice::get_gpu_id() const {
|
||||
return gpu_id_;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiGPUDevice::amdgpu_query_info(unsigned info_id,
|
||||
unsigned size, void *value) const {
|
||||
int fd = drm_.get_drm_fd_by_index(gpu_id_);
|
||||
if (fd == -1) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
return drm_.amdgpu_query_info(fd, info_id, size, value);
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiGPUDevice::amdgpu_query_hw_ip(unsigned info_id,
|
||||
unsigned hw_ip_type, unsigned size, void *value) const {
|
||||
int fd = drm_.get_drm_fd_by_index(gpu_id_);
|
||||
if (fd == -1) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
return drm_.amdgpu_query_hw_ip(fd, info_id, hw_ip_type, size, value);
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiGPUDevice::amdgpu_query_fw(unsigned info_id,
|
||||
unsigned fw_type, unsigned size, void *value) const {
|
||||
int fd = drm_.get_drm_fd_by_index(gpu_id_);
|
||||
if (fd == -1) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
return drm_.amdgpu_query_fw(fd, info_id, fw_type, size, value);
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiGPUDevice::amdgpu_query_vbios(void *info) const {
|
||||
int fd = drm_.get_drm_fd_by_index(gpu_id_);
|
||||
if (fd == -1) return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
|
||||
return drm_.amdgpu_query_vbios(fd, info);
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "amd_smi/impl/amd_smi_lib_loader.h"
|
||||
#include <iostream>
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
AMDSmiLibraryLoader::AMDSmiLibraryLoader(): libHandler_(nullptr) {
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiLibraryLoader::load(const char* filename) {
|
||||
if (filename == nullptr) {
|
||||
return AMDSMI_STATUS_FAIL_LOAD_MODULE;
|
||||
}
|
||||
if (libHandler_) {
|
||||
unload();
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> guard(library_mutex_);
|
||||
libHandler_ = dlopen(filename, RTLD_LAZY);
|
||||
if (!libHandler_) {
|
||||
char* error = dlerror();
|
||||
std::cerr << "Fail to open " << filename <<": " << error
|
||||
<< std::endl;
|
||||
return AMDSMI_STATUS_FAIL_LOAD_MODULE;
|
||||
}
|
||||
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiLibraryLoader::unload() {
|
||||
std::lock_guard<std::mutex> guard(library_mutex_);
|
||||
if (libHandler_) {
|
||||
dlclose(libHandler_);
|
||||
libHandler_ = nullptr;
|
||||
}
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
AMDSmiLibraryLoader::~AMDSmiLibraryLoader() {
|
||||
unload();
|
||||
}
|
||||
|
||||
} // namespace rdc
|
||||
} // namespace amd
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include "amd_smi/impl/amd_smi_socket.h"
|
||||
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
AMDSmiSocket::~AMDSmiSocket() {
|
||||
for (uint32_t i = 0; i < devices_.size(); i++) {
|
||||
delete devices_[i];
|
||||
}
|
||||
devices_.clear();
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
#include "amd_smi/impl/amd_smi_system.h"
|
||||
#include "amd_smi/impl/amd_smi_gpu_device.h"
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
#define AMD_SMI_INIT_FLAG_RESRV_TEST1 0x800000000000000 //!< Reserved for test
|
||||
|
||||
amdsmi_status_t AMDSmiSystem::init(uint64_t flags) {
|
||||
init_flag_ = flags;
|
||||
// populate sockets and devices
|
||||
if (flags & AMDSMI_INIT_AMD_GPUS) {
|
||||
drm_.init();
|
||||
// init rsmi
|
||||
rsmi_status_t ret = rsmi_init(flags);
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return static_cast<amdsmi_status_t>(ret);
|
||||
}
|
||||
|
||||
uint32_t device_count = 0;
|
||||
ret = rsmi_num_monitor_devices(&device_count);
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return static_cast<amdsmi_status_t>(ret);
|
||||
}
|
||||
|
||||
for (uint32_t i=0; i < device_count; i++) {
|
||||
uint64_t bdfid = 0;
|
||||
ret = rsmi_dev_pci_id_get(i, &bdfid);
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return static_cast<amdsmi_status_t>(ret);
|
||||
}
|
||||
|
||||
uint64_t domain = (bdfid >> 32) & 0xffffffff;
|
||||
uint64_t bus = (bdfid >> 8) & 0xff;
|
||||
uint64_t device_id = (bdfid >> 3) & 0x1f;
|
||||
uint64_t function = bdfid & 0x7;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << std::setfill('0') << std::uppercase << std::hex
|
||||
<< std::setw(4) << domain << ":" << std::setw(2) << bus << ":"
|
||||
<< std::setw(2) << device_id << "." << std::setw(2) << function;
|
||||
|
||||
// Multiple devices may share the same socket
|
||||
auto socket_id = ss.str();
|
||||
AMDSmiSocket* socket = nullptr;
|
||||
for (unsigned int j=0; j < sockets_.size(); j++) {
|
||||
if (sockets_[j]->get_socket_id() == socket_id) {
|
||||
socket = sockets_[j];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (socket == nullptr) {
|
||||
socket = new AMDSmiSocket(ss.str());
|
||||
sockets_.push_back(socket);
|
||||
}
|
||||
|
||||
AMDSmiDevice* device = new AMDSmiGPUDevice(i, drm_);
|
||||
socket->add_device(device);
|
||||
devices_.insert(device);
|
||||
}
|
||||
} else {
|
||||
return AMDSMI_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiSystem::cleanup() {
|
||||
for (uint32_t i = 0; i < sockets_.size(); i++) {
|
||||
delete sockets_[i];
|
||||
}
|
||||
devices_.clear();
|
||||
sockets_.clear();
|
||||
init_flag_ = AMDSMI_INIT_ALL_DEVICES;
|
||||
rsmi_status_t ret = rsmi_shut_down();
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return static_cast<amdsmi_status_t>(ret);
|
||||
}
|
||||
|
||||
drm_.cleanup();
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiSystem::handle_to_socket(
|
||||
amdsmi_socket_handle socket_handle,
|
||||
AMDSmiSocket** socket) {
|
||||
if (socket_handle == nullptr || socket == nullptr) {
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
}
|
||||
*socket = static_cast<AMDSmiSocket*>(socket_handle);
|
||||
|
||||
// double check handlers is here
|
||||
if (std::find(sockets_.begin(), sockets_.end(), *socket)
|
||||
!= sockets_.end()) {
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiSystem::handle_to_device(
|
||||
amdsmi_device_handle device_handle,
|
||||
AMDSmiDevice** device) {
|
||||
if (device_handle == nullptr || device == nullptr) {
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
}
|
||||
*device = static_cast<AMDSmiDevice*>(device_handle);
|
||||
|
||||
// double check handlers is here
|
||||
if (std::find(devices_.begin(), devices_.end(), *device)
|
||||
!= devices_.end()) {
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
}
|
||||
|
||||
amdsmi_status_t AMDSmiSystem::gpu_index_to_handle(uint32_t gpu_index,
|
||||
amdsmi_device_handle* device_handle) {
|
||||
if (device_handle == nullptr)
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
|
||||
auto iter = devices_.begin();
|
||||
for (; iter != devices_.end(); iter++) {
|
||||
auto cur_device = (*iter);
|
||||
if (cur_device->get_device_type() != AMD_GPU)
|
||||
continue;
|
||||
amd::smi::AMDSmiGPUDevice* gpu_device =
|
||||
static_cast<amd::smi::AMDSmiGPUDevice*>(cur_device);
|
||||
uint32_t cur_gpu_index = gpu_device->get_gpu_id();
|
||||
if (gpu_index == cur_gpu_index) {
|
||||
*device_handle = cur_device;
|
||||
return AMDSMI_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
return AMDSMI_STATUS_INVAL;
|
||||
}
|
||||
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2017, Advanced Micro Devices, Inc.
|
||||
* Copyright (c) 2022, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
@@ -43,14 +43,14 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef INCLUDE_ROCM_SMI_ROCM_SMI64CONFIG_H_
|
||||
#define INCLUDE_ROCM_SMI_ROCM_SMI64CONFIG_H_
|
||||
#ifndef INCLUDE_AMD_SMI_AMD_SMI64CONFIG_H_
|
||||
#define INCLUDE_AMD_SMI_AMD_SMI64CONFIG_H_
|
||||
|
||||
// This file is generated on build.
|
||||
|
||||
#define rocm_smi_VERSION_MAJOR @rocm_smi_VERSION_MAJOR@
|
||||
#define rocm_smi_VERSION_MINOR @rocm_smi_VERSION_MINOR@
|
||||
#define rocm_smi_VERSION_PATCH @rocm_smi_VERSION_PATCH@
|
||||
#define rocm_smi_VERSION_BUILD "@rocm_smi_VERSION_BUILD@"
|
||||
#define amd_smi_VERSION_MAJOR @amd_smi_VERSION_MAJOR@
|
||||
#define amd_smi_VERSION_MINOR @amd_smi_VERSION_MINOR@
|
||||
#define amd_smi_VERSION_PATCH @amd_smi_VERSION_PATCH@
|
||||
#define amd_smi_VERSION_BUILD "@amd_smi_VERSION_BUILD@"
|
||||
|
||||
#endif // INCLUDE_ROCM_SMI_ROCM_SMI64CONFIG_H_
|
||||
#endif // INCLUDE_AMD_SMI_AMD_SMI64CONFIG_H_
|
||||
-4161
File diff suppressed because it is too large
Load Diff
@@ -1,438 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2019, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <linux/perf_event.h>
|
||||
#include <unistd.h>
|
||||
#include <asm/unistd.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
#include "rocm_smi/rocm_smi_counters.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_device.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
namespace evt {
|
||||
|
||||
static const char *kPathDeviceEventRoot = "/sys/bus/event_source/devices";
|
||||
|
||||
// Event group names
|
||||
static const char *kEvGrpDataFabricFName = "amdgpu_df_#";
|
||||
static const char *kEvGrpAmdGpuFName = "amdgpu_#";
|
||||
|
||||
// Data Fabric event file names
|
||||
static const char *kDFEvtCake0FtiReqAllocFName = "cake0_ftiinstat_reqalloc";
|
||||
static const char *kDFEvtCake0FtiRspAllocFName = "cake0_ftiinstat_rspalloc";
|
||||
static const char *kDFEvtCake0PcsOutTxDataFName = "cake0_pcsout_txdata";
|
||||
static const char *kDFEvtCake0PcsOutTxMetaFName = "cake0_pcsout_txmeta";
|
||||
static const char *kDFEvtCake1FtiReqAllocFName = "cake1_ftiinstat_reqalloc";
|
||||
static const char *kDFEvtCake1FtiRspAllocFName = "cake1_ftiinstat_rspalloc";
|
||||
static const char *kDFEvtCake1PcsOutTxDataFName = "cake1_pcsout_txdata";
|
||||
static const char *kDFEvtCake1PcsOutTxMetaFName = "cake1_pcsout_txmeta";
|
||||
|
||||
// XGMI Data Outbound event file names
|
||||
static const char *kXGMIDOutBound0FName = "xgmi_link0_data_outbound";
|
||||
static const char *kXGMIDOutBound1FName = "xgmi_link1_data_outbound";
|
||||
static const char *kXGMIDOutBound2FName = "xgmi_link2_data_outbound";
|
||||
static const char *kXGMIDOutBound3FName = "xgmi_link3_data_outbound";
|
||||
static const char *kXGMIDOutBound4FName = "xgmi_link4_data_outbound";
|
||||
static const char *kXGMIDOutBound5FName = "xgmi_link5_data_outbound";
|
||||
|
||||
|
||||
static const std::map<rsmi_event_type_t, const char *> kEventFNameMap = {
|
||||
{RSMI_EVNT_XGMI_0_NOP_TX, kDFEvtCake0PcsOutTxMetaFName},
|
||||
{RSMI_EVNT_XGMI_0_REQUEST_TX, kDFEvtCake0FtiReqAllocFName},
|
||||
{RSMI_EVNT_XGMI_0_RESPONSE_TX, kDFEvtCake0FtiRspAllocFName},
|
||||
{RSMI_EVNT_XGMI_0_BEATS_TX, kDFEvtCake0PcsOutTxDataFName},
|
||||
{RSMI_EVNT_XGMI_1_NOP_TX, kDFEvtCake1PcsOutTxMetaFName},
|
||||
{RSMI_EVNT_XGMI_1_REQUEST_TX, kDFEvtCake1FtiReqAllocFName},
|
||||
{RSMI_EVNT_XGMI_1_RESPONSE_TX, kDFEvtCake1FtiRspAllocFName},
|
||||
{RSMI_EVNT_XGMI_1_BEATS_TX, kDFEvtCake1PcsOutTxDataFName},
|
||||
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_0, kXGMIDOutBound0FName},
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_1, kXGMIDOutBound1FName},
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_2, kXGMIDOutBound2FName},
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_3, kXGMIDOutBound3FName},
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_4, kXGMIDOutBound4FName},
|
||||
{RSMI_EVNT_XGMI_DATA_OUT_5, kXGMIDOutBound5FName},
|
||||
};
|
||||
|
||||
static const std::map<rsmi_event_group_t, const char *> kEvtGrpFNameMap = {
|
||||
{RSMI_EVNT_GRP_XGMI, kEvGrpDataFabricFName},
|
||||
{RSMI_EVNT_GRP_XGMI_DATA_OUT, kEvGrpAmdGpuFName},
|
||||
{RSMI_EVNT_GRP_INVALID, "bogus"},
|
||||
};
|
||||
|
||||
static rsmi_event_group_t EvtGrpFromEvtID(rsmi_event_type_t evnt) {
|
||||
#define EVNT_GRP_RANGE_CHK(EVGRP_SHORT, EVGRP_ENUM) \
|
||||
if (evnt >= RSMI_EVNT_##EVGRP_SHORT##_FIRST && \
|
||||
evnt <= RSMI_EVNT_##EVGRP_SHORT##_LAST) { \
|
||||
return EVGRP_ENUM; \
|
||||
}
|
||||
EVNT_GRP_RANGE_CHK(XGMI, RSMI_EVNT_GRP_XGMI);
|
||||
EVNT_GRP_RANGE_CHK(XGMI_DATA_OUT, RSMI_EVNT_GRP_XGMI_DATA_OUT);
|
||||
|
||||
return RSMI_EVNT_GRP_INVALID;
|
||||
}
|
||||
|
||||
// Note below that dev_num is not the same as the usual dv_ind.
|
||||
// dev_num is the number of the device (e.g., 1 for card1) whereas dv_ind
|
||||
// is usually the index into the vector of devices
|
||||
void
|
||||
GetSupportedEventGroups(uint32_t dev_num, dev_evt_grp_set_t *supported_grps) {
|
||||
assert(supported_grps != nullptr);
|
||||
|
||||
std::string grp_path_base;
|
||||
std::string grp_path;
|
||||
int32_t ret;
|
||||
|
||||
grp_path_base = kPathDeviceEventRoot;
|
||||
grp_path_base += '/';
|
||||
struct stat file_stat;
|
||||
|
||||
for (auto g : kEvtGrpFNameMap) {
|
||||
grp_path = grp_path_base;
|
||||
grp_path += g.second;
|
||||
|
||||
std::replace(grp_path.begin(), grp_path.end(), '#',
|
||||
static_cast<char>('0' + dev_num));
|
||||
|
||||
ret = stat(grp_path.c_str(), &file_stat);
|
||||
|
||||
if (ret) {
|
||||
assert(errno == ENOENT);
|
||||
continue;
|
||||
}
|
||||
if (S_ISDIR(file_stat.st_mode)) {
|
||||
supported_grps->insert(g.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
// /sys/bus/event_source/devices/<hw block>_<instance>/type
|
||||
Event::Event(rsmi_event_type_t event, uint32_t dev_ind) :
|
||||
event_type_(event), prev_cntr_val_(0) {
|
||||
fd_ = -1;
|
||||
rsmi_event_group_t grp = EvtGrpFromEvtID(event);
|
||||
assert(grp != RSMI_EVNT_GRP_INVALID); // This should have failed before now
|
||||
|
||||
evt_path_root_ = kPathDeviceEventRoot;
|
||||
evt_path_root_ += '/';
|
||||
evt_path_root_ += kEvtGrpFNameMap.at(grp);
|
||||
|
||||
|
||||
amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance();
|
||||
assert(dev_ind < smi.devices().size());
|
||||
std::shared_ptr<amd::smi::Device> dev = smi.devices()[dev_ind];
|
||||
assert(dev != nullptr);
|
||||
|
||||
|
||||
dev_ind_ = dev_ind;
|
||||
dev_file_ind_ = dev->index();
|
||||
std::replace(evt_path_root_.begin(), evt_path_root_.end(), '#',
|
||||
static_cast<char>('0' + dev_file_ind_));
|
||||
}
|
||||
Event::~Event(void) {
|
||||
int ret;
|
||||
if (fd_ != -1) {
|
||||
ret = close(fd_);
|
||||
|
||||
if (ret == -1) {
|
||||
perror("Failed to close file descriptor.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
parse_field_config(std::string fstr, evnt_info_t *val) {
|
||||
std::stringstream ss(fstr);
|
||||
std::stringstream fs;
|
||||
std::string config_ln;
|
||||
std::string field_name;
|
||||
uint32_t start_bit;
|
||||
uint32_t end_bit;
|
||||
char jnk;
|
||||
|
||||
assert(val != nullptr);
|
||||
|
||||
getline(ss, config_ln, ':');
|
||||
ss >> start_bit;
|
||||
ss >> jnk;
|
||||
assert(jnk == '-');
|
||||
ss >> end_bit;
|
||||
|
||||
if (start_bit > end_bit ||
|
||||
start_bit > 0xFF ||
|
||||
end_bit > 0xFF ||
|
||||
((end_bit - start_bit + 1) > 0xFF)) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_UNEXPECTED_SIZE, __FUNCTION__);
|
||||
}
|
||||
|
||||
val->start_bit = static_cast<uint8_t>(start_bit);
|
||||
val->field_size = static_cast<uint8_t>(end_bit - start_bit + 1);
|
||||
}
|
||||
|
||||
static int32_t
|
||||
get_event_bitfield_info(std::string *config_path, evnt_info_t *val) {
|
||||
int32_t err;
|
||||
|
||||
std::string fstr;
|
||||
|
||||
err = ReadSysfsStr(*config_path, &fstr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
parse_field_config(fstr, val);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Event::get_event_file_info(void) {
|
||||
int32_t err;
|
||||
|
||||
std::string fn = evt_path_root_;
|
||||
std::string fstr;
|
||||
|
||||
fn += "/events/";
|
||||
fn += kEventFNameMap.at(event_type_);
|
||||
err = ReadSysfsStr(fn, &fstr);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
// parse_perf_attr(fstr, &event_id_.event_field_vals);
|
||||
std::stringstream ss(fstr);
|
||||
std::stringstream fs;
|
||||
std::string field_assgn;
|
||||
std::string field_name;
|
||||
evnt_info_t ev_info;
|
||||
|
||||
while (ss.rdbuf()->in_avail() != 0) {
|
||||
ev_info = {};
|
||||
getline(ss, field_assgn, ',');
|
||||
fs.clear();
|
||||
fs << field_assgn;
|
||||
getline(fs, field_name, '=');
|
||||
fs >> std::hex >> ev_info.value;
|
||||
assert(fs.rdbuf()->in_avail() == 0);
|
||||
|
||||
// Now, get the corresponding bitfield
|
||||
std::string config_path = evt_path_root_;
|
||||
config_path += "/format/";
|
||||
config_path += field_name;
|
||||
err = get_event_bitfield_info(&config_path, &ev_info);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
event_info_.push_back(ev_info);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Event::get_event_type(uint32_t *ev_type) {
|
||||
assert(ev_type != nullptr);
|
||||
if (ev_type == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
std::string fn = evt_path_root_;
|
||||
std::string fstr;
|
||||
|
||||
fn += "/type";
|
||||
|
||||
std::ifstream fs;
|
||||
fs.open(fn);
|
||||
|
||||
if (!fs.is_open()) {
|
||||
return errno;
|
||||
}
|
||||
fs >> *ev_type;
|
||||
fs.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
get_perf_attr_config(std::vector<evnt_info_t> *ev_info) {
|
||||
uint64_t ret_val = 0;
|
||||
|
||||
assert(ev_info != nullptr);
|
||||
|
||||
for (const evnt_info_t& ev : *ev_info) {
|
||||
ret_val |= ev.value << ev.start_bit;
|
||||
}
|
||||
return ret_val;
|
||||
}
|
||||
|
||||
int32_t
|
||||
amd::smi::evt::Event::openPerfHandle(void) {
|
||||
int32_t ret;
|
||||
|
||||
memset(&attr_, 0, sizeof(struct perf_event_attr));
|
||||
|
||||
ret = get_event_file_info();
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
ret = get_event_type(&attr_.type);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
attr_.size = sizeof(struct perf_event_attr);
|
||||
attr_.config = get_perf_attr_config(&event_info_);
|
||||
attr_.sample_type = PERF_SAMPLE_IDENTIFIER;
|
||||
attr_.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED |
|
||||
PERF_FORMAT_TOTAL_TIME_RUNNING;
|
||||
attr_.disabled = 1;
|
||||
attr_.inherit = 1;
|
||||
|
||||
int64_t p_ret = syscall(__NR_perf_event_open, &attr_,
|
||||
-1, 0, -1, PERF_FLAG_FD_NO_GROUP);
|
||||
|
||||
if (p_ret < 0) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
fd_ = static_cast<int>(p_ret);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
amd::smi::evt::Event::startCounter(void) {
|
||||
int32_t ret;
|
||||
|
||||
if (fd_ == -1) {
|
||||
ret = openPerfHandle();
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
ret = ioctl(fd_, PERF_EVENT_IOC_ENABLE, NULL);
|
||||
|
||||
if (ret == -1) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
assert(ret == 0); // We're expecting the ioctl call to return -1 or 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
amd::smi::evt::Event::stopCounter(void) {
|
||||
int32_t ret;
|
||||
|
||||
if (fd_ == -1) {
|
||||
return EBADF;
|
||||
}
|
||||
ret = ioctl(fd_, PERF_EVENT_IOC_DISABLE, NULL);
|
||||
|
||||
if (ret == -1) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
assert(ret == 0); // We're expecting the ioctl call to return -1 or 0
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
readn(int fd, void *buf, size_t n) {
|
||||
size_t left = n;
|
||||
ssize_t bytes;
|
||||
|
||||
while (left) {
|
||||
bytes = read(fd, buf, left);
|
||||
if (!bytes) { /* reach EOF */
|
||||
return static_cast<ssize_t>(n - left);
|
||||
}
|
||||
if (bytes < 0) {
|
||||
if (errno == EINTR) /* read got interrupted */
|
||||
continue;
|
||||
else
|
||||
return -errno;
|
||||
}
|
||||
|
||||
left -= static_cast<size_t>(bytes);
|
||||
buf = reinterpret_cast<void *>((reinterpret_cast<uint8_t *>(buf) + bytes));
|
||||
}
|
||||
return static_cast<ssize_t>(n);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
amd::smi::evt::Event::getValue(rsmi_counter_value_t *val) {
|
||||
assert(val != nullptr);
|
||||
ssize_t ret;
|
||||
|
||||
perf_read_format_t pvalue;
|
||||
ret = readn(fd_, &pvalue, sizeof(perf_read_format_t));
|
||||
if (ret < 0) {
|
||||
return static_cast<uint32_t>(-ret);
|
||||
}
|
||||
|
||||
if (ret != sizeof(perf_read_format_t)) {
|
||||
return EIO;
|
||||
}
|
||||
|
||||
val->value = pvalue.value - prev_cntr_val_;
|
||||
prev_cntr_val_ = pvalue.value;
|
||||
val->time_enabled = pvalue.enabled_time;
|
||||
val->time_running = pvalue.run_time;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace evt
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,312 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2021, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <regex> // NOLINT
|
||||
#include <vector>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "rocm_smi/rocm_smi_common.h" // Should go before rocm_smi.h
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_monitor.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
|
||||
#define TRY try {
|
||||
#define CATCH } catch (...) {return amd::smi::handleException();}
|
||||
|
||||
// Put definitions of old gpu_metrics formats here
|
||||
typedef struct {
|
||||
struct metrics_table_header_t common_header;
|
||||
|
||||
/* Driver attached timestamp (in ns) */
|
||||
uint64_t system_clock_counter;
|
||||
|
||||
/* Temperature */
|
||||
uint16_t temperature_edge;
|
||||
uint16_t temperature_hotspot;
|
||||
uint16_t temperature_mem;
|
||||
uint16_t temperature_vrgfx;
|
||||
uint16_t temperature_vrsoc;
|
||||
uint16_t temperature_vrmem;
|
||||
|
||||
/* Utilization */
|
||||
uint16_t average_gfx_activity;
|
||||
uint16_t average_umc_activity; // memory controller
|
||||
uint16_t average_mm_activity; // UVD or VCN
|
||||
|
||||
/* Power/Energy */
|
||||
uint16_t average_socket_power;
|
||||
uint32_t energy_accumulator;
|
||||
|
||||
/* Average clocks */
|
||||
uint16_t average_gfxclk_frequency;
|
||||
uint16_t average_socclk_frequency;
|
||||
uint16_t average_uclk_frequency;
|
||||
uint16_t average_vclk0_frequency;
|
||||
uint16_t average_dclk0_frequency;
|
||||
uint16_t average_vclk1_frequency;
|
||||
uint16_t average_dclk1_frequency;
|
||||
|
||||
/* Current clocks */
|
||||
uint16_t current_gfxclk;
|
||||
uint16_t current_socclk;
|
||||
uint16_t current_uclk;
|
||||
uint16_t current_vclk0;
|
||||
uint16_t current_dclk0;
|
||||
uint16_t current_vclk1;
|
||||
uint16_t current_dclk1;
|
||||
|
||||
/* Throttle status */
|
||||
uint32_t throttle_status;
|
||||
|
||||
/* Fans */
|
||||
uint16_t current_fan_speed;
|
||||
|
||||
/* Link width/speed */
|
||||
uint8_t pcie_link_width;
|
||||
uint8_t pcie_link_speed; // in 0.1 GT/s
|
||||
} rsmi_gpu_metrics_v_1_0_t;
|
||||
|
||||
typedef struct {
|
||||
rsmi_gpu_metrics_t base;
|
||||
uint64_t firmware_timestamp;
|
||||
} rsmi_gpu_metrics_v_1_2;
|
||||
|
||||
typedef struct {
|
||||
rsmi_gpu_metrics_t base;
|
||||
/* PMFW attached timestamp (10ns resolution) */
|
||||
uint64_t firmware_timestamp;
|
||||
|
||||
/* Voltage (mV) */
|
||||
uint16_t voltage_soc;
|
||||
uint16_t voltage_gfx;
|
||||
uint16_t voltage_mem;
|
||||
|
||||
uint16_t padding1;
|
||||
|
||||
/* Throttle status (ASIC independent) */
|
||||
uint64_t indep_throttle_status;
|
||||
|
||||
} rsmi_gpu_metrics_v_1_3;
|
||||
|
||||
static rsmi_status_t GetGPUMetricsFormat1(uint32_t dv_ind,
|
||||
rsmi_gpu_metrics_t *data, uint8_t content_v) {
|
||||
assert(content_v != RSMI_GPU_METRICS_API_CONTENT_VER_1 &&
|
||||
content_v != RSMI_GPU_METRICS_API_CONTENT_VER_2 &&
|
||||
content_v != RSMI_GPU_METRICS_API_CONTENT_VER_3 );
|
||||
if (content_v == RSMI_GPU_METRICS_API_CONTENT_VER_1 ||
|
||||
content_v == RSMI_GPU_METRICS_API_CONTENT_VER_2 ||
|
||||
content_v == RSMI_GPU_METRICS_API_CONTENT_VER_3 ) {
|
||||
// This function shouldn't be called if content version is
|
||||
// RSMI_GPU_METRICS_API_CONTENT_VER_1 or RSMI_GPU_METRICS_API_CONTENT_VER_2
|
||||
// or RSMI_GPU_METRICS_API_CONTENT_VER_3
|
||||
return RSMI_STATUS_INVALID_ARGS;
|
||||
}
|
||||
void *metric_data = nullptr;
|
||||
size_t data_size;
|
||||
rsmi_status_t ret;
|
||||
|
||||
rsmi_gpu_metrics_v_1_0_t metric_data_v_1_0;
|
||||
|
||||
if (content_v == 0) {
|
||||
metric_data = &metric_data_v_1_0;
|
||||
data_size = sizeof(rsmi_gpu_metrics_v_1_0_t);
|
||||
} // else { ... handle other conversions to v1
|
||||
|
||||
assert(metric_data != nullptr && "Unexpected conversion attempted.");
|
||||
ret = GetDevBinaryBlob(amd::smi::kDevGpuMetrics, dv_ind, data_size,
|
||||
metric_data);
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define ASSIGN_DATA_FIELD(FIELD, SRC) \
|
||||
data->FIELD = SRC->FIELD;
|
||||
|
||||
#define ASSIGN_COMMON_FORMATS(SRC) \
|
||||
ASSIGN_DATA_FIELD(common_header, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_edge, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_hotspot, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_mem, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_vrgfx, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_vrsoc, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(temperature_vrmem, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_gfx_activity, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_umc_activity, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_mm_activity, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_socket_power, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(system_clock_counter, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_gfxclk_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_socclk_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_uclk_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_vclk0_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_dclk0_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_vclk1_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(average_dclk1_frequency, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_gfxclk, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_socclk, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_uclk, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_vclk0, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_dclk0, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_vclk1, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_dclk1, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(throttle_status, (SRC)) \
|
||||
ASSIGN_DATA_FIELD(current_fan_speed, (SRC))
|
||||
|
||||
// Now handle differences from format 1
|
||||
if (content_v == 0) {
|
||||
// First handle all data that is common to Format1 and other formats
|
||||
ASSIGN_COMMON_FORMATS(
|
||||
reinterpret_cast<rsmi_gpu_metrics_v_1_0_t *>(metric_data))
|
||||
|
||||
// Then, the differences:
|
||||
data->energy_accumulator = static_cast<uint64_t>(
|
||||
reinterpret_cast<rsmi_gpu_metrics_v_1_0_t *>(
|
||||
metric_data)->energy_accumulator);
|
||||
data->pcie_link_width = static_cast<uint16_t>(
|
||||
reinterpret_cast<rsmi_gpu_metrics_v_1_0_t *>(
|
||||
metric_data)->pcie_link_width);
|
||||
data->pcie_link_speed = static_cast<uint16_t>(
|
||||
reinterpret_cast<rsmi_gpu_metrics_v_1_0_t *>(
|
||||
metric_data)->pcie_link_speed);
|
||||
|
||||
// These fields didn't exist in v0
|
||||
data->gfx_activity_acc = 0;
|
||||
data->mem_actvity_acc = 0;
|
||||
(void)memset(data->temperature_hbm, 0,
|
||||
RSMI_NUM_HBM_INSTANCES * sizeof(uint16_t));
|
||||
} // else handle other conversions to format 1
|
||||
#undef ASSIGN_DATA_FIELD
|
||||
#undef ASSIGN_COMMON_FORMATS
|
||||
return RSMI_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
// Translate gpu_metrics version 1.2 to rsmi_gpu_metrics_t. gpu_metrics
|
||||
// version 1.2 provides timestamp provided by the firmware. This timestamp
|
||||
// is sampled atomically along when gpu_metric information. Use this
|
||||
// timestamp instead of system_clock_counter
|
||||
|
||||
static void map_gpu_metrics_1_2_to_rsmi_gpu_metrics_t(
|
||||
const rsmi_gpu_metrics_v_1_2 *gpu_metrics_v_1_2,
|
||||
rsmi_gpu_metrics_t *rsmi_gpu_metrics)
|
||||
{
|
||||
memcpy(rsmi_gpu_metrics, &gpu_metrics_v_1_2->base,
|
||||
sizeof(rsmi_gpu_metrics_t));
|
||||
// firmware_timestamp is at 10ns resolution
|
||||
rsmi_gpu_metrics->system_clock_counter =
|
||||
gpu_metrics_v_1_2->firmware_timestamp * 10;
|
||||
}
|
||||
|
||||
static void map_gpu_metrics_1_3_to_rsmi_gpu_metrics_t(
|
||||
const rsmi_gpu_metrics_v_1_3 *gpu_metrics_v_1_3,
|
||||
rsmi_gpu_metrics_t *rsmi_gpu_metrics)
|
||||
{
|
||||
memcpy(rsmi_gpu_metrics, &gpu_metrics_v_1_3->base,
|
||||
sizeof(rsmi_gpu_metrics_t));
|
||||
// firmware_timestamp is at 10ns resolution
|
||||
rsmi_gpu_metrics->system_clock_counter =
|
||||
gpu_metrics_v_1_3->firmware_timestamp * 10;
|
||||
|
||||
}
|
||||
|
||||
rsmi_status_t
|
||||
rsmi_dev_gpu_metrics_info_get(uint32_t dv_ind, rsmi_gpu_metrics_t *smu) {
|
||||
TRY
|
||||
DEVICE_MUTEX
|
||||
CHK_SUPPORT_NAME_ONLY(smu)
|
||||
rsmi_gpu_metrics_v_1_2 smu_v_1_2;
|
||||
rsmi_gpu_metrics_v_1_3 smu_v_1_3;
|
||||
rsmi_status_t ret;
|
||||
|
||||
if (!dev->gpu_metrics_ver().structure_size) {
|
||||
ret = GetDevBinaryBlob(amd::smi::kDevGpuMetrics, dv_ind,
|
||||
sizeof(struct metrics_table_header_t), &dev->gpu_metrics_ver());
|
||||
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// only supports gpu_metrics_v1_x version
|
||||
if (dev->gpu_metrics_ver().format_revision != 1) {
|
||||
return RSMI_STATUS_NOT_SUPPORTED;
|
||||
}
|
||||
|
||||
// Initialize the smu fiedls to zero as some of them only valid in
|
||||
// a specific version.
|
||||
*smu = {};
|
||||
|
||||
if (dev->gpu_metrics_ver().content_revision ==
|
||||
RSMI_GPU_METRICS_API_CONTENT_VER_1) {
|
||||
ret = GetDevBinaryBlob(amd::smi::kDevGpuMetrics, dv_ind,
|
||||
sizeof(rsmi_gpu_metrics_t), smu);
|
||||
} else if (dev->gpu_metrics_ver().content_revision ==
|
||||
RSMI_GPU_METRICS_API_CONTENT_VER_2) {
|
||||
ret = GetDevBinaryBlob(amd::smi::kDevGpuMetrics, dv_ind,
|
||||
sizeof(rsmi_gpu_metrics_v_1_2), &smu_v_1_2);
|
||||
map_gpu_metrics_1_2_to_rsmi_gpu_metrics_t(&smu_v_1_2, smu);
|
||||
} else if (dev->gpu_metrics_ver().content_revision ==
|
||||
RSMI_GPU_METRICS_API_CONTENT_VER_3) {
|
||||
ret = GetDevBinaryBlob(amd::smi::kDevGpuMetrics, dv_ind,
|
||||
sizeof(rsmi_gpu_metrics_v_1_3), &smu_v_1_3);
|
||||
map_gpu_metrics_1_3_to_rsmi_gpu_metrics_t(&smu_v_1_3, smu);
|
||||
} else {
|
||||
ret = GetGPUMetricsFormat1(dv_ind, smu,
|
||||
dev->gpu_metrics_ver().content_revision);
|
||||
}
|
||||
|
||||
if (ret != RSMI_STATUS_SUCCESS) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
CATCH
|
||||
}
|
||||
@@ -1,407 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2020, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_io_link.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
static const char *kKFDNodesPathRoot = "/sys/class/kfd/kfd/topology/nodes";
|
||||
static const char *kKFDLinkPath[] = {"io_links", "p2p_links"};
|
||||
|
||||
// IO Link Property strings
|
||||
static const char *kIOLinkPropTYPEStr = "type";
|
||||
// static const char *kIOLinkPropVERSION_MAJORStr = "version_major";
|
||||
// static const char *kIOLinkPropVERSION_MINORStr = "version_minor";
|
||||
static const char *kIOLinkPropNODE_FROMStr = "node_from";
|
||||
static const char *kIOLinkPropNODE_TOStr = "node_to";
|
||||
static const char *kIOLinkPropWEIGHTStr = "weight";
|
||||
// static const char *kIOLinkPropMIN_LATENCYStr = "min_latency";
|
||||
// static const char *kIOLinkPropMAX_LATENCYStr = "max_latency";
|
||||
static const char *kIOLinkPropMIN_BANDWIDTHStr = "min_bandwidth";
|
||||
static const char *kIOLinkPropMAX_BANDWIDTHStr = "max_bandwidth";
|
||||
// static const char *kIOLinkPropRECOMMENDED_TRANSFER_SIZEStr =
|
||||
// "recommended_transfer_size";
|
||||
// static const char *kIOLinkPropFLAGSStr = "flags";
|
||||
|
||||
static bool is_number(const std::string &s) {
|
||||
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
|
||||
}
|
||||
|
||||
static std::string LinkPathRoot(uint32_t node_indx,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
std::string link_path_root = kKFDNodesPathRoot;
|
||||
link_path_root += '/';
|
||||
link_path_root += std::to_string(node_indx);
|
||||
link_path_root += '/';
|
||||
if (directory < sizeof(kKFDLinkPath)/sizeof(kKFDLinkPath[0])) {
|
||||
link_path_root += kKFDLinkPath[directory];
|
||||
} else {
|
||||
link_path_root = "";
|
||||
}
|
||||
return link_path_root;
|
||||
}
|
||||
|
||||
static std::string LinkPath(uint32_t node_indx, uint32_t link_indx,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
std::string link_path = LinkPathRoot(node_indx, directory);
|
||||
link_path += '/';
|
||||
link_path += std::to_string(link_indx);
|
||||
return link_path;
|
||||
}
|
||||
|
||||
static int OpenLinkProperties(uint32_t node_indx, uint32_t link_indx,
|
||||
std::ifstream *fs,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
int ret;
|
||||
std::string f_path;
|
||||
bool reg_file;
|
||||
|
||||
assert(fs != nullptr);
|
||||
if (fs == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
f_path = LinkPath(node_indx, link_indx, directory);
|
||||
f_path += "/";
|
||||
f_path += "properties";
|
||||
|
||||
ret = isRegularFile(f_path, ®_file);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!reg_file) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
fs->open(f_path);
|
||||
|
||||
if (!fs->is_open()) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadLinkProperties(uint32_t node_indx, uint32_t link_indx,
|
||||
std::vector<std::string> *retVec,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
std::string line;
|
||||
int ret;
|
||||
std::ifstream fs;
|
||||
|
||||
assert(retVec != nullptr);
|
||||
if (retVec == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
ret = OpenLinkProperties(node_indx, link_indx, &fs, directory);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (std::getline(fs, line)) {
|
||||
retVec->push_back(line);
|
||||
}
|
||||
|
||||
if (retVec->size() == 0) {
|
||||
fs.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Remove any *trailing* empty (whitespace) lines
|
||||
while (retVec->back().find_first_not_of(" \t\n\v\f\r") == std::string::npos) {
|
||||
retVec->pop_back();
|
||||
}
|
||||
|
||||
fs.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int DiscoverLinks(std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::shared_ptr<IOLink>> *links,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
assert(links != nullptr);
|
||||
if (links == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
assert(links->size() == 0);
|
||||
|
||||
links->clear();
|
||||
|
||||
auto kfd_node_dir = opendir(kKFDNodesPathRoot);
|
||||
|
||||
if (kfd_node_dir == nullptr) {
|
||||
std::string err_msg = "Failed to open KFD nodes directory ";
|
||||
err_msg += kKFDNodesPathRoot;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto dentry_kfd = readdir(kfd_node_dir);
|
||||
while (dentry_kfd != nullptr) {
|
||||
if (dentry_kfd->d_name[0] == '.') {
|
||||
dentry_kfd = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_number(dentry_kfd->d_name)) {
|
||||
dentry_kfd = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint32_t node_indx = static_cast<uint32_t>(std::stoi(dentry_kfd->d_name));
|
||||
std::shared_ptr<IOLink> link;
|
||||
uint32_t link_indx;
|
||||
std::string link_path_root = LinkPathRoot(node_indx, directory);
|
||||
|
||||
auto io_link_dir = opendir(link_path_root.c_str());
|
||||
assert(io_link_dir != nullptr);
|
||||
|
||||
auto dentry_io_link = readdir(io_link_dir);
|
||||
while (dentry_io_link != nullptr) {
|
||||
if (dentry_io_link->d_name[0] == '.') {
|
||||
dentry_io_link = readdir(io_link_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_number(dentry_io_link->d_name)) {
|
||||
dentry_io_link = readdir(io_link_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
link_indx = static_cast<uint32_t>(std::stoi(dentry_io_link->d_name));
|
||||
link = std::shared_ptr<IOLink>(new IOLink(node_indx, link_indx,
|
||||
directory));
|
||||
|
||||
link->Initialize();
|
||||
|
||||
(*links)[std::make_pair(link->node_from(), link->node_to())] = link;
|
||||
|
||||
dentry_io_link = readdir(io_link_dir);
|
||||
}
|
||||
|
||||
if (closedir(io_link_dir)) {
|
||||
std::string err_msg = "Failed to close KFD nodes directory ";
|
||||
err_msg += kKFDNodesPathRoot;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
dentry_kfd = readdir(kfd_node_dir);
|
||||
}
|
||||
|
||||
if (closedir(kfd_node_dir)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DiscoverIOLinks(std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::shared_ptr<IOLink>> *links) {
|
||||
return DiscoverLinks(links, IO_LINK_DIRECTORY);
|
||||
}
|
||||
|
||||
int DiscoverP2PLinks(std::map<std::pair<uint32_t, uint32_t>,
|
||||
std::shared_ptr<IOLink>> *links) {
|
||||
return DiscoverLinks(links, P2P_LINK_DIRECTORY);
|
||||
}
|
||||
|
||||
static int DiscoverLinksPerNode(uint32_t node_indx, std::map<uint32_t,
|
||||
std::shared_ptr<IOLink>> *links,
|
||||
LINK_DIRECTORY_TYPE directory) {
|
||||
assert(links != nullptr);
|
||||
if (links == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
assert(links->size() == 0);
|
||||
|
||||
links->clear();
|
||||
|
||||
std::shared_ptr<IOLink> link;
|
||||
uint32_t link_indx;
|
||||
std::string link_path_root = LinkPathRoot(node_indx, directory);
|
||||
|
||||
auto io_link_dir = opendir(link_path_root.c_str());
|
||||
assert(io_link_dir != nullptr);
|
||||
|
||||
auto dentry = readdir(io_link_dir);
|
||||
while (dentry != nullptr) {
|
||||
if (dentry->d_name[0] == '.') {
|
||||
dentry = readdir(io_link_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_number(dentry->d_name)) {
|
||||
dentry = readdir(io_link_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
link_indx = static_cast<uint32_t>(std::stoi(dentry->d_name));
|
||||
link = std::shared_ptr<IOLink>(new IOLink(node_indx, link_indx,
|
||||
directory));
|
||||
|
||||
link->Initialize();
|
||||
|
||||
(*links)[link->node_to()] = link;
|
||||
|
||||
dentry = readdir(io_link_dir);
|
||||
}
|
||||
|
||||
if (closedir(io_link_dir)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DiscoverIOLinksPerNode(uint32_t node_indx, std::map<uint32_t,
|
||||
std::shared_ptr<IOLink>> *links) {
|
||||
return DiscoverLinksPerNode(node_indx, links, IO_LINK_DIRECTORY);
|
||||
}
|
||||
|
||||
int DiscoverP2PLinksPerNode(uint32_t node_indx, std::map<uint32_t,
|
||||
std::shared_ptr<IOLink>> *links) {
|
||||
return DiscoverLinksPerNode(node_indx, links, P2P_LINK_DIRECTORY);
|
||||
}
|
||||
|
||||
IOLink::~IOLink() {
|
||||
}
|
||||
|
||||
int IOLink::ReadProperties(void) {
|
||||
int ret;
|
||||
|
||||
std::vector<std::string> propVec;
|
||||
|
||||
assert(properties_.size() == 0);
|
||||
if (properties_.size() > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ReadLinkProperties(node_indx_, link_indx_, &propVec,
|
||||
link_dir_type_);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string key_str;
|
||||
uint64_t val_int; // Assume all properties are unsigned integers for now
|
||||
std::istringstream fs;
|
||||
|
||||
for (uint32_t i = 0; i < propVec.size(); ++i) {
|
||||
fs.str(propVec[i]);
|
||||
fs >> key_str;
|
||||
fs >> val_int;
|
||||
|
||||
properties_[key_str] = val_int;
|
||||
|
||||
fs.str("");
|
||||
fs.clear();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
IOLink::Initialize(void) {
|
||||
int ret = 0;
|
||||
ret = ReadProperties();
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropTYPEStr,
|
||||
reinterpret_cast<uint64_t *>(&type_));
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropNODE_FROMStr,
|
||||
reinterpret_cast<uint64_t *>(&node_from_));
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropNODE_TOStr,
|
||||
reinterpret_cast<uint64_t *>(&node_to_));
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropWEIGHTStr, &weight_);
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropMIN_BANDWIDTHStr, &min_bandwidth_);
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = get_property_value(kIOLinkPropMAX_BANDWIDTHStr, &max_bandwidth_);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
IOLink::get_property_value(std::string property, uint64_t *value) {
|
||||
assert(value != nullptr);
|
||||
if (value == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (properties_.find(property) == properties_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*value = properties_[property];
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -1,772 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2019, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <fstream>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "rocm_smi/rocm_smi_io_link.h"
|
||||
#include "rocm_smi/rocm_smi_kfd.h"
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_device.h"
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
static const char *kKFDProcPathRoot = "/sys/class/kfd/kfd/proc";
|
||||
static const char *kKFDNodesPathRoot = "/sys/class/kfd/kfd/topology/nodes";
|
||||
// Sysfs file names
|
||||
static const char *kKFDPasidFName = "pasid";
|
||||
|
||||
|
||||
|
||||
// KFD Node Property strings
|
||||
// static const char *kKFDNodePropCPU_CORES_COUNTStr = "cpu_cores_count";
|
||||
// static const char *kKFDNodePropSIMD_COUNTStr = "simd_count";
|
||||
// static const char *kKFDNodePropMEM_BANKS_COUNTStr = "mem_banks_count";
|
||||
// static const char *kKFDNodePropCACHES_COUNTStr = "caches_count";
|
||||
// static const char *kKFDNodePropIO_LINKS_COUNTStr = "io_links_count";
|
||||
// static const char *kKFDNodePropCPU_CORE_ID_BASEStr = "cpu_core_id_base";
|
||||
// static const char *kKFDNodePropSIMD_ID_BASEStr = "simd_id_base";
|
||||
// static const char *kKFDNodePropMAX_WAVES_PER_SIMDStr = "max_waves_per_simd";
|
||||
// static const char *kKFDNodePropLDS_SIZE_IN_KBStr = "lds_size_in_kb";
|
||||
// static const char *kKFDNodePropGDS_SIZE_IN_KBStr = "gds_size_in_kb";
|
||||
// static const char *kKFDNodePropNUM_GWSStr = "num_gws";
|
||||
// static const char *kKFDNodePropWAVE_FRONT_SIZEStr = "wave_front_size";
|
||||
|
||||
static const char *kKFDNodePropARRAY_COUNTStr = "array_count";
|
||||
static const char *kKFDNodePropSIMD_ARRAYS_PER_ENGINEStr =
|
||||
"simd_arrays_per_engine";
|
||||
static const char *kKFDNodePropCU_PER_SIMD_ARRAYStr = "cu_per_simd_array";
|
||||
// static const char *kKFDNodePropSIMD_PER_CUStr = "simd_per_cu";
|
||||
// static const char *kKFDNodePropMAX_SLOTS_SCRATCH_CUStr =
|
||||
// "max_slots_scratch_cu";
|
||||
|
||||
// static const char *kKFDNodePropVENDOR_IDStr = "vendor_id";
|
||||
// static const char *kKFDNodePropDEVICE_IDStr = "device_id";
|
||||
static const char *kKFDNodePropLOCATION_IDStr = "location_id";
|
||||
static const char *kKFDNodePropDOMAINStr = "domain";
|
||||
// static const char *kKFDNodePropDRM_RENDER_MINORStr = "drm_render_minor";
|
||||
static const char *kKFDNodePropHIVE_IDStr = "hive_id";
|
||||
// static const char *kKFDNodePropNUM_SDMA_ENGINESStr = "num_sdma_engines";
|
||||
// static const char *kKFDNodePropNUM_SDMA_XGMI_ENGINESStr =
|
||||
// "num_sdma_xgmi_engines";
|
||||
// static const char *kKFDNodePropNUM_SDMA_QUEUES_PER_ENGINEStr =
|
||||
// "num_sdma_queues_per_engine";
|
||||
// static const char *kKFDNodePropNUM_CP_QUEUESStr = "num_cp_queues";
|
||||
// static const char *kKFDNodePropMAX_ENGINE_CLK_FCOMPUTEStr =
|
||||
// "max_engine_clk_fcompute";
|
||||
// static const char *kKFDNodePropLOCAL_MEM_SIZEStr = "local_mem_size";
|
||||
// static const char *kKFDNodePropFW_VERSIONStr = "fw_version";
|
||||
// static const char *kKFDNodePropCAPABILITYStr = "capability";
|
||||
// static const char *kKFDNodePropDEBUG_PROPStr = "debug_prop";
|
||||
// static const char *kKFDNodePropSDMA_FW_VERSIOStr = "sdma_fw_versio";
|
||||
// static const char *kKFDNodePropMAX_ENGINE_CLK_CCOMPUTEStr =
|
||||
// "max_engine_clk_ccompute";
|
||||
|
||||
static bool is_number(const std::string &s) {
|
||||
return !s.empty() && std::all_of(s.begin(), s.end(), ::isdigit);
|
||||
}
|
||||
|
||||
static std::string KFDDevicePath(uint32_t dev_id) {
|
||||
std::string node_path = kKFDNodesPathRoot;
|
||||
node_path += '/';
|
||||
node_path += std::to_string(dev_id);
|
||||
return node_path;
|
||||
}
|
||||
|
||||
static int OpenKFDNodeFile(uint32_t dev_id, std::string node_file,
|
||||
std::ifstream *fs) {
|
||||
std::string line;
|
||||
int ret;
|
||||
std::string f_path;
|
||||
bool reg_file;
|
||||
|
||||
assert(fs != nullptr);
|
||||
|
||||
f_path = KFDDevicePath(dev_id);
|
||||
f_path += "/";
|
||||
f_path += node_file;
|
||||
|
||||
ret = isRegularFile(f_path, ®_file);
|
||||
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
if (!reg_file) {
|
||||
return ENOENT;
|
||||
}
|
||||
|
||||
fs->open(f_path);
|
||||
|
||||
if (!fs->is_open()) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool KFDNodeSupported(uint32_t node_indx) {
|
||||
std::ifstream fs;
|
||||
bool ret = true;
|
||||
int err;
|
||||
err = OpenKFDNodeFile(node_indx, "properties", &fs);
|
||||
|
||||
if (err == ENOENT) {
|
||||
return false;
|
||||
}
|
||||
if (fs.peek() == std::ifstream::traits_type::eof()) {
|
||||
ret = false;
|
||||
}
|
||||
fs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ReadKFDDeviceProperties(uint32_t kfd_node_id,
|
||||
std::vector<std::string> *retVec) {
|
||||
std::string line;
|
||||
int ret;
|
||||
std::ifstream fs;
|
||||
std::string properties_path;
|
||||
|
||||
assert(retVec != nullptr);
|
||||
|
||||
ret = OpenKFDNodeFile(kfd_node_id, "properties", &fs);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
while (std::getline(fs, line)) {
|
||||
retVec->push_back(line);
|
||||
}
|
||||
|
||||
if (retVec->size() == 0) {
|
||||
fs.close();
|
||||
return ENOENT;
|
||||
}
|
||||
// Remove any *trailing* empty (whitespace) lines
|
||||
while (retVec->back().find_first_not_of(" \t\n\v\f\r") == std::string::npos) {
|
||||
retVec->pop_back();
|
||||
}
|
||||
|
||||
fs.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadKFDGpuId(uint32_t kfd_node_id, uint64_t *gpu_id) {
|
||||
std::string line;
|
||||
int ret;
|
||||
std::ifstream fs;
|
||||
std::string gpu_id_str;
|
||||
|
||||
assert(gpu_id != nullptr);
|
||||
|
||||
ret = OpenKFDNodeFile(kfd_node_id, "gpu_id", &fs);
|
||||
|
||||
if (ret) {
|
||||
fs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << fs.rdbuf();
|
||||
fs.close();
|
||||
|
||||
gpu_id_str = ss.str();
|
||||
|
||||
gpu_id_str.erase(std::remove(gpu_id_str.begin(), gpu_id_str.end(), '\n'),
|
||||
gpu_id_str.end());
|
||||
|
||||
if (!is_number(gpu_id_str)) {
|
||||
return ENXIO;
|
||||
}
|
||||
|
||||
*gpu_id = static_cast<uint64_t>(std::stoi(gpu_id_str));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ReadKFDGpuName(uint32_t kfd_node_id, std::string *gpu_name) {
|
||||
std::string line;
|
||||
int ret;
|
||||
std::ifstream fs;
|
||||
|
||||
assert(gpu_name != nullptr);
|
||||
|
||||
ret = OpenKFDNodeFile(kfd_node_id, "name", &fs);
|
||||
|
||||
if (ret) {
|
||||
fs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::stringstream ss;
|
||||
ss << fs.rdbuf();
|
||||
fs.close();
|
||||
|
||||
*gpu_name = ss.str();
|
||||
|
||||
gpu_name->erase(std::remove(gpu_name->begin(), gpu_name->end(), '\n'),
|
||||
gpu_name->end());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetProcessInfo(rsmi_process_info_t *procs, uint32_t num_allocated,
|
||||
uint32_t *num_procs_found) {
|
||||
assert(num_procs_found != nullptr);
|
||||
|
||||
*num_procs_found = 0;
|
||||
errno = 0;
|
||||
auto proc_dir = opendir(kKFDProcPathRoot);
|
||||
|
||||
if (proc_dir == nullptr) {
|
||||
perror("Unable to open process directory");
|
||||
return errno;
|
||||
}
|
||||
auto dentry = readdir(proc_dir);
|
||||
|
||||
std::string proc_id_str;
|
||||
std::string tmp;
|
||||
|
||||
while (dentry != nullptr) {
|
||||
if (dentry->d_name[0] == '.') {
|
||||
dentry = readdir(proc_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
proc_id_str = dentry->d_name;
|
||||
assert(is_number(proc_id_str) && "Unexpected file name in kfd/proc dir");
|
||||
if (!is_number(proc_id_str)) {
|
||||
dentry = readdir(proc_dir);
|
||||
continue;
|
||||
}
|
||||
if (procs && *num_procs_found < num_allocated) {
|
||||
int err;
|
||||
std::string tmp;
|
||||
|
||||
procs[*num_procs_found].process_id =
|
||||
static_cast<uint32_t>(std::stoi(proc_id_str));
|
||||
|
||||
std::string pasid_str_path = kKFDProcPathRoot;
|
||||
pasid_str_path += "/";
|
||||
pasid_str_path += proc_id_str;
|
||||
pasid_str_path += "/";
|
||||
pasid_str_path += kKFDPasidFName;
|
||||
|
||||
err = ReadSysfsStr(pasid_str_path, &tmp);
|
||||
if (err) {
|
||||
dentry = readdir(proc_dir);
|
||||
continue;
|
||||
}
|
||||
assert(is_number(tmp) && "Unexpected value in pasid file");
|
||||
if (!is_number(tmp)) {
|
||||
closedir(proc_dir);
|
||||
return EINVAL;
|
||||
}
|
||||
procs[*num_procs_found].pasid = static_cast<uint32_t>(std::stoi(tmp));
|
||||
}
|
||||
++(*num_procs_found);
|
||||
|
||||
dentry = readdir(proc_dir);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
if (closedir(proc_dir)) {
|
||||
return errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read the gpuid files found in all the <queue id> dirs and put them in
|
||||
// gpus_found.
|
||||
// Directory structure:
|
||||
// /sys/class/kfd/kfd/proc/<pid>/queues/<queue id>/gpuid
|
||||
|
||||
int GetProcessGPUs(uint32_t pid, std::unordered_set<uint64_t> *gpu_set) {
|
||||
int err;
|
||||
|
||||
assert(gpu_set != nullptr);
|
||||
if (gpu_set == nullptr) {
|
||||
return RSMI_STATUS_INVALID_ARGS;
|
||||
}
|
||||
errno = 0;
|
||||
|
||||
std::string queues_dir = kKFDProcPathRoot;
|
||||
queues_dir += "/";
|
||||
queues_dir += std::to_string(pid);
|
||||
queues_dir += "/queues";
|
||||
|
||||
auto queues_dir_hd = opendir(queues_dir.c_str());
|
||||
|
||||
if (queues_dir_hd == nullptr) {
|
||||
std::string err_str = "Unable to open queues directory for process ";
|
||||
err_str += std::to_string(pid);
|
||||
perror(err_str.c_str());
|
||||
return ESRCH;
|
||||
}
|
||||
|
||||
auto q_dentry = readdir(queues_dir_hd);
|
||||
|
||||
std::string q_gpu_id_str;
|
||||
std::string q_dir;
|
||||
|
||||
std::string tmp;
|
||||
|
||||
while (q_dentry != nullptr) {
|
||||
if (q_dentry->d_name[0] == '.') {
|
||||
q_dentry = readdir(queues_dir_hd);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_number(q_dentry->d_name)) {
|
||||
q_dentry = readdir(queues_dir_hd);
|
||||
continue;
|
||||
}
|
||||
|
||||
q_gpu_id_str = queues_dir + '/' + q_dentry->d_name + "/gpuid";
|
||||
|
||||
err = ReadSysfsStr(q_gpu_id_str, &tmp);
|
||||
if (err) {
|
||||
q_dentry = readdir(queues_dir_hd);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t val;
|
||||
try {
|
||||
val = static_cast<uint64_t>(std::stoi(tmp));
|
||||
} catch (...) {
|
||||
std::cerr << "Error; read invalid data: " << tmp << " from " <<
|
||||
q_gpu_id_str << std::endl;
|
||||
closedir(queues_dir_hd);
|
||||
return ENXIO; // Return "no such device" if we read an invalid gpu id
|
||||
}
|
||||
gpu_set->insert(val);
|
||||
|
||||
q_dentry = readdir(queues_dir_hd);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
if (closedir(queues_dir_hd)) {
|
||||
return errno;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int GetProcessInfoForPID(uint32_t pid, rsmi_process_info_t *proc,
|
||||
std::unordered_set<uint64_t> *gpu_set) {
|
||||
assert(proc != nullptr);
|
||||
assert(gpu_set != nullptr);
|
||||
int err;
|
||||
std::string tmp;
|
||||
std::unordered_set<uint64_t>::iterator itr;
|
||||
|
||||
std::string proc_str_path = kKFDProcPathRoot;
|
||||
proc_str_path += "/";
|
||||
proc_str_path += std::to_string(pid);
|
||||
|
||||
if (!FileExists(proc_str_path.c_str())) {
|
||||
return ESRCH;
|
||||
}
|
||||
proc->process_id = pid;
|
||||
|
||||
std::string pasid_str_path = proc_str_path;
|
||||
pasid_str_path += "/";
|
||||
pasid_str_path += kKFDPasidFName;
|
||||
|
||||
err = ReadSysfsStr(pasid_str_path, &tmp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
assert(is_number(tmp) && "Unexpected value in pasid file");
|
||||
|
||||
if (!is_number(tmp)) {
|
||||
return EINVAL;
|
||||
}
|
||||
proc->pasid = static_cast<uint32_t>(std::stoi(tmp));
|
||||
|
||||
proc->vram_usage = 0;
|
||||
proc->sdma_usage = 0;
|
||||
proc->cu_occupancy = 0;
|
||||
|
||||
uint32_t cu_count = 0;
|
||||
static amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance();
|
||||
static std::map<uint64_t, std::shared_ptr<KFDNode>>& kfd_node_map =
|
||||
smi.kfd_node_map();
|
||||
|
||||
for (itr = gpu_set->begin(); itr != gpu_set->end(); itr++) {
|
||||
uint64_t gpu_id = (*itr);
|
||||
|
||||
std::string vram_str_path = proc_str_path;
|
||||
vram_str_path += "/vram_";
|
||||
vram_str_path += std::to_string(gpu_id);
|
||||
|
||||
err = ReadSysfsStr(vram_str_path, &tmp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!is_number(tmp)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
proc->vram_usage += std::stoull(tmp);
|
||||
|
||||
std::string sdma_str_path = proc_str_path;
|
||||
sdma_str_path += "/sdma_";
|
||||
sdma_str_path += std::to_string(gpu_id);
|
||||
|
||||
err = ReadSysfsStr(sdma_str_path, &tmp);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
if (!is_number(tmp)) {
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
proc->sdma_usage += std::stoull(tmp);
|
||||
|
||||
// Build the path and read from Sysfs file, info that
|
||||
// encodes Compute Unit usage by a process of interest
|
||||
std::string cu_occupancy_path = proc_str_path;
|
||||
cu_occupancy_path += "/stats_";
|
||||
cu_occupancy_path += std::to_string(gpu_id);
|
||||
cu_occupancy_path += "/cu_occupancy";
|
||||
err = ReadSysfsStr(cu_occupancy_path, &tmp);
|
||||
if (err == 0) {
|
||||
if (!is_number(tmp)) {
|
||||
return EINVAL;
|
||||
}
|
||||
// Update CU usage by the process
|
||||
proc->cu_occupancy += std::stoi(tmp);
|
||||
|
||||
// Collect count of compute units
|
||||
cu_count += kfd_node_map[gpu_id]->cu_count();
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust CU occupancy to percent.
|
||||
if (cu_count > 0) {
|
||||
proc->cu_occupancy = ((proc->cu_occupancy * 100) / cu_count);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int DiscoverKFDNodes(std::map<uint64_t, std::shared_ptr<KFDNode>> *nodes) {
|
||||
assert(nodes != nullptr);
|
||||
|
||||
if (nodes == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
assert(nodes->size() == 0);
|
||||
|
||||
nodes->clear();
|
||||
|
||||
std::shared_ptr<KFDNode> node;
|
||||
uint32_t node_indx;
|
||||
|
||||
auto kfd_node_dir = opendir(kKFDNodesPathRoot);
|
||||
if (kfd_node_dir == nullptr) {
|
||||
return errno;
|
||||
}
|
||||
|
||||
auto dentry = readdir(kfd_node_dir);
|
||||
while (dentry != nullptr) {
|
||||
if (dentry->d_name[0] == '.') {
|
||||
dentry = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_number(dentry->d_name)) {
|
||||
dentry = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
node_indx = static_cast<uint32_t>(std::stoi(dentry->d_name));
|
||||
|
||||
if (!KFDNodeSupported(node_indx)) {
|
||||
dentry = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
node = std::shared_ptr<KFDNode>(new KFDNode(node_indx));
|
||||
|
||||
node->Initialize();
|
||||
|
||||
if (node->gpu_id() == 0) {
|
||||
// Don't add; this is a cpu node.
|
||||
dentry = readdir(kfd_node_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
uint64_t kfd_gpu_node_bus_fn;
|
||||
uint64_t kfd_gpu_node_domain;
|
||||
int ret;
|
||||
ret =
|
||||
node->get_property_value(kKFDNodePropLOCATION_IDStr,
|
||||
&kfd_gpu_node_bus_fn);
|
||||
if (ret != 0) {
|
||||
std:: cerr << "Failed to open properties file for kfd node " <<
|
||||
node->node_index() << "." << std::endl;
|
||||
closedir(kfd_node_dir);
|
||||
return ret;
|
||||
}
|
||||
ret =
|
||||
node->get_property_value(kKFDNodePropDOMAINStr, &kfd_gpu_node_domain);
|
||||
if (ret != 0) {
|
||||
std::cerr << "Failed to get \"domain\" properity from properties "
|
||||
"files for kfd node " << node->node_index() << "." << std::endl;
|
||||
closedir(kfd_node_dir);
|
||||
return ret;
|
||||
}
|
||||
|
||||
uint64_t kfd_bdfid =
|
||||
(kfd_gpu_node_domain << 32) | (kfd_gpu_node_bus_fn);
|
||||
(*nodes)[kfd_bdfid] = node;
|
||||
|
||||
dentry = readdir(kfd_node_dir);
|
||||
}
|
||||
|
||||
if (closedir(kfd_node_dir)) {
|
||||
std::string err_str = "Failed to close KFD node directory ";
|
||||
err_str += kKFDNodesPathRoot;
|
||||
err_str += ".";
|
||||
perror(err_str.c_str());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
KFDNode::~KFDNode() {
|
||||
}
|
||||
|
||||
int KFDNode::ReadProperties(void) {
|
||||
int ret;
|
||||
|
||||
std::vector<std::string> propVec;
|
||||
|
||||
assert(properties_.size() == 0);
|
||||
if (properties_.size() > 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ret = ReadKFDDeviceProperties(node_indx_, &propVec);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string key_str;
|
||||
// std::string val_str;
|
||||
uint64_t val_int; // Assume all properties are unsigned integers for now
|
||||
std::istringstream fs;
|
||||
|
||||
for (uint32_t i = 0; i < propVec.size(); ++i) {
|
||||
fs.str(propVec[i]);
|
||||
fs >> key_str;
|
||||
fs >> val_int;
|
||||
|
||||
properties_[key_str] = val_int;
|
||||
|
||||
fs.str("");
|
||||
fs.clear();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
KFDNode::Initialize(void) {
|
||||
int ret = 0;
|
||||
ret = ReadProperties();
|
||||
if (ret) {return ret;}
|
||||
|
||||
ret = ReadKFDGpuId(node_indx_, &gpu_id_);
|
||||
if (ret || (gpu_id_ == 0)) {return ret;}
|
||||
|
||||
ret = ReadKFDGpuName(node_indx_, &name_);
|
||||
|
||||
ret = get_property_value(kKFDNodePropHIVE_IDStr, &xgmi_hive_id_);
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (get xgmi hive id).");
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::shared_ptr<IOLink>> io_link_map_tmp;
|
||||
ret = DiscoverIOLinksPerNode(node_indx_, &io_link_map_tmp);
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (IO Links discovery per node).");
|
||||
}
|
||||
|
||||
std::map<uint32_t, std::shared_ptr<IOLink>>::iterator it;
|
||||
uint32_t node_to;
|
||||
uint64_t node_to_gpu_id;
|
||||
std::shared_ptr<IOLink> link;
|
||||
bool numa_node_found = false;
|
||||
for (it = io_link_map_tmp.begin(); it != io_link_map_tmp.end(); it++) {
|
||||
io_link_map_[it->first] = it->second;
|
||||
node_to = it->first;
|
||||
link = it->second;
|
||||
ret = ReadKFDGpuId(node_to, &node_to_gpu_id);
|
||||
if (ret) {return ret;}
|
||||
if (node_to_gpu_id == 0) { // CPU node
|
||||
if (numa_node_found) {
|
||||
if (numa_node_weight_ > link->weight()) {
|
||||
numa_node_number_ = node_to;
|
||||
numa_node_weight_ = link->weight();
|
||||
numa_node_type_ = link->type();
|
||||
}
|
||||
} else {
|
||||
numa_node_number_ = node_to;
|
||||
numa_node_weight_ = link->weight();
|
||||
numa_node_type_ = link->type();
|
||||
numa_node_found = true;
|
||||
}
|
||||
} else {
|
||||
io_link_type_[node_to] = link->type();
|
||||
io_link_weight_[node_to] = link->weight();
|
||||
io_link_max_bandwidth_[node_to] = link->max_bandwidth();
|
||||
io_link_min_bandwidth_[node_to] = link->min_bandwidth();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Pre-compute the total number of compute units a device has
|
||||
uint64_t tmp_val;
|
||||
ret = get_property_value(kKFDNodePropSIMD_ARRAYS_PER_ENGINEStr, &tmp_val);
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library "
|
||||
"(get number of shader arrays per engine).");
|
||||
}
|
||||
cu_count_ = uint32_t(tmp_val);
|
||||
ret = get_property_value(kKFDNodePropARRAY_COUNTStr, &tmp_val);
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (get number of shader arrays).");
|
||||
}
|
||||
cu_count_ = cu_count_ * uint32_t(tmp_val);
|
||||
ret = get_property_value(kKFDNodePropCU_PER_SIMD_ARRAYStr, &tmp_val);
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (get number of CU's per array).");
|
||||
}
|
||||
cu_count_ = cu_count_ * uint32_t(tmp_val);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
KFDNode::get_property_value(std::string property, uint64_t *value) {
|
||||
assert(value != nullptr);
|
||||
if (value == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (properties_.find(property) == properties_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*value = properties_[property];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
KFDNode::get_io_link_type(uint32_t node_to, IO_LINK_TYPE *type) {
|
||||
assert(type != nullptr);
|
||||
if (type == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (io_link_type_.find(node_to) == io_link_type_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*type = io_link_type_[node_to];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
KFDNode::get_io_link_weight(uint32_t node_to, uint64_t *weight) {
|
||||
assert(weight != nullptr);
|
||||
if (weight == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (io_link_weight_.find(node_to) == io_link_weight_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*weight = io_link_weight_[node_to];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
KFDNode::get_io_link_bandwidth(uint32_t node_to, uint64_t *max_bandwidth,
|
||||
uint64_t *min_bandwidth){
|
||||
assert (max_bandwidth != nullptr && min_bandwidth != nullptr);
|
||||
if (max_bandwidth == nullptr || min_bandwidth == nullptr ){
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
if (io_link_max_bandwidth_.find(node_to) == io_link_max_bandwidth_.end() ||
|
||||
io_link_min_bandwidth_.find(node_to) == io_link_min_bandwidth_.end()){
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
*max_bandwidth = io_link_max_bandwidth_[node_to];
|
||||
*min_bandwidth = io_link_min_bandwidth_[node_to];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -1,659 +0,0 @@
|
||||
/*
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2017, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <dirent.h>
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include <cerrno>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
#include "rocm_smi/rocm_smi_device.h"
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_kfd.h"
|
||||
|
||||
static const char *kPathDRMRoot = "/sys/class/drm";
|
||||
static const char *kPathHWMonRoot = "/sys/class/hwmon";
|
||||
static const char *kPathPowerRoot = "/sys/kernel/debug/dri";
|
||||
|
||||
static const char *kDeviceNamePrefix = "card";
|
||||
|
||||
static const char *kAMDMonitorTypes[] = {"radeon", "amdgpu", ""};
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
static uint32_t GetDeviceIndex(const std::string s) {
|
||||
std::string t = s;
|
||||
size_t tmp = t.find_last_not_of("0123456789");
|
||||
t.erase(0, tmp+1);
|
||||
|
||||
assert(stoi(t) >= 0);
|
||||
return static_cast<uint32_t>(stoi(t));
|
||||
}
|
||||
|
||||
// Find the drm minor from from sysfs path "/sys/class/drm/cardX/device/drm".
|
||||
// From the directory renderDN in that sysfs path, the drm minor can be
|
||||
// computed for cardX.
|
||||
// On success, return drm_minor which is >= 128 otherwise return 0
|
||||
static uint32_t GetDrmRenderMinor(const std::string s) {
|
||||
std::string drm_path = s;
|
||||
int drm_minor = 0;
|
||||
const std::string render_file_prefix = "renderD";
|
||||
const uint64_t prefix_size = render_file_prefix.size();
|
||||
drm_path += "/device/drm";
|
||||
|
||||
auto drm_dir = opendir(drm_path.c_str());
|
||||
if (drm_dir == nullptr)
|
||||
return 0;
|
||||
|
||||
auto dentry = readdir(drm_dir);
|
||||
|
||||
while (dentry != nullptr) {
|
||||
std::string render_file = dentry->d_name;
|
||||
if (!render_file.compare(0, prefix_size, render_file_prefix)) {
|
||||
drm_minor = stoi(render_file.substr(prefix_size));
|
||||
if (drm_minor)
|
||||
break;
|
||||
}
|
||||
dentry = readdir(drm_dir);
|
||||
}
|
||||
|
||||
if (closedir(drm_dir)) {
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint32_t>(drm_minor);
|
||||
}
|
||||
|
||||
// Determine if provided string is a bdfid pci path directory of the form
|
||||
// XXXX:XX:XX.X,
|
||||
// domain:bus:device.function
|
||||
//
|
||||
// where X is a hex integer (lower case is expected). If so, write the value
|
||||
// to bdfid
|
||||
static bool bdfid_from_path(const std::string in_name, uint64_t *bdfid) {
|
||||
char *p = nullptr;
|
||||
char *name_start;
|
||||
char name[13] = {'\0'};
|
||||
uint64_t tmp;
|
||||
|
||||
assert(bdfid != nullptr);
|
||||
|
||||
if (in_name.size() != 12) {
|
||||
return false;
|
||||
}
|
||||
|
||||
tmp = in_name.copy(name, 12);
|
||||
assert(tmp == 12);
|
||||
|
||||
// BDFID = ((<DOMAIN> & 0xffff) << 32) | ((<BUS> & 0xff) << 8) |
|
||||
// ((device& 0x1f) <<3 ) | (function & 0x7)
|
||||
*bdfid = 0;
|
||||
name_start = name;
|
||||
p = name_start;
|
||||
|
||||
// Match this: XXXX:xx:xx.x
|
||||
tmp = std::strtoul(p, &p, 16);
|
||||
if (*p != ':' || p - name_start != 4) {
|
||||
return false;
|
||||
}
|
||||
*bdfid |= tmp << 32;
|
||||
|
||||
// Match this: xxxx:XX:xx.x
|
||||
p++; // Skip past ':'
|
||||
tmp = std::strtoul(p, &p, 16);
|
||||
if (*p != ':' || p - name_start != 7) {
|
||||
return false;
|
||||
}
|
||||
*bdfid |= tmp << 8;
|
||||
|
||||
// Match this: xxxx:xx:XX.x
|
||||
p++; // Skip past ':'
|
||||
tmp = std::strtoul(p, &p, 16);
|
||||
if (*p != '.' || p - name_start != 10) {
|
||||
return false;
|
||||
}
|
||||
*bdfid |= tmp << 3;
|
||||
|
||||
// Match this: xxxx:xx:xx.X
|
||||
p++; // Skip past '.'
|
||||
tmp = std::strtoul(p, &p, 16);
|
||||
if (*p != '\0' || p - name_start != 12) {
|
||||
return false;
|
||||
}
|
||||
*bdfid |= tmp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint32_t ConstructBDFID(std::string path, uint64_t *bdfid) {
|
||||
assert(bdfid != nullptr);
|
||||
char tpath[256] = {'\0'};
|
||||
ssize_t ret;
|
||||
memset(tpath,0,256);
|
||||
|
||||
ret = readlink(path.c_str(), tpath, 256);
|
||||
|
||||
assert(ret > 0);
|
||||
assert(ret < 256);
|
||||
|
||||
if (ret <= 0 || ret >= 256) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// We are looking for the last element in the path that has the form
|
||||
// XXXX:XX:XX.X, where X is a hex integer (lower case is expected)
|
||||
std::size_t slash_i, end_i;
|
||||
std::string tmp;
|
||||
|
||||
std::string tpath_str(tpath);
|
||||
|
||||
end_i = tpath_str.size() - 1;
|
||||
while (end_i > 0) {
|
||||
slash_i = tpath_str.find_last_of('/', end_i);
|
||||
tmp = tpath_str.substr(slash_i + 1, end_i - slash_i);
|
||||
|
||||
if (bdfid_from_path(tmp, bdfid)) {
|
||||
return 0;
|
||||
}
|
||||
end_i = slash_i - 1;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
RocmSMI::Initialize(uint64_t flags) {
|
||||
auto i = 0;
|
||||
uint32_t ret;
|
||||
int i_ret;
|
||||
|
||||
assert(ref_count_ == 1);
|
||||
if (ref_count_ != 1) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Unexpected: RocmSMI ref_count_ != 1");
|
||||
}
|
||||
|
||||
init_options_ = flags;
|
||||
|
||||
euid_ = geteuid();
|
||||
|
||||
GetEnvVariables();
|
||||
|
||||
while (env_vars_.debug_inf_loop) {}
|
||||
|
||||
while (std::string(kAMDMonitorTypes[i]) != "") {
|
||||
amd_monitor_types_.insert(kAMDMonitorTypes[i]);
|
||||
++i;
|
||||
}
|
||||
|
||||
// DiscoverAmdgpuDevices() will search for devices and monitors and update
|
||||
// internal data structures.
|
||||
ret = DiscoverAmdgpuDevices();
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"DiscoverAmdgpuDevices() failed.");
|
||||
}
|
||||
|
||||
uint64_t bdfid;
|
||||
for (uint32_t i = 0; i < devices_.size(); ++i) {
|
||||
if (ConstructBDFID(devices_[i]->path(), &bdfid) != 0) {
|
||||
std::cerr << "Failed to construct BDFID." << std::endl;
|
||||
ret = 1;
|
||||
}
|
||||
devices_[i]->set_bdfid(bdfid);
|
||||
}
|
||||
if (ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (amdgpu node discovery).");
|
||||
}
|
||||
|
||||
std::map<uint64_t, std::shared_ptr<KFDNode>> tmp_map;
|
||||
i_ret = DiscoverKFDNodes(&tmp_map);
|
||||
if (i_ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (KFD node discovery).");
|
||||
}
|
||||
|
||||
std::map<std::pair<uint32_t, uint32_t>, std::shared_ptr<IOLink>>
|
||||
io_link_map_tmp;
|
||||
i_ret = DiscoverIOLinks(&io_link_map_tmp);
|
||||
if (i_ret != 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"Failed to initialize rocm_smi library (IO Links discovery).");
|
||||
}
|
||||
std::map<std::pair<uint32_t, uint32_t>, std::shared_ptr<IOLink>>::iterator it;
|
||||
for (it = io_link_map_tmp.begin(); it != io_link_map_tmp.end(); it++)
|
||||
io_link_map_[it->first] = it->second;
|
||||
|
||||
std::shared_ptr<amd::smi::Device> dev;
|
||||
|
||||
// Remove any drm nodes that don't have a corresponding readable kfd node.
|
||||
// kfd nodes will not be added if their properties file is not readable.
|
||||
auto dev_iter = devices_.begin();
|
||||
while (dev_iter != devices_.end()) {
|
||||
uint64_t bdfid = (*dev_iter)->bdfid();
|
||||
if (tmp_map.find(bdfid) == tmp_map.end()) {
|
||||
dev_iter = devices_.erase(dev_iter);
|
||||
continue;
|
||||
}
|
||||
dev_iter++;
|
||||
}
|
||||
|
||||
// 1. construct kfd_node_map_ with gpu_id as key and *Device as value
|
||||
// 2. for each kfd node, write the corresponding dv_ind
|
||||
// 3. for each amdgpu device, write the corresponding gpu_id
|
||||
for (uint32_t dv_ind = 0; dv_ind < devices_.size(); ++dv_ind) {
|
||||
dev = devices_[dv_ind];
|
||||
uint64_t bdfid = dev->bdfid();
|
||||
assert(tmp_map.find(bdfid) != tmp_map.end());
|
||||
if (tmp_map.find(bdfid) == tmp_map.end()) {
|
||||
throw amd::smi::rsmi_exception(RSMI_INITIALIZATION_ERROR,
|
||||
"amdgpu device bdfid has no KFD matching node");
|
||||
}
|
||||
|
||||
tmp_map[bdfid]->set_amdgpu_dev_index(dv_ind);
|
||||
dev_ind_to_node_ind_map_[dv_ind] = tmp_map[bdfid]->node_index();
|
||||
uint64_t gpu_id = tmp_map[bdfid]->gpu_id();
|
||||
dev->set_kfd_gpu_id(gpu_id);
|
||||
kfd_node_map_[gpu_id] = tmp_map[bdfid];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
RocmSMI::Cleanup() {
|
||||
devices_.clear();
|
||||
monitors_.clear();
|
||||
|
||||
if (kfd_notif_evt_fh() >= 0) {
|
||||
int ret = close(kfd_notif_evt_fh());
|
||||
if (ret < 0) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_FILE_ERROR,
|
||||
"Failed to close kfd file handle on shutdown.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RocmSMI::RocmSMI(uint64_t flags) : init_options_(flags),
|
||||
kfd_notif_evt_fh_(-1), kfd_notif_evt_fh_refcnt_(0) {
|
||||
}
|
||||
|
||||
RocmSMI::~RocmSMI() {
|
||||
}
|
||||
|
||||
RocmSMI& RocmSMI::getInstance(uint64_t flags) {
|
||||
// Assume c++11 or greater. static objects will be created by only 1 thread
|
||||
// and creation will be thread-safe.
|
||||
static RocmSMI singleton(flags);
|
||||
return singleton;
|
||||
}
|
||||
|
||||
static uint32_t GetEnvVarUInteger(const char *ev_str) {
|
||||
#ifndef DEBUG
|
||||
(void)ev_str;
|
||||
#else
|
||||
ev_str = getenv(ev_str);
|
||||
|
||||
if (ev_str) {
|
||||
int ret = atoi(ev_str);
|
||||
assert(ret >= 0);
|
||||
return static_cast<uint32_t>(ret);
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Get and store env. variables in this method
|
||||
void RocmSMI::GetEnvVariables(void) {
|
||||
#ifndef DEBUG
|
||||
(void)GetEnvVarUInteger(nullptr); // This is to quiet release build warning.
|
||||
env_vars_.debug_output_bitfield = 0;
|
||||
env_vars_.path_DRM_root_override = nullptr;
|
||||
env_vars_.path_HWMon_root_override = nullptr;
|
||||
env_vars_.path_power_root_override = nullptr;
|
||||
env_vars_.enum_override = 0;
|
||||
env_vars_.debug_inf_loop = 0;
|
||||
#else
|
||||
env_vars_.debug_output_bitfield = GetEnvVarUInteger("RSMI_DEBUG_BITFIELD");
|
||||
env_vars_.path_DRM_root_override = getenv("RSMI_DEBUG_DRM_ROOT_OVERRIDE");
|
||||
env_vars_.path_HWMon_root_override = getenv("RSMI_DEBUG_HWMON_ROOT_OVERRIDE");
|
||||
env_vars_.path_power_root_override = getenv("RSMI_DEBUG_PP_ROOT_OVERRIDE");
|
||||
env_vars_.enum_override = GetEnvVarUInteger("RSMI_DEBUG_ENUM_OVERRIDE");
|
||||
env_vars_.debug_inf_loop = GetEnvVarUInteger("RSMI_DEBUG_INFINITE_LOOP");
|
||||
#endif
|
||||
}
|
||||
|
||||
const RocmSMI_env_vars& RocmSMI::getEnv(void) {
|
||||
return env_vars_;
|
||||
}
|
||||
|
||||
std::shared_ptr<Monitor>
|
||||
RocmSMI::FindMonitor(std::string monitor_path) {
|
||||
std::string tmp;
|
||||
std::string err_msg;
|
||||
std::string mon_name;
|
||||
std::shared_ptr<Monitor> m;
|
||||
|
||||
if (!FileExists(monitor_path.c_str())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto mon_dir = opendir(monitor_path.c_str());
|
||||
|
||||
if (mon_dir == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
auto dentry = readdir(mon_dir);
|
||||
|
||||
while (dentry != nullptr) {
|
||||
if (dentry->d_name[0] == '.') {
|
||||
dentry = readdir(mon_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
mon_name = monitor_path;
|
||||
mon_name += "/";
|
||||
mon_name += dentry->d_name;
|
||||
tmp = mon_name + "/name";
|
||||
|
||||
if (FileExists(tmp.c_str())) {
|
||||
std::ifstream fs;
|
||||
fs.open(tmp);
|
||||
|
||||
if (!fs.is_open()) {
|
||||
err_msg = "Failed to open monitor file ";
|
||||
err_msg += tmp;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
std::string mon_type;
|
||||
fs >> mon_type;
|
||||
fs.close();
|
||||
|
||||
if (amd_monitor_types_.find(mon_type) != amd_monitor_types_.end()) {
|
||||
m = std::shared_ptr<Monitor>(new Monitor(mon_name, &env_vars_));
|
||||
m->setTempSensorLabelMap();
|
||||
m->setVoltSensorLabelMap();
|
||||
break;
|
||||
}
|
||||
}
|
||||
dentry = readdir(mon_dir);
|
||||
}
|
||||
|
||||
if (closedir(mon_dir)) {
|
||||
err_msg = "Failed to close monitor directory ";
|
||||
err_msg += kPathHWMonRoot;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return m;
|
||||
}
|
||||
void
|
||||
RocmSMI::AddToDeviceList(std::string dev_name) {
|
||||
auto dev_path = std::string(kPathDRMRoot);
|
||||
dev_path += "/";
|
||||
dev_path += dev_name;
|
||||
|
||||
auto dev = std::shared_ptr<Device>(new Device(dev_path, &env_vars_));
|
||||
|
||||
std::shared_ptr<Monitor> m = FindMonitor(dev_path + "/device/hwmon");
|
||||
dev->set_monitor(m);
|
||||
|
||||
std::string d_name = dev_name;
|
||||
uint32_t card_indx = GetDeviceIndex(d_name);
|
||||
dev->set_drm_render_minor(GetDrmRenderMinor(dev_path));
|
||||
dev->set_card_index(card_indx);
|
||||
GetSupportedEventGroups(card_indx, dev->supported_event_groups());
|
||||
|
||||
devices_.push_back(dev);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static const uint32_t kAmdGpuId = 0x1002;
|
||||
|
||||
static bool isAMDGPU(std::string dev_path) {
|
||||
std::string vend_path = dev_path + "/device/vendor";
|
||||
std::string vbios_v_path = dev_path + "/device/vbios_version";
|
||||
if (!FileExists(vend_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!FileExists(vbios_v_path.c_str())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::ifstream fs;
|
||||
fs.open(vend_path);
|
||||
|
||||
if (!fs.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t vendor_id;
|
||||
|
||||
fs >> std::hex >> vendor_id;
|
||||
|
||||
fs.close();
|
||||
|
||||
if (vendor_id == kAmdGpuId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t RocmSMI::DiscoverAmdgpuDevices(void) {
|
||||
std::string err_msg;
|
||||
uint32_t count = 0;
|
||||
|
||||
// If this gets called more than once, clear previous findings.
|
||||
devices_.clear();
|
||||
monitors_.clear();
|
||||
|
||||
auto drm_dir = opendir(kPathDRMRoot);
|
||||
if (drm_dir == nullptr) {
|
||||
err_msg = "Failed to open drm root directory ";
|
||||
err_msg += kPathDRMRoot;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto dentry = readdir(drm_dir);
|
||||
|
||||
while (dentry != nullptr) {
|
||||
if (memcmp(dentry->d_name, kDeviceNamePrefix, strlen(kDeviceNamePrefix))
|
||||
== 0) {
|
||||
if ((strcmp(dentry->d_name, ".") == 0) ||
|
||||
(strcmp(dentry->d_name, "..") == 0))
|
||||
continue;
|
||||
count++;
|
||||
}
|
||||
dentry = readdir(drm_dir);
|
||||
}
|
||||
|
||||
for (uint32_t node_id = 0; node_id < count; node_id++) {
|
||||
std::string path = kPathDRMRoot;
|
||||
path += "/card";
|
||||
path += std::to_string(node_id);
|
||||
if (isAMDGPU(path) ||
|
||||
(init_options_ & RSMI_INIT_FLAG_ALL_GPUS)) {
|
||||
std::string d_name = "card";
|
||||
d_name += std::to_string(node_id);
|
||||
AddToDeviceList(d_name);
|
||||
}
|
||||
}
|
||||
|
||||
if (closedir(drm_dir)) {
|
||||
err_msg = "Failed to close drm root directory ";
|
||||
err_msg += kPathDRMRoot;
|
||||
err_msg += ".";
|
||||
perror(err_msg.c_str());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Since these sysfs files require sudo access, we won't discover them
|
||||
// with rsmi_init() (and thus always require the user to use "sudo".
|
||||
// Instead, we will discover() all the power monitors the first time
|
||||
// they are needed and then check for previous discovery on each subsequent
|
||||
// call.
|
||||
int RocmSMI::DiscoverAMDPowerMonitors(bool force_update) {
|
||||
if (force_update) {
|
||||
power_mons_.clear();
|
||||
}
|
||||
|
||||
if (power_mons_.size() != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
auto dri_dir = opendir(kPathPowerRoot);
|
||||
|
||||
if (dri_dir == nullptr) {
|
||||
return errno;
|
||||
}
|
||||
auto dentry = readdir(dri_dir);
|
||||
|
||||
std::string mon_name;
|
||||
std::string tmp;
|
||||
|
||||
while (dentry != nullptr) {
|
||||
if (dentry->d_name[0] == '.') {
|
||||
dentry = readdir(dri_dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
mon_name = kPathPowerRoot;
|
||||
mon_name += "/";
|
||||
mon_name += dentry->d_name;
|
||||
tmp = mon_name + "/amdgpu_pm_info";
|
||||
|
||||
if (FileExists(tmp.c_str())) {
|
||||
std::shared_ptr<PowerMon> mon =
|
||||
std::shared_ptr<PowerMon>(new PowerMon(mon_name, &env_vars_));
|
||||
power_mons_.push_back(mon);
|
||||
mon->set_dev_index(GetDeviceIndex(dentry->d_name));
|
||||
}
|
||||
dentry = readdir(dri_dir);
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
if (closedir(dri_dir)) {
|
||||
power_mons_.clear();
|
||||
return errno;
|
||||
}
|
||||
|
||||
for (auto m : power_mons_) {
|
||||
for (auto d : devices_) {
|
||||
if (m->dev_index() == d->index()) {
|
||||
d->set_power_monitor(m);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t RocmSMI::IterateSMIDevices(
|
||||
std::function<uint32_t(std::shared_ptr<Device>&, void *)> func, void *p) {
|
||||
if (func == nullptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
auto d = devices_.begin();
|
||||
uint32_t ret;
|
||||
|
||||
while (d != devices_.end()) {
|
||||
ret = func(*d, p);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
++d;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RocmSMI::get_node_index(uint32_t dv_ind, uint32_t *node_ind) {
|
||||
if (dev_ind_to_node_ind_map_.find(dv_ind) == dev_ind_to_node_ind_map_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*node_ind = dev_ind_to_node_ind_map_[dv_ind];
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RocmSMI::get_io_link_weight(uint32_t node_from, uint32_t node_to,
|
||||
uint64_t *weight) {
|
||||
assert(weight != nullptr);
|
||||
if (weight == nullptr) {
|
||||
return EINVAL;
|
||||
}
|
||||
if (io_link_map_.find(std::make_pair(node_from, node_to)) ==
|
||||
io_link_map_.end()) {
|
||||
return EINVAL;
|
||||
}
|
||||
*weight = io_link_map_[std::make_pair(node_from, node_to)]->weight();
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -1,639 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2017, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <dirent.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <regex> // NOLINT
|
||||
#include <vector>
|
||||
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_monitor.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
struct MonitorNameEntry {
|
||||
MonitorTypes type;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
|
||||
static const char *kMonTempFName = "temp#_input";
|
||||
static const char *kMonFanSpeedFName = "pwm#";
|
||||
static const char *kMonMaxFanSpeedFName = "pwm#_max";
|
||||
static const char *kMonFanRPMsName = "fan#_input";
|
||||
static const char *kMonFanControlEnableName = "pwm#_enable";
|
||||
static const char *kMonNameFName = "name";
|
||||
static const char *kMonPowerCapDefaultName = "power#_cap_default";
|
||||
static const char *kMonPowerCapName = "power#_cap";
|
||||
static const char *kMonPowerCapMaxName = "power#_cap_max";
|
||||
static const char *kMonPowerCapMinName = "power#_cap_min";
|
||||
static const char *kMonPowerAveName = "power#_average";
|
||||
static const char *kMonTempMaxName = "temp#_max";
|
||||
static const char *kMonTempMinName = "temp#_min";
|
||||
static const char *kMonTempMaxHystName = "temp#_max_hyst";
|
||||
static const char *kMonTempMinHystName = "temp#_min_hyst";
|
||||
static const char *kMonTempCriticalName = "temp#_crit";
|
||||
static const char *kMonTempCriticalHystName = "temp#_crit_hyst";
|
||||
static const char *kMonTempEmergencyName = "temp#_emergency";
|
||||
static const char *kMonTempEmergencyHystName = "temp#_emergency_hyst";
|
||||
static const char *kMonTempCritMinName = "temp#_lcrit";
|
||||
static const char *kMonTempCritMinHystName = "temp#_lcrit_hyst";
|
||||
static const char *kMonTempOffsetName = "temp#_offset";
|
||||
static const char *kMonTempLowestName = "temp#_lowest";
|
||||
static const char *kMonTempHighestName = "temp#_highest";
|
||||
static const char *kMonTempLabelName = "temp#_label";
|
||||
static const char *kMonVoltFName = "in#_input";
|
||||
static const char *kMonVoltMinName = "in#_min";
|
||||
static const char *kMonVoltMinCritName = "in#_lcrit";
|
||||
static const char *kMonVoltMaxName = "in#_max";
|
||||
static const char *kMonVoltMaxCritName = "in#_crit";
|
||||
static const char *kMonVoltAverageName = "in#_average";
|
||||
static const char *kMonVoltLowestName = "in#_lowest";
|
||||
static const char *kMonVoltHighestName = "in#_highest";
|
||||
static const char *kMonVoltLabelName = "in#_label";
|
||||
|
||||
static const char *kTempSensorTypeMemoryName = "mem";
|
||||
static const char *kTempSensorTypeJunctionName = "junction";
|
||||
static const char *kTempSensorTypeEdgeName = "edge";
|
||||
|
||||
static const char *kTempSensorTypeVddgfxName = "vddgfx";
|
||||
|
||||
|
||||
static const std::map<std::string, rsmi_temperature_type_t>
|
||||
kTempSensorNameMap = {
|
||||
{kTempSensorTypeMemoryName, RSMI_TEMP_TYPE_MEMORY},
|
||||
{kTempSensorTypeJunctionName, RSMI_TEMP_TYPE_JUNCTION},
|
||||
{kTempSensorTypeEdgeName, RSMI_TEMP_TYPE_EDGE},
|
||||
};
|
||||
|
||||
static const std::map<std::string, rsmi_voltage_type_t>
|
||||
kVoltSensorNameMap = {
|
||||
{kTempSensorTypeVddgfxName, RSMI_VOLT_TYPE_VDDGFX},
|
||||
};
|
||||
|
||||
static const std::map<MonitorTypes, const char *> kMonitorNameMap = {
|
||||
{kMonName, kMonNameFName},
|
||||
{kMonTemp, kMonTempFName},
|
||||
{kMonFanSpeed, kMonFanSpeedFName},
|
||||
{kMonFanCntrlEnable, kMonFanControlEnableName},
|
||||
{kMonMaxFanSpeed, kMonMaxFanSpeedFName},
|
||||
{kMonFanRPMs, kMonFanRPMsName},
|
||||
{kMonPowerCap, kMonPowerCapName},
|
||||
{kMonPowerCapDefault, kMonPowerCapDefaultName},
|
||||
{kMonPowerCapMax, kMonPowerCapMaxName},
|
||||
{kMonPowerCapMin, kMonPowerCapMinName},
|
||||
{kMonPowerAve, kMonPowerAveName},
|
||||
{kMonTempMax, kMonTempMaxName},
|
||||
{kMonTempMin, kMonTempMinName},
|
||||
{kMonTempMaxHyst, kMonTempMaxHystName},
|
||||
{kMonTempMinHyst, kMonTempMinHystName},
|
||||
{kMonTempCritical, kMonTempCriticalName},
|
||||
{kMonTempCriticalHyst, kMonTempCriticalHystName},
|
||||
{kMonTempEmergency, kMonTempEmergencyName},
|
||||
{kMonTempEmergencyHyst, kMonTempEmergencyHystName},
|
||||
{kMonTempCritMin, kMonTempCritMinName},
|
||||
{kMonTempCritMinHyst, kMonTempCritMinHystName},
|
||||
{kMonTempOffset, kMonTempOffsetName},
|
||||
{kMonTempLowest, kMonTempLowestName},
|
||||
{kMonTempHighest, kMonTempHighestName},
|
||||
{kMonTempLabel, kMonTempLabelName},
|
||||
{kMonVolt, kMonVoltFName},
|
||||
{kMonVoltMin, kMonVoltMinName},
|
||||
{kMonVoltMinCrit, kMonVoltMinCritName},
|
||||
{kMonVoltMax, kMonVoltMaxName},
|
||||
{kMonVoltMaxCrit, kMonVoltMaxCritName},
|
||||
{kMonVoltAverage, kMonVoltAverageName},
|
||||
{kMonVoltLowest, kMonVoltLowestName},
|
||||
{kMonVoltHighest, kMonVoltHighestName},
|
||||
{kMonVoltLabel, kMonVoltLabelName},
|
||||
};
|
||||
|
||||
static std::map<MonitorTypes, uint64_t> kMonInfoVarTypeToRSMIVariant = {
|
||||
// rsmi_temperature_metric_t
|
||||
{kMonTemp, RSMI_TEMP_CURRENT},
|
||||
{kMonTempMax, RSMI_TEMP_MAX},
|
||||
{kMonTempMin, RSMI_TEMP_MIN},
|
||||
{kMonTempMaxHyst, RSMI_TEMP_MAX_HYST},
|
||||
{kMonTempMinHyst, RSMI_TEMP_MIN_HYST},
|
||||
{kMonTempCritical, RSMI_TEMP_CRITICAL},
|
||||
{kMonTempCriticalHyst, RSMI_TEMP_CRITICAL_HYST},
|
||||
{kMonTempEmergency, RSMI_TEMP_EMERGENCY},
|
||||
{kMonTempEmergencyHyst, RSMI_TEMP_EMERGENCY_HYST},
|
||||
{kMonTempCritMin, RSMI_TEMP_CRIT_MIN},
|
||||
{kMonTempCritMinHyst, RSMI_TEMP_CRIT_MIN_HYST},
|
||||
{kMonTempOffset, RSMI_TEMP_OFFSET},
|
||||
{kMonTempLowest, RSMI_TEMP_LOWEST},
|
||||
{kMonTempHighest, RSMI_TEMP_HIGHEST},
|
||||
{kMonInvalid, RSMI_DEFAULT_VARIANT},
|
||||
// rsmi_voltage_metric_t
|
||||
{kMonVolt, RSMI_VOLT_CURRENT},
|
||||
{kMonVoltMin, RSMI_VOLT_MIN},
|
||||
{kMonVoltMinCrit, RSMI_VOLT_MIN_CRIT},
|
||||
{kMonVoltMax, RSMI_VOLT_MAX},
|
||||
{kMonVoltMaxCrit, RSMI_VOLT_MAX_CRIT},
|
||||
{kMonVoltAverage, RSMI_VOLT_AVERAGE},
|
||||
{kMonVoltLowest, RSMI_VOLT_LOWEST},
|
||||
{kMonVoltHighest, RSMI_VOLT_HIGHEST},
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
std::vector<const char *> mandatory_depends;
|
||||
std::vector<MonitorTypes> variants;
|
||||
} monitor_depends_t;
|
||||
|
||||
static const std::map<const char *, monitor_depends_t> kMonFuncDependsMap = {
|
||||
{"rsmi_dev_power_ave_get", { .mandatory_depends = {kMonPowerAveName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_power_cap_get", { .mandatory_depends = {kMonPowerCapName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_power_cap_default_get", { .mandatory_depends = {kMonPowerCapDefaultName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_power_cap_range_get", { .mandatory_depends =
|
||||
{kMonPowerCapMaxName,
|
||||
kMonPowerCapMinName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_power_cap_set", { .mandatory_depends =
|
||||
{kMonPowerCapMaxName,
|
||||
kMonPowerCapMinName,
|
||||
kMonPowerCapName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
|
||||
{"rsmi_dev_fan_rpms_get", { .mandatory_depends = {kMonFanRPMsName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_fan_speed_get", { .mandatory_depends = {kMonFanSpeedFName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_fan_speed_max_get", { .mandatory_depends =
|
||||
{kMonMaxFanSpeedFName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_temp_metric_get", { .mandatory_depends =
|
||||
{kMonTempLabelName},
|
||||
.variants = {kMonTemp,
|
||||
kMonTempMax,
|
||||
kMonTempMin,
|
||||
kMonTempMaxHyst,
|
||||
kMonTempMinHyst,
|
||||
kMonTempCritical,
|
||||
kMonTempCriticalHyst,
|
||||
kMonTempEmergency,
|
||||
kMonTempEmergencyHyst,
|
||||
kMonTempCritMin,
|
||||
kMonTempCritMinHyst,
|
||||
kMonTempOffset,
|
||||
kMonTempLowest,
|
||||
kMonTempHighest,
|
||||
},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_fan_reset", { .mandatory_depends =
|
||||
{kMonFanControlEnableName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_fan_speed_set", { .mandatory_depends =
|
||||
{kMonMaxFanSpeedFName,
|
||||
kMonFanControlEnableName,
|
||||
kMonFanSpeedFName},
|
||||
.variants = {kMonInvalid},
|
||||
}
|
||||
},
|
||||
{"rsmi_dev_volt_metric_get", { .mandatory_depends =
|
||||
{kMonVoltLabelName},
|
||||
.variants = {kMonVolt,
|
||||
kMonVoltMin,
|
||||
kMonVoltMinCrit,
|
||||
kMonVoltMax,
|
||||
kMonVoltMaxCrit,
|
||||
kMonVoltAverage,
|
||||
kMonVoltLowest,
|
||||
kMonVoltHighest,
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
Monitor::Monitor(std::string path, RocmSMI_env_vars const *e) :
|
||||
path_(path), env_(e) {
|
||||
#ifndef DEBUG
|
||||
env_ = nullptr;
|
||||
#endif
|
||||
}
|
||||
Monitor::~Monitor(void) {
|
||||
}
|
||||
|
||||
std::string
|
||||
Monitor::MakeMonitorPath(MonitorTypes type, uint32_t sensor_id) {
|
||||
std::string tempPath = path_;
|
||||
std::string fn = kMonitorNameMap.at(type);
|
||||
|
||||
std::replace(fn.begin(), fn.end(), '#', static_cast<char>('0' + sensor_id));
|
||||
|
||||
tempPath += "/";
|
||||
tempPath += fn;
|
||||
|
||||
return tempPath;
|
||||
}
|
||||
|
||||
int Monitor::writeMonitor(MonitorTypes type, uint32_t sensor_id,
|
||||
std::string val) {
|
||||
std::string sysfs_path = MakeMonitorPath(type, sensor_id);
|
||||
|
||||
DBG_FILE_ERROR(sysfs_path, &val)
|
||||
return WriteSysfsStr(sysfs_path, val);
|
||||
}
|
||||
|
||||
// This string version should work for all valid monitor types
|
||||
int Monitor::readMonitor(MonitorTypes type, uint32_t sensor_id,
|
||||
std::string *val) {
|
||||
assert(val != nullptr);
|
||||
|
||||
std::string temp_str;
|
||||
std::string sysfs_path = MakeMonitorPath(type, sensor_id);
|
||||
|
||||
DBG_FILE_ERROR(sysfs_path, (std::string *)nullptr)
|
||||
return ReadSysfsStr(sysfs_path, val);
|
||||
}
|
||||
|
||||
int32_t
|
||||
Monitor::setTempSensorLabelMap(void) {
|
||||
std::string type_str;
|
||||
int ret;
|
||||
|
||||
if (temp_type_index_map_.size() > 0) {
|
||||
return 0; // We've already filled in the map
|
||||
}
|
||||
auto add_temp_sensor_entry = [&](uint32_t file_index) {
|
||||
ret = readMonitor(kMonTempLabel, file_index, &type_str);
|
||||
rsmi_temperature_type_t t_type;
|
||||
|
||||
// If readMonitor fails, there is no label file for the file_index.
|
||||
// In that case, map the type to file index 0, which is not supported
|
||||
// and will fail appropriately later when we check for support.
|
||||
if (ret) {
|
||||
index_temp_type_map_.insert({file_index, RSMI_TEMP_TYPE_INVALID});
|
||||
} else {
|
||||
t_type = kTempSensorNameMap.at(type_str);
|
||||
temp_type_index_map_[t_type] = file_index;
|
||||
index_temp_type_map_.insert({file_index, t_type});
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
for (uint32_t t = RSMI_TEMP_TYPE_FIRST; t <= RSMI_TEMP_TYPE_LAST; ++t) {
|
||||
temp_type_index_map_.insert(
|
||||
{static_cast<rsmi_temperature_type_t>(t), RSMI_TEMP_TYPE_INVALID});
|
||||
}
|
||||
for (uint32_t i = 1; i <= RSMI_TEMP_TYPE_LAST + 1; ++i) {
|
||||
ret = add_temp_sensor_entry(i);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t
|
||||
Monitor::setVoltSensorLabelMap(void) {
|
||||
std::string type_str;
|
||||
int ret;
|
||||
|
||||
if (volt_type_index_map_.size() > 0) {
|
||||
return 0; // We've already filled in the map
|
||||
}
|
||||
auto add_volt_sensor_entry = [&](uint32_t file_index) {
|
||||
ret = readMonitor(kMonVoltLabel, file_index, &type_str);
|
||||
rsmi_voltage_type_t t_type = kVoltSensorNameMap.at(type_str);
|
||||
// If readMonitor fails, there is no label file for the file_index.
|
||||
// In that case, map the type to file index 0, which is not supported
|
||||
// and will fail appropriately later when we check for support.
|
||||
if (ret) {
|
||||
volt_type_index_map_.insert({t_type, 0});
|
||||
index_volt_type_map_.insert({file_index, RSMI_VOLT_TYPE_INVALID});
|
||||
} else {
|
||||
volt_type_index_map_.insert({t_type, file_index});
|
||||
index_volt_type_map_.insert({file_index, t_type});
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
for (uint32_t i = 0; i < RSMI_VOLT_TYPE_LAST + 1; ++i) {
|
||||
ret = add_volt_sensor_entry(i);
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int get_supported_sensors(std::string dir_path, std::string fn_reg_ex,
|
||||
std::vector<uint64_t> *sensors) {
|
||||
auto hwmon_dir = opendir(dir_path.c_str());
|
||||
assert(hwmon_dir != nullptr);
|
||||
assert(sensors != nullptr);
|
||||
|
||||
sensors->clear();
|
||||
|
||||
std::string::size_type pos = fn_reg_ex.find('#');
|
||||
|
||||
if (pos == std::string::npos) {
|
||||
closedir(hwmon_dir);
|
||||
return -1;
|
||||
}
|
||||
fn_reg_ex.erase(pos, 1);
|
||||
fn_reg_ex.insert(pos, "([0-9]+)");
|
||||
fn_reg_ex = "\\b" + fn_reg_ex + "\\b";
|
||||
|
||||
auto dentry = readdir(hwmon_dir);
|
||||
std::smatch match;
|
||||
uint64_t mon_val;
|
||||
|
||||
char *endptr;
|
||||
try {
|
||||
std::regex re(fn_reg_ex);
|
||||
std::string fn;
|
||||
|
||||
while (dentry != nullptr) {
|
||||
fn = dentry->d_name;
|
||||
if (std::regex_search(fn, match, re)) {
|
||||
assert(match.size() == 2); // 1 for whole match + 1 for sub-match
|
||||
errno = 0;
|
||||
std::string val_str(match.str(1));
|
||||
mon_val = strtoul(val_str.c_str(), &endptr, 10);
|
||||
assert(errno == 0);
|
||||
assert(*endptr == '\0');
|
||||
if (errno) {
|
||||
closedir(hwmon_dir);
|
||||
return -2;
|
||||
}
|
||||
sensors->push_back(mon_val);
|
||||
}
|
||||
dentry = readdir(hwmon_dir);
|
||||
}
|
||||
if (closedir(hwmon_dir)) {
|
||||
return errno;
|
||||
}
|
||||
} catch (std::regex_error& e) {
|
||||
std::cout << "Regular expression error:" << std::endl;
|
||||
std::cout << e.what() << std::endl;
|
||||
std::cout << "Regex error code: " << e.code() << std::endl;
|
||||
return -3;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Monitor::getTempSensorIndex(rsmi_temperature_type_t type) {
|
||||
return temp_type_index_map_.at(type);
|
||||
}
|
||||
|
||||
rsmi_temperature_type_t
|
||||
Monitor::getTempSensorEnum(uint64_t ind) {
|
||||
return index_temp_type_map_.at(ind);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
Monitor::getVoltSensorIndex(rsmi_voltage_type_t type) {
|
||||
return volt_type_index_map_.at(type);
|
||||
}
|
||||
|
||||
rsmi_voltage_type_t
|
||||
Monitor::getVoltSensorEnum(uint64_t ind) {
|
||||
return index_volt_type_map_.at(ind);
|
||||
}
|
||||
|
||||
static std::vector<uint64_t> get_intersection(std::vector<uint64_t> *v1,
|
||||
std::vector<uint64_t> *v2) {
|
||||
assert(v1 != nullptr);
|
||||
assert(v2 != nullptr);
|
||||
std::vector<uint64_t> intersect;
|
||||
|
||||
std::sort(v1->begin(), v1->end());
|
||||
std::sort(v2->begin(), v2->end());
|
||||
|
||||
std::set_intersection(v1->begin(), v1->end(), v2->begin(), v2->end(),
|
||||
std::back_inserter(intersect));
|
||||
return intersect;
|
||||
}
|
||||
|
||||
// Use this enum to encode the monitor type into the monitor ID.
|
||||
// We can later use this to convert to rsmi-api sensor types; for exampple,
|
||||
// rsmi_temperature_type_t, which is what the caller will expect. Add
|
||||
// new types as needed.
|
||||
|
||||
typedef enum {
|
||||
eDefaultMonitor = 0,
|
||||
eTempMonitor,
|
||||
eVoltMonitor,
|
||||
} monitor_types;
|
||||
|
||||
static monitor_types getFuncType(std::string f_name) {
|
||||
monitor_types ret = eDefaultMonitor;
|
||||
|
||||
if (f_name.compare("rsmi_dev_temp_metric_get") == 0) {
|
||||
ret = eTempMonitor;
|
||||
}
|
||||
if (f_name.compare("rsmi_dev_volt_metric_get") == 0) {
|
||||
ret = eVoltMonitor;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void Monitor::fillSupportedFuncs(SupportedFuncMap *supported_funcs) {
|
||||
std::map<const char *, monitor_depends_t>::const_iterator it =
|
||||
kMonFuncDependsMap.begin();
|
||||
std::string mon_root = path_;
|
||||
bool mand_depends_met;
|
||||
std::shared_ptr<VariantMap> supported_variants;
|
||||
std::vector<uint64_t> sensors_i;
|
||||
std::vector<uint64_t> intersect;
|
||||
int ret;
|
||||
monitor_types m_type;
|
||||
|
||||
assert(supported_funcs != nullptr);
|
||||
|
||||
while (it != kMonFuncDependsMap.end()) {
|
||||
// First, see if all the mandatory dependencies are there
|
||||
std::vector<const char *>::const_iterator dep =
|
||||
it->second.mandatory_depends.begin();
|
||||
|
||||
m_type = getFuncType(it->first);
|
||||
mand_depends_met = true;
|
||||
|
||||
// Initialize "intersect". A monitor is considered supported if all of its
|
||||
// dependency monitors with the same sensor index are present. So we
|
||||
// initialize "intersect" with the set of sensors that exist for the first
|
||||
// mandatory monitor, and take intersection of that with the subsequent
|
||||
// dependency monitors. The main assumption here is that
|
||||
// variant_<sensor_i>'s sensor-based dependencies have the same index i;
|
||||
// in other words, variant_i is not dependent on a sensor j, j != i
|
||||
|
||||
// Initialize intersect with the available monitors for the first
|
||||
// mandatory dependency.
|
||||
ret = get_supported_sensors(mon_root + "/", *dep, &intersect);
|
||||
std::string dep_path;
|
||||
if (ret == -1) {
|
||||
// In this case, the dependency is not sensor-specific, so just
|
||||
// see if the file exists.
|
||||
dep_path = mon_root + "/" + *dep;
|
||||
if (!FileExists(dep_path.c_str())) {
|
||||
mand_depends_met = false;
|
||||
}
|
||||
} else if (ret <= -2) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_INTERNAL_EXCEPTION,
|
||||
"Failed to parse monitor file name: " + dep_path);
|
||||
}
|
||||
dep++;
|
||||
|
||||
while (mand_depends_met && dep != it->second.mandatory_depends.end()) {
|
||||
ret = get_supported_sensors(mon_root + "/", *dep, &sensors_i);
|
||||
|
||||
if (ret == 0) {
|
||||
intersect = get_intersection(&sensors_i, &intersect);
|
||||
} else if (ret == -1) {
|
||||
// In this case, the dependency is not sensor-specific, so just
|
||||
// see if the file exists.
|
||||
std::string dep_path = mon_root + "/" + *dep;
|
||||
if (!FileExists(dep_path.c_str())) {
|
||||
mand_depends_met = false;
|
||||
break;
|
||||
}
|
||||
} else if (ret <= -2) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_INTERNAL_EXCEPTION,
|
||||
"Failed to parse monitor file name: " + dep_path);
|
||||
}
|
||||
|
||||
dep++;
|
||||
}
|
||||
|
||||
if (!mand_depends_met) {
|
||||
it++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// "intersect" holds the set of sensors for the mandatory dependencies
|
||||
// that exist.
|
||||
|
||||
std::vector<MonitorTypes>::const_iterator var =
|
||||
it->second.variants.begin();
|
||||
supported_variants = std::make_shared<VariantMap>();
|
||||
|
||||
std::vector<uint64_t> supported_monitors;
|
||||
|
||||
for (; var != it->second.variants.end(); var++) {
|
||||
if (*var != kMonInvalid) {
|
||||
ret = get_supported_sensors(mon_root + "/",
|
||||
kMonitorNameMap.at(*var), &sensors_i);
|
||||
|
||||
if (ret == 0) {
|
||||
supported_monitors = get_intersection(&sensors_i, &intersect);
|
||||
} else if (ret <= -2) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_INTERNAL_EXCEPTION,
|
||||
"Failed to parse monitor file name: " + dep_path);
|
||||
}
|
||||
} else {
|
||||
supported_monitors = intersect;
|
||||
}
|
||||
if (supported_monitors.size() > 0) {
|
||||
for (uint32_t i = 0; i < supported_monitors.size(); ++i) {
|
||||
if (m_type == eDefaultMonitor) {
|
||||
assert(supported_monitors[i] > 0);
|
||||
supported_monitors[i] |=
|
||||
(supported_monitors[i] - 1) << MONITOR_TYPE_BIT_POSITION;
|
||||
} else if (m_type == eTempMonitor) {
|
||||
// Temp sensor file names are 1-based
|
||||
assert(supported_monitors[i] > 0);
|
||||
supported_monitors[i] |=
|
||||
static_cast<uint64_t>(getTempSensorEnum(supported_monitors[i]))
|
||||
<< MONITOR_TYPE_BIT_POSITION;
|
||||
} else if (m_type == eVoltMonitor) {
|
||||
// Voltage sensor file names are 0-based
|
||||
supported_monitors[i] |=
|
||||
static_cast<uint64_t>(getVoltSensorEnum(supported_monitors[i]))
|
||||
<< MONITOR_TYPE_BIT_POSITION;
|
||||
} else {
|
||||
assert(false); // Unexpected monitor type
|
||||
}
|
||||
}
|
||||
(*supported_variants)[kMonInfoVarTypeToRSMIVariant.at(*var)] =
|
||||
std::make_shared<SubVariant>(supported_monitors);
|
||||
}
|
||||
}
|
||||
|
||||
if (it->second.variants.size() == 0) {
|
||||
(*supported_funcs)[it->first] = nullptr;
|
||||
supported_variants = nullptr; // Invoke destructor
|
||||
} else if ((*supported_variants).size() > 0) {
|
||||
(*supported_funcs)[it->first] = supported_variants;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2017, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_monitor.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_common.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
|
||||
static const char *kPowerMonPMName = "amdgpu_pm_info";
|
||||
|
||||
// Using this map in case we add other files from dri directory to parse.
|
||||
static const std::map<PowerMonTypes, const char *> kMonitorNameMap = {
|
||||
{kPowerMaxGPUPower, kPowerMonPMName},
|
||||
};
|
||||
|
||||
PowerMon::PowerMon(std::string path, RocmSMI_env_vars const *e) :
|
||||
path_(path), env_(e) {
|
||||
}
|
||||
PowerMon::~PowerMon(void) {
|
||||
}
|
||||
|
||||
static int parse_power_str(std::string s, PowerMonTypes type, uint64_t *val) {
|
||||
std::stringstream ss(s);
|
||||
std::string ln;
|
||||
std::string search_str;
|
||||
|
||||
assert(val != nullptr);
|
||||
|
||||
switch (type) {
|
||||
case kPowerMaxGPUPower:
|
||||
search_str = "(max GPU)";
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false); // Invalid search Power type requested
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
while (std::getline(ss, ln)) {
|
||||
if (ln.rfind(search_str) != std::string::npos) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
return EPERM;
|
||||
}
|
||||
|
||||
ss.clear();
|
||||
std::stringstream l_ss;
|
||||
|
||||
l_ss << ln;
|
||||
|
||||
double num_units;
|
||||
std::string sz;
|
||||
|
||||
switch (type) {
|
||||
case kPowerMaxGPUPower:
|
||||
l_ss >> num_units;
|
||||
l_ss >> sz;
|
||||
assert(sz == "W"); // We only expect Watts at this time
|
||||
if (sz != "W") {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_UNEXPECTED_DATA,
|
||||
__FUNCTION__);
|
||||
}
|
||||
|
||||
if (num_units > static_cast<long double>(0xFFFFFFFFFFFFFFFF)/1000) {
|
||||
throw amd::smi::rsmi_exception(RSMI_STATUS_UNEXPECTED_DATA,
|
||||
__FUNCTION__);
|
||||
}
|
||||
*val = static_cast<uint64_t>(num_units * 1000); // Convert W to mW
|
||||
break;
|
||||
|
||||
default:
|
||||
assert(false); // Invalid search Power type requested
|
||||
return EINVAL;
|
||||
}
|
||||
ss.clear();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int PowerMon::readPowerValue(PowerMonTypes type, uint64_t *power) {
|
||||
auto tempPath = path_;
|
||||
std::string fstr;
|
||||
|
||||
assert(power != nullptr);
|
||||
|
||||
tempPath += "/";
|
||||
tempPath += kMonitorNameMap.at(type);
|
||||
|
||||
DBG_FILE_ERROR(tempPath, (std::string *)nullptr)
|
||||
int ret = ReadSysfsStr(tempPath, &fstr);
|
||||
|
||||
if (ret) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
return parse_power_str(fstr, type, power);
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
@@ -1,238 +0,0 @@
|
||||
/*
|
||||
* =============================================================================
|
||||
* The University of Illinois/NCSA
|
||||
* Open Source License (NCSA)
|
||||
*
|
||||
* Copyright (c) 2018, Advanced Micro Devices, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Developed by:
|
||||
*
|
||||
* AMD Research and AMD ROC Software Development
|
||||
*
|
||||
* Advanced Micro Devices, Inc.
|
||||
*
|
||||
* www.amd.com
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal with 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:
|
||||
*
|
||||
* - Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimers.
|
||||
* - Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimers in
|
||||
* the documentation and/or other materials provided with the distribution.
|
||||
* - Neither the names of <Name of Development Group, Name of Institution>,
|
||||
* nor the names of its contributors may be used to endorse or promote
|
||||
* products derived from this Software without specific prior written
|
||||
* permission.
|
||||
*
|
||||
* 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 CONTRIBUTORS 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 WITH THE SOFTWARE.
|
||||
*
|
||||
*/
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "rocm_smi/rocm_smi.h"
|
||||
#include "rocm_smi/rocm_smi_utils.h"
|
||||
#include "rocm_smi/rocm_smi_exception.h"
|
||||
#include "rocm_smi/rocm_smi_main.h"
|
||||
#include "rocm_smi/rocm_smi_device.h"
|
||||
|
||||
namespace amd {
|
||||
namespace smi {
|
||||
|
||||
// Return 0 if same file, 1 if not, and -1 for error
|
||||
int SameFile(const std::string fileA, const std::string fileB) {
|
||||
struct stat aStat;
|
||||
struct stat bStat;
|
||||
int ret;
|
||||
|
||||
ret = stat(fileA.c_str(), &aStat);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = stat(fileB.c_str(), &bStat);
|
||||
if (ret) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (aStat.st_dev != bStat.st_dev) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (aStat.st_ino != bStat.st_ino) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FileExists(char const *filename) {
|
||||
struct stat buf;
|
||||
return (stat(filename, &buf) == 0);
|
||||
}
|
||||
|
||||
int isRegularFile(std::string fname, bool *is_reg) {
|
||||
struct stat file_stat;
|
||||
int ret;
|
||||
|
||||
assert(is_reg != nullptr);
|
||||
|
||||
ret = stat(fname.c_str(), &file_stat);
|
||||
if (ret) {
|
||||
return errno;
|
||||
}
|
||||
*is_reg = S_ISREG(file_stat.st_mode);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int WriteSysfsStr(std::string path, std::string val) {
|
||||
std::ofstream fs;
|
||||
int ret = 0;
|
||||
|
||||
fs.open(path);
|
||||
if (!fs.is_open()) {
|
||||
ret = errno;
|
||||
errno = 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
fs << val;
|
||||
fs.close();
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ReadSysfsStr(std::string path, std::string *retStr) {
|
||||
std::stringstream ss;
|
||||
int ret = 0;
|
||||
|
||||
assert(retStr != nullptr);
|
||||
|
||||
std::ifstream fs;
|
||||
fs.open(path);
|
||||
|
||||
if (!fs.is_open()) {
|
||||
ret = errno;
|
||||
errno = 0;
|
||||
return ret;
|
||||
}
|
||||
ss << fs.rdbuf();
|
||||
fs.close();
|
||||
|
||||
*retStr = ss.str();
|
||||
|
||||
retStr->erase(std::remove(retStr->begin(), retStr->end(), '\n'),
|
||||
retStr->end());
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool IsInteger(const std::string & n_str) {
|
||||
if (n_str.empty() || ((!isdigit(n_str[0])) && (n_str[0] != '-')
|
||||
&& (n_str[0] != '+'))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
char * tmp;
|
||||
strtol(n_str.c_str(), &tmp, 10);
|
||||
|
||||
return (*tmp == 0);
|
||||
}
|
||||
|
||||
rsmi_status_t handleException() {
|
||||
try {
|
||||
throw;
|
||||
} catch (const std::bad_alloc& e) {
|
||||
debug_print("RSMI exception: BadAlloc\n");
|
||||
return RSMI_STATUS_OUT_OF_RESOURCES;
|
||||
} catch (const amd::smi::rsmi_exception& e) {
|
||||
debug_print("Exception caught: %s.\n", e.what());
|
||||
return e.error_code();
|
||||
} catch (const std::exception& e) {
|
||||
debug_print("Exception caught: %s\n", e.what());
|
||||
return RSMI_STATUS_INTERNAL_EXCEPTION;
|
||||
} catch (const std::nested_exception& e) {
|
||||
debug_print("Callback threw.\n");
|
||||
return RSMI_STATUS_INTERNAL_EXCEPTION;
|
||||
} catch (...) {
|
||||
debug_print("Unknown exception caught.\n");
|
||||
return RSMI_STATUS_INTERNAL_EXCEPTION;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_t *GetMutex(uint32_t dv_ind) {
|
||||
amd::smi::RocmSMI& smi = amd::smi::RocmSMI::getInstance();
|
||||
|
||||
if (dv_ind >= smi.devices().size()) {
|
||||
return nullptr;
|
||||
}
|
||||
std::shared_ptr<amd::smi::Device> dev = smi.devices()[dv_ind];
|
||||
assert(dev != nullptr);
|
||||
|
||||
return dev->mutex();
|
||||
}
|
||||
|
||||
rsmi_status_t GetDevValueVec(amd::smi::DevInfoTypes type,
|
||||
uint32_t dv_ind, std::vector<std::string> *val_vec) {
|
||||
assert(val_vec != nullptr);
|
||||
if (val_vec == nullptr) {
|
||||
return RSMI_STATUS_INVALID_ARGS;
|
||||
}
|
||||
GET_DEV_FROM_INDX
|
||||
|
||||
int ret = dev->readDevInfo(type, val_vec);
|
||||
return ErrnoToRsmiStatus(ret);
|
||||
}
|
||||
|
||||
rsmi_status_t
|
||||
GetDevBinaryBlob(amd::smi::DevInfoTypes type,
|
||||
uint32_t dv_ind, std::size_t b_size, void* p_binary_data) {
|
||||
assert(p_binary_data != nullptr);
|
||||
if (p_binary_data == nullptr) {
|
||||
return RSMI_STATUS_INVALID_ARGS;
|
||||
}
|
||||
GET_DEV_FROM_INDX
|
||||
|
||||
int ret = dev->readDevInfo(type, b_size, p_binary_data);
|
||||
return ErrnoToRsmiStatus(ret);
|
||||
}
|
||||
|
||||
rsmi_status_t ErrnoToRsmiStatus(int err) {
|
||||
switch (err) {
|
||||
case 0: return RSMI_STATUS_SUCCESS;
|
||||
case ESRCH: return RSMI_STATUS_NOT_FOUND;
|
||||
case EACCES: return RSMI_STATUS_PERMISSION;
|
||||
case EPERM:
|
||||
case ENOENT: return RSMI_STATUS_NOT_SUPPORTED;
|
||||
case EBADF:
|
||||
case EISDIR: return RSMI_STATUS_FILE_ERROR;
|
||||
case EINTR: return RSMI_STATUS_INTERRUPT;
|
||||
case EIO: return RSMI_STATUS_UNEXPECTED_SIZE;
|
||||
case ENXIO: return RSMI_STATUS_UNEXPECTED_DATA;
|
||||
case EBUSY: return RSMI_STATUS_BUSY;
|
||||
default: return RSMI_STATUS_UNKNOWN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace smi
|
||||
} // namespace amd
|
||||
Reference in New Issue
Block a user