blob: 0450eefe531e51ab671abeb7ebb23f409d2685e1 [file]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaPciePortMetrics.hpp"
#include "NvidiaUtils.hpp"
#include "Utils.hpp"
#include <bits/basic_string.h>
#include <MctpRequester.hpp>
#include <NvidiaGpuMctpVdm.hpp>
#include <OcpMctpVdm.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <cstddef>
#include <cstdint>
#include <format>
#include <functional>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <vector>
using std::string;
using namespace std::literals;
constexpr const char* metricInterface = "xyz.openbmc_project.Metric.Value";
NvidiaPciePortMetrics::NvidiaPciePortMetrics(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
sdbusplus::asio::object_server& objectServer,
gpu::PcieScalarGroupId scalarGroupId,
const std::vector<NvidiaMetricInfo>& metricsInfo,
gpu::DeviceIdentification deviceType) :
eid(eid), portType(portType), upstreamPortNumber(upstreamPortNumber),
portNumber(portNumber), scalarGroupId(scalarGroupId), path(path),
conn(conn), mctpRequester(mctpRequester), objectServer(objectServer),
deviceType(deviceType)
{
int rc = 0;
switch (deviceType)
{
case gpu::DeviceIdentification::DEVICE_GPU:
request.resize(gpu::queryScalarGroupTelemetryV1RequestSize);
rc = gpu::encodeQueryScalarGroupTelemetryV1Request(
0, 0, scalarGroupId, request);
break;
case gpu::DeviceIdentification::DEVICE_PCIE:
request.resize(gpu::queryScalarGroupTelemetryV2RequestSize);
rc = gpu::encodeQueryScalarGroupTelemetryV2Request(
0, portType, this->upstreamPortNumber, portNumber,
scalarGroupId, request);
break;
default:
break;
}
if (rc != 0)
{
request.clear();
lg2::error(
"Failed to encode PCIe Port Metrics request: rc={RC}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}, ScalarGroup={SG}",
"RC", rc, "EID", eid, "PT", static_cast<uint8_t>(portType), "UP",
upstreamPortNumber, "PN", portNumber, "SG",
static_cast<uint8_t>(scalarGroupId));
}
const std::string metricsDbusPathPrefix =
metricPath + std::format("port_{}_{}", pcieDeviceName, name);
const sdbusplus::object_path portDbusPath =
inventoryPrefix / pcieDeviceName / name;
for (const auto& [id, name] : metricsInfo)
{
const std::string metricsDbusPath = metricsDbusPathPrefix + name;
metricValueInterfaces[id] =
objectServer.add_interface(metricsDbusPath, metricInterface);
metricValueInterfaces[id]->register_property(
"Unit", "xyz.openbmc_project.Metric.Value.Unit.Count"s);
metricValueInterfaces[id]->register_property("Value", 0.0);
std::vector<Association> associations;
associations.emplace_back("measuring", "measured_by", portDbusPath);
metricAssociationInterfaces[id] =
objectServer.add_interface(metricsDbusPath, association::interface);
metricAssociationInterfaces[id]->register_property("Associations",
associations);
if (!metricValueInterfaces[id]->initialize())
{
lg2::error(
"Error initializing PCIe Port Metric interface, eid={EID}, "
"portType={PT}, portNumber={PN}, scalarGroup={SG}, metric={MN}",
"EID", eid, "PT", static_cast<uint8_t>(portType), "PN",
portNumber, "SG", static_cast<uint8_t>(scalarGroupId), "MN",
name);
}
if (!metricAssociationInterfaces[id]->initialize())
{
lg2::error(
"Error initializing PCIe Port Metric Association interface, eid={EID}, "
"portType={PT}, portNumber={PN}, scalarGroup={SG}, metric={MN}",
"EID", eid, "PT", static_cast<uint8_t>(portType), "PN",
portNumber, "SG", static_cast<uint8_t>(scalarGroupId), "MN",
name);
}
}
}
NvidiaPciePortMetrics::~NvidiaPciePortMetrics()
{
for (const auto& iface : metricValueInterfaces)
{
objectServer.remove_interface(iface);
}
for (const auto& iface : metricAssociationInterfaces)
{
objectServer.remove_interface(iface);
}
}
void NvidiaPciePortMetrics::processResponse(
const std::error_code& sendRecvMsgResult, std::span<const uint8_t> response)
{
if (sendRecvMsgResult)
{
lg2::error(
"Error updating PCIe Port Metrics: sending message over MCTP failed, "
"rc={RC}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}, ScalarGroup={SG}",
"RC", sendRecvMsgResult.message(), "EID", eid, "PT",
static_cast<uint8_t>(portType), "UP", upstreamPortNumber, "PN",
portNumber, "SG", static_cast<uint8_t>(scalarGroupId));
return;
}
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
size_t numTelemetryValue = 0;
int rc = 0;
switch (deviceType)
{
case gpu::DeviceIdentification::DEVICE_GPU:
rc = gpu::decodeQueryScalarGroupTelemetryV1Response(
response, cc, reasonCode, numTelemetryValue, telemetryValues);
break;
case gpu::DeviceIdentification::DEVICE_PCIE:
rc = gpu::decodeQueryScalarGroupTelemetryV2Response(
response, cc, reasonCode, numTelemetryValue, telemetryValues);
break;
default:
return;
}
if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
{
lg2::error(
"Error updating PCIe Port Errors: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}, ScalarGroup={SG}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, "EID",
eid, "PT", static_cast<uint8_t>(portType), "UP", upstreamPortNumber,
"PN", portNumber, "SG", static_cast<uint8_t>(scalarGroupId));
return;
}
for (size_t i = 0; i < numTelemetryValue; ++i)
{
if (metricValueInterfaces[i] != nullptr)
{
metricValueInterfaces[i]->set_property(
"Value", static_cast<double>(telemetryValues[i]));
}
}
}
void NvidiaPciePortMetrics::update()
{
if (request.empty())
{
return;
}
mctpRequester.sendRecvMsg(
eid, request,
[weak{weak_from_this()}](const std::error_code& ec,
std::span<const uint8_t> buffer) {
std::shared_ptr<NvidiaPciePortMetrics> self = weak.lock();
if (!self)
{
lg2::error("Invalid reference to NvidiaPciePortMetrics");
return;
}
self->processResponse(ec, buffer);
});
}
std::shared_ptr<NvidiaPciePortMetrics> makeNvidiaPciePortErrors(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
sdbusplus::asio::object_server& objectServer,
gpu::DeviceIdentification deviceType)
{
return std::make_shared<NvidiaPciePortMetrics>(
conn, mctpRequester, name, pcieDeviceName, path, eid, portType,
upstreamPortNumber, portNumber, objectServer,
gpu::PcieScalarGroupId::AerErrorCounters,
std::vector<NvidiaMetricInfo>{
{0, "/pcie/non_fatal_error_count"},
{1, "/pcie/fatal_error_count"},
{2, "/pcie/unsupported_request_count"},
{3, "/pcie/correctable_error_count"},
},
deviceType);
}
std::shared_ptr<NvidiaPciePortMetrics> makeNvidiaPciePortCounters(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
sdbusplus::asio::object_server& objectServer,
gpu::DeviceIdentification deviceType)
{
return std::make_shared<NvidiaPciePortMetrics>(
conn, mctpRequester, name, pcieDeviceName, path, eid, portType,
upstreamPortNumber, portNumber, objectServer,
gpu::PcieScalarGroupId::DetailedErrorCounters,
std::vector<NvidiaMetricInfo>{
{1, "/pcie/nak_received_count"},
{2, "/pcie/nak_sent_count"},
{4, "/pcie/replay_rollover_count"},
{6, "/pcie/replay_count"},
},
deviceType);
}
std::shared_ptr<NvidiaPciePortMetrics> makeNvidiaPciePortL0ToRecoveryCount(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
sdbusplus::asio::object_server& objectServer,
gpu::DeviceIdentification deviceType)
{
return std::make_shared<NvidiaPciePortMetrics>(
conn, mctpRequester, name, pcieDeviceName, path, eid, portType,
upstreamPortNumber, portNumber, objectServer,
gpu::PcieScalarGroupId::RecoveryCounter,
std::vector<NvidiaMetricInfo>{
{0, "/pcie/l0_to_recovery_count"},
},
deviceType);
}