From 78b826f81d03a32b6e5f4b5fd08fe47a76d2efe3 Mon Sep 17 00:00:00 2001 From: Jussi Kuokkanen Date: Tue, 24 Oct 2023 22:26:52 +0300 Subject: [PATCH] add AMD power nodes --- src/plugins/AMD.cpp | 89 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/plugins/AMD.cpp b/src/plugins/AMD.cpp index 2018423..1ab48ae 100644 --- a/src/plugins/AMD.cpp +++ b/src/plugins/AMD.cpp @@ -256,6 +256,82 @@ std::vector> getFanSpeedRead(AMDGPUData data) { return {}; } +std::vector> getPowerLimit(AMDGPUData data) { + // Get delta of min and max fan RPMs + char path[96]; + snprintf(path, 96, "%s/power1_cap_min", data.hwmonPath.c_str()); + auto contents = fileContents(path); + if (!contents.has_value()) + return {}; + + // uW -> W + double minLimit = static_cast(std::stoi(*contents)) / 1000000; + + snprintf(path, 96, "%s/power1_cap_max", data.hwmonPath.c_str()); + contents = fileContents(path); + if (!contents.has_value()) + return {}; + + double maxLimit = static_cast(std::stoi(*contents)) / 1000000; + Range range{minLimit, maxLimit}; + + snprintf(path, 96, "%s/power1_cap", data.hwmonPath.c_str()); + + auto getFunc = [=]() -> std::optional { + auto string = fileContents(path); + if (!string.has_value()) + return std::nullopt; + + int cur_uW = std::stoi(*string); + return static_cast(cur_uW) / 1000000; + }; + + auto setFunc = [=](AssignmentArgument a) -> std::optional { + if (!std::holds_alternative(a)) + return AssignmentError::InvalidType; + + auto value = std::get(a); + if (value < range.min || value > range.max) + return AssignmentError::OutOfRange; + + // W -> uW + auto target = std::round(value * 1000000); + if (std::ofstream{path} << target) + return std::nullopt; + return AssignmentError::UnknownError; + }; + + Assignable a{setFunc, range, getFunc, _("W")}; + + return {DeviceNode{ + .name = _("Power Limit"), + .interface = a, + .hash = md5(data.pciId + "Power Limit"), + }}; +} + +std::vector> getPowerUsage(AMDGPUData data) { + auto func = [=]() -> ReadResult { + uint power; + // TODO: is this microwatts too? + if (amdgpu_query_sensor_info(data.devHandle, AMDGPU_INFO_SENSOR_GPU_AVG_POWER, + sizeof(power), &power) == 0) + return static_cast(power) / 1000000; + return ReadError::UnknownError; + }; + + DynamicReadable dr{func, _("W")}; + + if (hasReadableValue(func())) { + return {DeviceNode{ + .name = _("Power Usage"), + .interface = dr, + .hash = md5(data.pciId + "Power Usage"), + }}; + } + return {}; +} + std::vector> getFanRoot(AMDGPUData data) { return {DeviceNode{ .name = _("Fans"), @@ -264,6 +340,15 @@ std::vector> getFanRoot(AMDGPUData data) { }}; } +std::vector> getPowerRoot(AMDGPUData data) { + // Root for power usage and power limit + return {DeviceNode{ + .name = _("Power"), + .interface = std::nullopt, + .hash = md5(data.pciId + "Power"), + }}; +} + std::vector> getGPUName(AMDGPUData data) { auto name = amdgpu_get_marketing_name(data.devHandle); if (name) { @@ -284,6 +369,10 @@ auto gpuTree = TreeConstructor{ {getFanMode, {}}, {getFanSpeedWrite, {}}, {getFanSpeedRead, {}} + }}, + {getPowerRoot, { + {getPowerLimit, {}}, + {getPowerUsage, {}} }} } };