| /* |
| * SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "NvidiaGpuViolationDuration.hpp" |
| |
| #include <bits/basic_string.h> |
| |
| #include <MctpRequester.hpp> |
| #include <NvidiaGpuLongRunningCommand.hpp> |
| #include <NvidiaGpuMctpVdm.hpp> |
| #include <NvidiaLongRunningHandler.hpp> |
| #include <NvidiaUtils.hpp> |
| #include <OcpMctpVdm.hpp> |
| #include <SerialQueue.hpp> |
| #include <Utils.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| |
| #include <cstdint> |
| #include <format> |
| #include <functional> |
| #include <memory> |
| #include <span> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| using namespace std::literals; |
| |
| namespace |
| { |
| |
| constexpr double nanosecondsPerSecond = 1'000'000'000.0; |
| |
| std::shared_ptr<sdbusplus::asio::dbus_interface> createMetricInterface( |
| sdbusplus::asio::object_server& objectServer, |
| const sdbusplus::object_path& metricObjectPath) |
| { |
| auto metricInterface = objectServer.add_interface( |
| metricObjectPath, "xyz.openbmc_project.Metric.Value"); |
| |
| metricInterface->register_property( |
| "Unit", "xyz.openbmc_project.Metric.Value.Unit.Seconds"s); |
| metricInterface->register_property("Value", 0.0); |
| |
| return metricInterface; |
| } |
| |
| std::shared_ptr<sdbusplus::asio::dbus_interface> createAssociationInterface( |
| sdbusplus::asio::object_server& objectServer, |
| const sdbusplus::object_path& metricObjectPath, |
| const sdbusplus::object_path& processorObjectPath) |
| { |
| std::vector<Association> associations; |
| associations.emplace_back("measuring", "measured_by", processorObjectPath); |
| |
| auto associationInterface = |
| objectServer.add_interface(metricObjectPath, association::interface); |
| |
| associationInterface->register_property("Associations", associations); |
| |
| return associationInterface; |
| } |
| |
| void initializeInterface( |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& iface, |
| const std::string& deviceName, uint8_t eid, const std::string& label) |
| { |
| if (!iface->initialize()) |
| { |
| lg2::error("Error initializing {LABEL} for GPU {NAME}, eid={EID}", |
| "LABEL", label, "NAME", deviceName, "EID", eid); |
| } |
| } |
| |
| } // namespace |
| |
| NvidiaGpuViolationDuration::NvidiaGpuViolationDuration( |
| mctp::MctpRequester& mctpRequester, |
| sdbusplus::asio::object_server& objectServer, const std::string& deviceName, |
| uint8_t eid, std::shared_ptr<SerialQueue> longRunningQueue, |
| std::shared_ptr<NvidiaLongRunningResponseHandler> |
| longRunningResponseHandler) |
| { |
| const sdbusplus::object_path gpuMetricPath = |
| sdbusplus::object_path(metricPath) / std::format("gpu_{}", deviceName); |
| const sdbusplus::object_path powerObjectPath = |
| gpuMetricPath / "power_limit_throttle_duration"; |
| const sdbusplus::object_path thermalObjectPath = |
| gpuMetricPath / "thermal_limit_throttle_duration"; |
| |
| const sdbusplus::object_path processorObjectPath = |
| sdbusplus::object_path(inventoryPath) / deviceName; |
| |
| powerMetricInterface = createMetricInterface(objectServer, powerObjectPath); |
| powerMetricAssociationInterface = createAssociationInterface( |
| objectServer, powerObjectPath, processorObjectPath); |
| |
| thermalMetricInterface = |
| createMetricInterface(objectServer, thermalObjectPath); |
| thermalMetricAssociationInterface = createAssociationInterface( |
| objectServer, thermalObjectPath, processorObjectPath); |
| |
| initializeInterface(powerMetricInterface, deviceName, eid, |
| "Power Limit Throttle Duration metric interface"); |
| initializeInterface( |
| powerMetricAssociationInterface, deviceName, eid, |
| "Power Limit Throttle Duration metric association interface"); |
| initializeInterface(thermalMetricInterface, deviceName, eid, |
| "Thermal Limit Throttle Duration metric interface"); |
| initializeInterface( |
| thermalMetricAssociationInterface, deviceName, eid, |
| "Thermal Limit Throttle Duration metric association interface"); |
| |
| cmd = std::make_shared<NvidiaGpuLongRunningCommand>( |
| eid, mctpRequester, std::move(longRunningQueue), |
| std::move(longRunningResponseHandler), |
| NvidiaGpuLongRunningCommand::Config{ |
| .metricName = "GPU Violation Duration", |
| .messageType = gpu::MessageType::PLATFORM_ENVIRONMENTAL, |
| .commandId = |
| gpu::PlatformEnvironmentalCommands::GET_VIOLATION_DURATION, |
| .requestSize = gpu::getViolationDurationRequestSize, |
| .encodeRequest = std::bind_front( |
| &gpu::encodeGetViolationDurationRequest, uint8_t{0}), |
| .onImmediateSuccess = std::bind_front( |
| &NvidiaGpuViolationDuration::onImmediateSuccess, |
| powerMetricInterface, thermalMetricInterface, eid), |
| .onLongRunningPayload = std::bind_front( |
| &NvidiaGpuViolationDuration::onLongRunningPayload, |
| powerMetricInterface, thermalMetricInterface, eid), |
| }); |
| } |
| |
| void NvidiaGpuViolationDuration::update() |
| { |
| cmd->update(); |
| } |
| |
| void NvidiaGpuViolationDuration::onImmediateSuccess( |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| powerMetricInterface, |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| thermalMetricInterface, |
| uint8_t eid, std::span<const uint8_t> buffer) |
| { |
| ocp::accelerator_management::CompletionCode cc{}; |
| uint16_t reasonCode = 0; |
| uint64_t hwViolationDuration = 0; |
| uint64_t globalSwViolationDuration = 0; |
| uint64_t powerViolationDuration = 0; |
| uint64_t thermalViolationDuration = 0; |
| |
| const int rc = gpu::decodeGetViolationDurationResponse( |
| buffer, cc, reasonCode, hwViolationDuration, globalSwViolationDuration, |
| powerViolationDuration, thermalViolationDuration); |
| |
| if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS) |
| { |
| lg2::error("Error updating GPU Violation Duration: decode failed, " |
| "rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}", |
| "RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, |
| "EID", eid); |
| return; |
| } |
| |
| applyDurations(powerMetricInterface, thermalMetricInterface, |
| powerViolationDuration, thermalViolationDuration); |
| } |
| |
| void NvidiaGpuViolationDuration::onLongRunningPayload( |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| powerMetricInterface, |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| thermalMetricInterface, |
| uint8_t eid, std::span<const uint8_t> payload) |
| { |
| uint64_t hwViolationDuration = 0; |
| uint64_t globalSwViolationDuration = 0; |
| uint64_t powerViolationDuration = 0; |
| uint64_t thermalViolationDuration = 0; |
| |
| const int rc = gpu::decodeGetViolationDurationResponse( |
| payload, hwViolationDuration, globalSwViolationDuration, |
| powerViolationDuration, thermalViolationDuration); |
| |
| if (rc != 0) |
| { |
| lg2::error("Error updating GPU Violation Duration: failed to decode " |
| "long running response data, rc={RC}, EID={EID}", |
| "RC", rc, "EID", eid); |
| return; |
| } |
| |
| applyDurations(powerMetricInterface, thermalMetricInterface, |
| powerViolationDuration, thermalViolationDuration); |
| } |
| |
| void NvidiaGpuViolationDuration::applyDurations( |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| powerMetricInterface, |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& |
| thermalMetricInterface, |
| uint64_t powerViolationNs, uint64_t thermalViolationNs) |
| { |
| powerMetricInterface->set_property( |
| "Value", static_cast<double>(powerViolationNs) / nanosecondsPerSecond); |
| thermalMetricInterface->set_property( |
| "Value", |
| static_cast<double>(thermalViolationNs) / nanosecondsPerSecond); |
| } |