blob: 410576ea4f42a5356c94c263a6a2d7d6874bcecb [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaGpuUtilizationMetrics.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;
NvidiaGpuUtilizationMetrics::NvidiaGpuUtilizationMetrics(
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 metricRoot =
sdbusplus::object_path(metricPath) / std::format("gpu_{}", deviceName);
const sdbusplus::object_path processorMetricObjectPath =
metricRoot / "processor_bandwidth";
processorMetricInterface = objectServer.add_interface(
processorMetricObjectPath, "xyz.openbmc_project.Metric.Value");
processorMetricInterface->register_property(
"Unit", "xyz.openbmc_project.Metric.Value.Unit.Percent"s);
processorMetricInterface->register_property("Value", 0.0);
const sdbusplus::object_path processorInventoryPath =
sdbusplus::object_path(inventoryPath) / deviceName;
std::vector<Association> processorAssociations;
processorAssociations.emplace_back("measuring", "measured_by",
processorInventoryPath);
processorMetricAssociationInterface = objectServer.add_interface(
processorMetricObjectPath, association::interface);
processorMetricAssociationInterface->register_property(
"Associations", processorAssociations);
if (!processorMetricInterface->initialize())
{
lg2::error(
"Error initializing Processor Bandwidth metric interface for GPU {NAME}, eid={EID}",
"NAME", deviceName, "EID", eid);
}
if (!processorMetricAssociationInterface->initialize())
{
lg2::error(
"Error initializing Processor Bandwidth metric association interface for GPU {NAME}, eid={EID}",
"NAME", deviceName, "EID", eid);
}
const sdbusplus::object_path memoryMetricObjectPath =
metricRoot / "memory_bandwidth";
memoryMetricInterface = objectServer.add_interface(
memoryMetricObjectPath, "xyz.openbmc_project.Metric.Value");
memoryMetricInterface->register_property(
"Unit", "xyz.openbmc_project.Metric.Value.Unit.Percent"s);
memoryMetricInterface->register_property("Value", 0.0);
const sdbusplus::object_path memoryInventoryPath(std::format(
"{}_{}", processorInventoryPath.string(), dramInventorySuffix));
std::vector<Association> memoryAssociations;
memoryAssociations.emplace_back("measuring", "measured_by",
memoryInventoryPath);
memoryMetricAssociationInterface = objectServer.add_interface(
memoryMetricObjectPath, association::interface);
memoryMetricAssociationInterface->register_property("Associations",
memoryAssociations);
if (!memoryMetricInterface->initialize())
{
lg2::error(
"Error initializing Memory Bandwidth metric interface for GPU {NAME}, eid={EID}",
"NAME", deviceName, "EID", eid);
}
if (!memoryMetricAssociationInterface->initialize())
{
lg2::error(
"Error initializing Memory Bandwidth metric association interface for GPU {NAME}, eid={EID}",
"NAME", deviceName, "EID", eid);
}
cmd = std::make_shared<NvidiaGpuLongRunningCommand>(
eid, mctpRequester, std::move(longRunningQueue),
std::move(longRunningResponseHandler),
NvidiaGpuLongRunningCommand::Config{
.metricName = "GPU Utilization Metrics",
.messageType = gpu::MessageType::PLATFORM_ENVIRONMENTAL,
.commandId =
gpu::PlatformEnvironmentalCommands::GET_CURRENT_UTILIZATION,
.requestSize = gpu::getCurrentUtilizationRequestSize,
.encodeRequest = std::bind_front(
&gpu::encodeGetCurrentUtilizationModeRequest, uint8_t{0}),
.onImmediateSuccess = std::bind_front(
&NvidiaGpuUtilizationMetrics::onImmediateSuccess,
processorMetricInterface, memoryMetricInterface, eid),
.onLongRunningPayload = std::bind_front(
&NvidiaGpuUtilizationMetrics::onLongRunningPayload,
processorMetricInterface, memoryMetricInterface, eid),
});
}
void NvidiaGpuUtilizationMetrics::update()
{
cmd->update();
}
void NvidiaGpuUtilizationMetrics::onImmediateSuccess(
const std::shared_ptr<sdbusplus::asio::dbus_interface>&
processorMetricInterface,
const std::shared_ptr<sdbusplus::asio::dbus_interface>&
memoryMetricInterface,
uint8_t eid, std::span<const uint8_t> buffer)
{
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
uint32_t utilization = 0;
uint32_t memoryUtilization = 0;
const int rc = gpu::decodeGetCurrentUtilizationModeResponse(
buffer, cc, reasonCode, utilization, memoryUtilization);
if (rc != 0)
{
lg2::error("Error updating GPU Utilization Metrics: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode,
"EID", eid);
return;
}
applyUtilization(processorMetricInterface, utilization);
applyUtilization(memoryMetricInterface, memoryUtilization);
}
void NvidiaGpuUtilizationMetrics::onLongRunningPayload(
const std::shared_ptr<sdbusplus::asio::dbus_interface>&
processorMetricInterface,
const std::shared_ptr<sdbusplus::asio::dbus_interface>&
memoryMetricInterface,
uint8_t eid, std::span<const uint8_t> payload)
{
uint32_t utilization = 0;
uint32_t memoryUtilization = 0;
const int rc = gpu::decodeGetCurrentUtilizationModeResponse(
payload, utilization, memoryUtilization);
if (rc != 0)
{
lg2::error("Error updating GPU Utilization Metrics: failed to decode "
"long running response data, rc={RC}, EID={EID}",
"RC", rc, "EID", eid);
return;
}
applyUtilization(processorMetricInterface, utilization);
applyUtilization(memoryMetricInterface, memoryUtilization);
}
void NvidiaGpuUtilizationMetrics::applyUtilization(
const std::shared_ptr<sdbusplus::asio::dbus_interface>& metricInterface,
uint32_t utilization)
{
metricInterface->set_property("Value", static_cast<double>(utilization));
}