blob: 158cb8c8088d188d9f96a99a176e6882d2cff0ff [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaEthPort.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 <array>
#include <cstdint>
#include <cstring>
#include <format>
#include <functional>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <tuple>
#include <utility>
#include <vector>
using std::string;
using namespace std::literals;
static constexpr uint8_t permanentMacAddressTag = 2;
NvidiaEthPortMetrics::NvidiaEthPortMetrics(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& deviceName, const std::string& path, uint8_t eid,
uint16_t portNumber, sdbusplus::asio::object_server& objectServer,
const std::vector<std::pair<uint8_t, uint64_t>>& addresses) :
eid(eid), portNumber(portNumber), path(path), conn(conn),
mctpRequester(mctpRequester), objectServer(objectServer)
{
const int rc = gpu::encodeGetEthernetPortTelemetryCountersRequest(
0, portNumber, request);
if (rc == 0)
{
requestEncoded = true;
}
else
{
lg2::error(
"Failed to encode Ethernet Port Metrics request for EID={EID}, PortNumber={PN}, rc={RC}",
"EID", eid, "PN", portNumber, "RC", rc);
}
const sdbusplus::object_path deviceDbusPath = inventoryPrefix / deviceName;
const sdbusplus::object_path portDbusPath =
inventoryPrefix / deviceName / name;
const std::string metricsDbusPathPrefix =
metricPath + std::format("port_{}_{}", deviceName, name);
portInterface = objectServer.add_interface(
portDbusPath, "xyz.openbmc_project.Inventory.Connector.Port");
std::vector<Association> associations;
associations.emplace_back("connected_to", "connecting", deviceDbusPath);
associationInterface =
objectServer.add_interface(portDbusPath, association::interface);
associationInterface->register_property("Associations", associations);
portInterface->register_property(
"PortProtocol",
std::string("xyz.openbmc_project.Inventory.Connector.Port."
"PortProtocol.Ethernet"));
for (const auto& [tag, value] : addresses)
{
if (tag != permanentMacAddressTag)
{
continue;
}
std::array<uint8_t, 6> mac{};
std::memcpy(mac.data(), &value, sizeof(mac));
const std::string macStr =
std::format("{:02X}:{:02X}:{:02X}:{:02X}:{:02X}:{:02X}", mac[0],
mac[1], mac[2], mac[3], mac[4], mac[5]);
const sdbusplus::object_path ndfDbusPath =
inventoryPrefix / deviceName / "NetworkDeviceFunctions" / name;
networkDeviceFunctionInterface = objectServer.add_interface(
ndfDbusPath, "xyz.openbmc_project.Inventory.Item.NetworkInterface");
networkDeviceFunctionInterface->register_property("MACAddress", macStr);
if (!networkDeviceFunctionInterface->initialize())
{
lg2::error(
"Error initializing NetworkDeviceFunction interface, eid={EID}, portNumber={PN}",
"EID", eid, "PN", portNumber);
}
std::vector<Association> ndfAssociations;
ndfAssociations.emplace_back("exposed_by", "exposing", deviceDbusPath);
ndfAssociations.emplace_back("assigned_to", "assigning", portDbusPath);
networkDeviceFunctionAssociationInterface =
objectServer.add_interface(ndfDbusPath, association::interface);
networkDeviceFunctionAssociationInterface->register_property(
"Associations", ndfAssociations);
if (!networkDeviceFunctionAssociationInterface->initialize())
{
lg2::error(
"Error initializing NetworkDeviceFunction Association interface, eid={EID}, portNumber={PN}",
"EID", eid, "PN", portNumber);
}
break;
}
static constexpr auto telemetryMetrics =
std::to_array<std::tuple<uint8_t, const char*, const std::string*>>({
{0, "/nic/rx_bytes", &metricUnitBytes},
{1, "/nic/tx_bytes", &metricUnitBytes},
{2, "/nic/rx_unicast_frames", &metricUnitCount},
{3, "/nic/rx_multicast_frames", &metricUnitCount},
{4, "/nic/rx_broadcast_frames", &metricUnitCount},
{5, "/nic/tx_unicast_frames", &metricUnitCount},
{6, "/nic/tx_multicast_frames", &metricUnitCount},
{7, "/nic/tx_broadcast_frames", &metricUnitCount},
{8, "/nic/rx_fcs_errors", &metricUnitCount},
{9, "/nic/rx_frame_alignment_errors", &metricUnitCount},
{10, "/nic/rx_false_carrier_errors", &metricUnitCount},
{11, "/nic/rx_undersize_frames", &metricUnitCount},
{12, "/nic/rx_oversize_frames", &metricUnitCount},
{13, "/nic/rx_pause_xon_frames", &metricUnitCount},
{14, "/nic/rx_pause_xoff_frames", &metricUnitCount},
{15, "/nic/tx_pause_xon_frames", &metricUnitCount},
{16, "/nic/tx_pause_xoff_frames", &metricUnitCount},
{17, "/nic/tx_single_collisions", &metricUnitCount},
{18, "/nic/tx_multiple_collisions", &metricUnitCount},
{19, "/nic/tx_late_collisions", &metricUnitCount},
{20, "/nic/tx_excessive_collisions", &metricUnitCount},
});
for (const auto& [tag, metricName, unit] : telemetryMetrics)
{
metricValueInterface[tag] =
objectServer.add_interface(metricsDbusPathPrefix + metricName,
"xyz.openbmc_project.Metric.Value");
metricValueInterface[tag]->register_property("Unit", *unit);
metricValueInterface[tag]->register_property("Value", 0.0);
std::vector<Association> associations;
associations.emplace_back("measuring", "measured_by", portDbusPath);
metricAssociationInterfaces[tag] = objectServer.add_interface(
metricsDbusPathPrefix + metricName, association::interface);
metricAssociationInterfaces[tag]->register_property("Associations",
associations);
if (!metricValueInterface[tag]->initialize())
{
lg2::error(
"Error initializing Ethernet Port Metric interface, eid={EID}, portNumber={PN}, metric={MN}",
"EID", eid, "PN", portNumber, "MN", metricName);
}
if (!metricAssociationInterfaces[tag]->initialize())
{
lg2::error(
"Error initializing Ethernet Port Metric Association interface, eid={EID}, portNumber={PN}, metric={MN}",
"EID", eid, "PN", portNumber, "MN", metricName);
}
}
if (!portInterface->initialize())
{
lg2::error(
"Error initializing Ethernet Port interface, eid={EID}, portNumber={PN}",
"EID", eid, "PN", portNumber);
}
if (!associationInterface->initialize())
{
lg2::error(
"Error initializing Association interface for Ethernet Port, eid={EID}, portNumber={PN}",
"EID", eid, "PN", portNumber);
}
}
NvidiaEthPortMetrics::~NvidiaEthPortMetrics()
{
objectServer.remove_interface(portInterface);
objectServer.remove_interface(associationInterface);
objectServer.remove_interface(networkDeviceFunctionInterface);
objectServer.remove_interface(networkDeviceFunctionAssociationInterface);
for (const auto& iface : metricValueInterface)
{
objectServer.remove_interface(iface);
}
for (const auto& iface : metricAssociationInterfaces)
{
objectServer.remove_interface(iface);
}
}
void NvidiaEthPortMetrics::processResponse(
const std::error_code& sendRecvMsgResult, std::span<const uint8_t> response)
{
if (sendRecvMsgResult)
{
lg2::error(
"Error updating Ethernet Port Metrics: sending message over MCTP failed, "
"rc={RC}, EID={EID}, PortNumber={PN}",
"RC", sendRecvMsgResult.message(), "EID", eid, "PN", portNumber);
return;
}
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
std::vector<std::pair<uint8_t, uint64_t>> telemetryValues;
const int rc = gpu::decodeGetEthernetPortTelemetryCountersResponse(
response, cc, reasonCode, telemetryValues);
if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
{
lg2::error(
"Error updating Ethernet Port Metrics: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}, PortNumber={PN}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, "EID",
eid, "PN", portNumber);
return;
}
for (const auto& [tag, value] : telemetryValues)
{
if (tag < maxTelemetryValues && metricValueInterface[tag])
{
metricValueInterface[tag]->set_property("Value",
static_cast<double>(value));
}
}
}
void NvidiaEthPortMetrics::update()
{
if (!requestEncoded)
{
return;
}
mctpRequester.sendRecvMsg(
eid, request,
[weak{weak_from_this()}](const std::error_code& ec,
std::span<const uint8_t> buffer) {
std::shared_ptr<NvidiaEthPortMetrics> self = weak.lock();
if (!self)
{
lg2::error("Invalid reference to NvidiaEthPortMetrics");
return;
}
self->processResponse(ec, buffer);
});
}