rework to use TreeConstructor

This commit is contained in:
Jussi Kuokkanen
2023-10-12 12:56:11 +03:00
parent 196045c815
commit 7ce038749a
2 changed files with 130 additions and 117 deletions

View File

@@ -1,6 +1,8 @@
#include <Functional.hpp>
#include <Crypto.hpp>
#include <patterns.hpp>
#include <Plugin.hpp>
#include <TreeConstructor.hpp>
#include <Utils.hpp>
#include <errno.h>
#include <fcntl.h>
@@ -9,9 +11,12 @@
#include <iostream>
#include <libdrm/amdgpu.h>
#include <libdrm/amdgpu_drm.h>
#include <libintl.h>
#include <unistd.h>
#include <xf86drm.h>
#define _(String) gettext(String)
extern int errno;
#ifndef DRM_RENDER_MINOR_NAME
@@ -25,139 +30,146 @@ extern int errno;
using namespace mpark::patterns;
using namespace TuxClocker::Plugin;
using namespace TuxClocker::Crypto;
using namespace TuxClocker::Device;
using namespace TuxClocker;
namespace fs = std::filesystem;
struct AMDGPUInfo {
// Thus is Linux only
std::optional<std::string> hwmonPath;
struct AMDGPUData {
// Full path, eg. /sys/class/drm/renderD128/device/hwmon
std::string hwmonPath;
amdgpu_device_handle devHandle;
// Used as identifier
std::string pciId;
};
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;
// 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),
};
}
fail:
close(fd);
drmFreeVersion(v_ptr);
return std::nullopt;
}
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)) {
auto data = fromRenderDFile(entry);
if (data.has_value())
retval.push_back(data.value());
}
return retval;
}
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;
};
DynamicReadable dr{func, _("°C")};
if (hasReadableValue(func())) {
return {DeviceNode{
.name = _("Temperature"),
.interface = dr,
.hash = md5(data.pciId + "Temperature"),
}};
}
return {};
}
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),
}};
}
return {};
}
// clang-format off
auto gpuTree = TreeConstructor<AMDGPUData, DeviceNode>{
getGPUName, {
{getTemperature, {}}
}
};
// clang-format on
class AMDPlugin : public DevicePlugin {
public:
AMDPlugin();
std::optional<InitializationError> initializationError() { return std::nullopt; }
void foo() { std::cout << "hi from AMD plugin\n"; }
TreeNode<DeviceNode> deviceRootNode();
~AMDPlugin();
private:
TreeNode<DeviceNode> m_rootNode;
std::vector<AMDGPUInfo> m_GPUInfoVec;
template <typename T>
static std::variant<ReadError, ReadableValue> libdrmRead(amdgpu_device_handle dev,
uint query, std::optional<std::function<T(T)>> transformFunc = std::nullopt);
std::vector<AMDGPUData> m_gpuDataVec;
};
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() {
TreeNode<DeviceNode> root;
auto dataVec = fromFilesystem();
m_gpuDataVec = dataVec;
for (auto &data : dataVec)
constructTree(gpuTree, root, data);
return root;
}
AMDPlugin::~AMDPlugin() {
for (auto info : m_GPUInfoVec) {
for (auto info : m_gpuDataVec) {
amdgpu_device_deinitialize(info.devHandle);
}
}
template <typename T>
std::variant<ReadError, ReadableValue> AMDPlugin::libdrmRead(
amdgpu_device_handle dev, uint query, std::optional<std::function<T(T)>> transformFunc) {
T value;
auto retval = amdgpu_query_sensor_info(dev, query, sizeof(T), &value);
if (retval != 0)
return ReadError::UnknownError;
if (!transformFunc.has_value())
return value;
return transformFunc.value()(value);
}
AMDPlugin::AMDPlugin() {
struct FSInfo {
std::string path;
std::string filename;
};
std::vector<FSInfo> infoVec;
// 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 entry is renderD file
if (entry.path().string().find(DRM_RENDER_MINOR_NAME) != std::string::npos) {
infoVec.push_back(
FSInfo{entry.path().string(), entry.path().filename().string()});
}
}
for (const auto &info : infoVec) {
auto fd = open(info.path.c_str(), O_RDONLY);
auto v_ptr = drmGetVersion(fd);
amdgpu_device_handle dev;
uint32_t m, n;
int devInitRetval;
if (fd > 0 && v_ptr &&
std::string(v_ptr->name).find(_AMDGPU_NAME) != std::string::npos &&
(devInitRetval = amdgpu_device_initialize(fd, &m, &n, &dev)) == 0) {
// Device uses amdgpu
// Find hwmon path if available
std::ostringstream stream;
stream << "/sys/class/drm/" << info.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;
}
}
}
// This exception is only abnormal to get on Linux
catch (fs::filesystem_error &e) {
}
m_GPUInfoVec.push_back(AMDGPUInfo{hwmonPath, dev});
continue;
}
// if (devInitRetval == 0) amdgpu_device_deinitialize(dev);
close(fd);
drmFreeVersion(v_ptr);
}
// Add nodes with successful queries to device node
// Data structure for mapping to DynamicReadables
struct UnspecializedReadable {
std::function<std::variant<ReadError, ReadableValue>(AMDGPUInfo)> func;
std::optional<std::string> unit;
std::string nodeName;
};
// List of query functions for DynamicReadables
std::vector<UnspecializedReadable> rawNodes = {
{[&](AMDGPUInfo info) {
return libdrmRead<uint>(info.devHandle, AMDGPU_INFO_SENSOR_GPU_TEMP,
std::function<uint(uint)>([](uint val) { return val / 1000; }));
},
std::nullopt, "Temperature"}};
for (auto info : m_GPUInfoVec) {
auto availableReadables = filter(rawNodes,
std::function<bool(UnspecializedReadable)>([info](UnspecializedReadable ur) {
auto result = ur.func(info);
// Check if this node returns an error or not
match(result)(
pattern(as<ReadError>(_)) = [] { return false; },
pattern(_) = [] { return true; });
return true;
}));
auto specializedNodes = map(availableReadables,
std::function<DynamicReadable(UnspecializedReadable)>(
[info](UnspecializedReadable ur) {
// auto specializedFunc = specializeFunction<std::variant<ReadError,
// ReadableValue>, AMDGPUInfo>(info, ur.func);
return DynamicReadable([info, ur]() { return ur.func(info); });
}));
}
}
TreeNode<DeviceNode> AMDPlugin::deviceRootNode() { return m_rootNode; }
TUXCLOCKER_PLUGIN_EXPORT(AMDPlugin)