| /* |
| * 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); |
| }); |
| } |