Files
tuxclocker/src/plugins/AMD.cpp

179 lines
4.3 KiB
C++
Raw Normal View History

2023-10-12 12:56:11 +03:00
#include <Crypto.hpp>
#include <patterns.hpp>
2020-03-18 01:39:44 +02:00
#include <Plugin.hpp>
2023-10-12 12:56:11 +03:00
#include <TreeConstructor.hpp>
#include <Utils.hpp>
2020-03-14 12:58:21 +02:00
2020-03-20 00:42:16 +02:00
#include <errno.h>
#include <fcntl.h>
#include <filesystem>
#include <functional>
2020-03-18 01:39:44 +02:00
#include <iostream>
2020-03-20 00:42:16 +02:00
#include <libdrm/amdgpu.h>
#include <libdrm/amdgpu_drm.h>
2023-10-12 12:56:11 +03:00
#include <libintl.h>
2020-03-20 00:42:16 +02:00
#include <unistd.h>
#include <xf86drm.h>
2023-10-12 12:56:11 +03:00
#define _(String) gettext(String)
2020-03-20 00:42:16 +02:00
extern int errno;
#ifndef DRM_RENDER_MINOR_NAME
#define DRM_RENDER_MINOR_NAME "renderD"
#endif
#define _HWMON_NAME "hwmon"
#define _AMDGPU_NAME "amdgpu"
#define DEVICE_FILE_PREFIX DRM_DIR_NAME "/" DRM_RENDER_MINOR_NAME
#define RENDERD_OFFSET 128
2020-03-18 01:39:44 +02:00
using namespace mpark::patterns;
2020-03-18 01:39:44 +02:00
using namespace TuxClocker::Plugin;
2023-10-12 12:56:11 +03:00
using namespace TuxClocker::Crypto;
2020-03-18 01:39:44 +02:00
using namespace TuxClocker::Device;
using namespace TuxClocker;
2020-03-20 00:42:16 +02:00
namespace fs = std::filesystem;
2023-10-12 12:56:11 +03:00
struct AMDGPUData {
// Full path, eg. /sys/class/drm/renderD128/device/hwmon
std::string hwmonPath;
2020-03-20 00:42:16 +02:00
amdgpu_device_handle devHandle;
2023-10-12 12:56:11 +03:00
// Used as identifier
std::string pciId;
2020-03-20 00:42:16 +02:00
};
2023-10-12 12:56:11 +03:00
std::optional<AMDGPUData> fromRenderDFile(const fs::directory_entry &entry) {
auto fd = open(entry.path().c_str(), O_RDONLY);
auto v_ptr = drmGetVersion(fd);
amdgpu_device_handle dev;
uint32_t m, n;
int devInitRetval = amdgpu_device_initialize(fd, &m, &n, &dev);
if (fd > 0 && v_ptr && devInitRetval == 0 &&
std::string(v_ptr->name).find(_AMDGPU_NAME) != std::string::npos) {
// Device uses amdgpu
// Find hwmon path
std::ostringstream stream;
// Eg. renderD128
auto filename = entry.path().filename().string();
stream << "/sys/class/drm/" << filename << "/device/hwmon";
std::optional<std::string> hwmonPath = std::nullopt;
try {
for (const auto &entry : fs::directory_iterator(stream.str())) {
if (entry.path().filename().string().find("hwmon") !=
std::string::npos) {
hwmonPath = entry.path().string();
break;
}
}
} catch (fs::filesystem_error &e) {
goto fail;
}
if (!hwmonPath.has_value())
goto fail;
2020-03-18 01:39:44 +02:00
2023-10-12 12:56:11 +03:00
// Get PCI id
drm_amdgpu_info_device info;
if (amdgpu_query_info(dev, AMDGPU_INFO_DEV_INFO, sizeof(info), &info) != 0)
goto fail;
drmFreeVersion(v_ptr);
return AMDGPUData{
.hwmonPath = hwmonPath.value(),
.devHandle = dev,
.pciId = std::to_string(info.device_id),
};
2020-03-20 00:42:16 +02:00
}
2023-10-12 12:56:11 +03:00
fail:
close(fd);
drmFreeVersion(v_ptr);
return std::nullopt;
2020-03-20 00:42:16 +02:00
}
2023-10-12 12:56:11 +03:00
std::vector<AMDGPUData> fromFilesystem() {
std::vector<AMDGPUData> retval;
// Iterate through files in GPU device folder and find which ones have amdgpu loaded
for (const auto &entry : fs::directory_iterator(DRM_DIR_NAME)) {
// Check if path contains 'renderD' so we don't create root nodes for 'cardX' too
if (entry.path().string().find(DRM_RENDER_MINOR_NAME) != std::string::npos) {
auto data = fromRenderDFile(entry);
if (data.has_value())
retval.push_back(data.value());
}
2023-10-12 12:56:11 +03:00
}
return retval;
}
2023-10-12 12:56:11 +03:00
std::vector<TreeNode<DeviceNode>> getTemperature(AMDGPUData data) {
auto func = [=]() -> ReadResult {
uint temp;
// Always uses uintptr_t to write return data
if (amdgpu_query_sensor_info(
data.devHandle, AMDGPU_INFO_SENSOR_GPU_TEMP, sizeof(temp), &temp) == 0)
return temp / 1000;
return ReadError::UnknownError;
2020-03-20 00:42:16 +02:00
};
2023-07-26 16:33:11 +03:00
2023-10-12 12:56:11 +03:00
DynamicReadable dr{func, _("°C")};
if (hasReadableValue(func())) {
return {DeviceNode{
.name = _("Temperature"),
.interface = dr,
.hash = md5(data.pciId + "Temperature"),
}};
2020-03-20 00:42:16 +02:00
}
2023-10-12 12:56:11 +03:00
return {};
}
2023-07-26 16:33:11 +03:00
2023-10-12 12:56:11 +03:00
std::vector<TreeNode<DeviceNode>> getGPUName(AMDGPUData data) {
auto name = amdgpu_get_marketing_name(data.devHandle);
if (name) {
return {DeviceNode{
.name = name,
.interface = std::nullopt,
.hash = md5(data.pciId),
}};
2020-03-20 00:42:16 +02:00
}
2023-10-12 12:56:11 +03:00
return {};
}
2023-07-26 16:33:11 +03:00
2023-10-12 12:56:11 +03:00
// clang-format off
auto gpuTree = TreeConstructor<AMDGPUData, DeviceNode>{
getGPUName, {
{getTemperature, {}}
}
};
// clang-format on
2023-07-26 16:33:11 +03:00
2023-10-12 12:56:11 +03:00
class AMDPlugin : public DevicePlugin {
public:
std::optional<InitializationError> initializationError() { return std::nullopt; }
TreeNode<DeviceNode> deviceRootNode();
~AMDPlugin();
private:
std::vector<AMDGPUData> m_gpuDataVec;
};
2023-07-26 16:33:11 +03:00
2023-10-12 12:56:11 +03:00
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() {
TreeNode<DeviceNode> root;
auto dataVec = fromFilesystem();
m_gpuDataVec = dataVec;
for (auto &data : dataVec)
constructTree(gpuTree, root, data);
return root;
2020-03-18 01:39:44 +02:00
}
2023-10-12 12:56:11 +03:00
AMDPlugin::~AMDPlugin() {
for (auto info : m_gpuDataVec) {
amdgpu_device_deinitialize(info.devHandle);
}
}
2020-03-18 01:39:44 +02:00
TUXCLOCKER_PLUGIN_EXPORT(AMDPlugin)