xref: /openbmc/dbus-sensors/src/nvidia-gpu/NvidiaEthPort.cpp (revision 7427aeef4225bf23715539b195a23bce10865265)
1 /*
2  * SPDX-FileCopyrightText: Copyright OpenBMC Authors
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #include "NvidiaEthPort.hpp"
7 
8 #include "NvidiaUtils.hpp"
9 #include "Utils.hpp"
10 
11 #include <bits/basic_string.h>
12 
13 #include <MctpRequester.hpp>
14 #include <NvidiaGpuMctpVdm.hpp>
15 #include <NvidiaPcieDevice.hpp>
16 #include <OcpMctpVdm.hpp>
17 #include <phosphor-logging/lg2.hpp>
18 #include <sdbusplus/asio/connection.hpp>
19 #include <sdbusplus/asio/object_server.hpp>
20 #include <sdbusplus/message/native_types.hpp>
21 
22 #include <array>
23 #include <cstdint>
24 #include <format>
25 #include <functional>
26 #include <memory>
27 #include <span>
28 #include <string>
29 #include <system_error>
30 #include <utility>
31 #include <vector>
32 
33 using std::string;
34 
35 using namespace std::literals;
36 
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)37 NvidiaEthPortMetrics::NvidiaEthPortMetrics(
38     std::shared_ptr<sdbusplus::asio::connection>& conn,
39     mctp::MctpRequester& mctpRequester, const std::string& name,
40     const std::string& deviceName, const std::string& path, uint8_t eid,
41     uint16_t portNumber, sdbusplus::asio::object_server& objectServer) :
42     eid(eid), portNumber(portNumber), path(path), conn(conn),
43     mctpRequester(mctpRequester)
44 {
45     const sdbusplus::message::object_path deviceDbusPath =
46         sdbusplus::message::object_path(nicPathPrefix) / deviceName;
47 
48     const sdbusplus::message::object_path portDbusPath =
49         sdbusplus::message::object_path(nicPathPrefix) / deviceName / name;
50 
51     const std::string metricsDbusPathPrefix =
52         metricPath + std::format("port_{}_{}", deviceName, name);
53 
54     portInterface = objectServer.add_interface(
55         portDbusPath, "xyz.openbmc_project.Inventory.Connector.Port");
56 
57     std::vector<Association> associations;
58     associations.emplace_back("connected_to", "connecting", deviceDbusPath);
59 
60     associationInterface =
61         objectServer.add_interface(portDbusPath, association::interface);
62 
63     associationInterface->register_property("Associations", associations);
64 
65     constexpr std::array<std::pair<uint8_t, const char*>, 21> telemetryMetrics =
66         {{
67             {0, "/nic/rx_bytes"},
68             {1, "/nic/tx_bytes"},
69             {2, "/nic/rx_unicast_frames"},
70             {3, "/nic/rx_multicast_frames"},
71             {4, "/nic/rx_broadcast_frames"},
72             {5, "/nic/tx_unicast_frames"},
73             {6, "/nic/tx_multicast_frames"},
74             {7, "/nic/tx_broadcast_frames"},
75             {8, "/nic/rx_fcs_errors"},
76             {9, "/nic/rx_frame_alignment_errors"},
77             {10, "/nic/rx_false_carrier_errors"},
78             {11, "/nic/rx_undersize_frames"},
79             {12, "/nic/rx_oversize_frames"},
80             {13, "/nic/rx_pause_xon_frames"},
81             {14, "/nic/rx_pause_xoff_frames"},
82             {15, "/nic/tx_pause_xon_frames"},
83             {16, "/nic/tx_pause_xoff_frames"},
84             {17, "/nic/tx_single_collisions"},
85             {18, "/nic/tx_multiple_collisions"},
86             {19, "/nic/tx_late_collisions"},
87             {20, "/nic/tx_excessive_collisions"},
88         }};
89 
90     for (const auto& [tag, metricName] : telemetryMetrics)
91     {
92         metricValueInterface[tag] =
93             objectServer.add_interface(metricsDbusPathPrefix + metricName,
94                                        "xyz.openbmc_project.Metric.Value");
95 
96         metricValueInterface[tag]->register_property(
97             "Unit", "xyz.openbmc_project.Metric.Value.Unit.Count"s);
98         metricValueInterface[tag]->register_property("Value", 0.0);
99 
100         std::vector<Association> associations;
101         associations.emplace_back("measuring", "measured_by", portDbusPath);
102 
103         metricAssociationInterfaces[tag] = objectServer.add_interface(
104             metricsDbusPathPrefix + metricName, association::interface);
105         metricAssociationInterfaces[tag]->register_property("Associations",
106                                                             associations);
107         if (!metricValueInterface[tag]->initialize())
108         {
109             lg2::error(
110                 "Error initializing Ethernet Port Metric Interface for EID={EID}, PortNumber={PN}, Metric={MN}",
111                 "EID", eid, "PN", portNumber, "MN", metricName);
112         }
113 
114         if (!metricAssociationInterfaces[tag]->initialize())
115         {
116             lg2::error(
117                 "Error initializing Ethernet Port Metric Association Interface for EID={EID}, PortNumber={PN}, Metric={MN}",
118                 "EID", eid, "PN", portNumber, "MN", metricName);
119         }
120     }
121 
122     if (!portInterface->initialize())
123     {
124         lg2::error(
125             "Error initializing Ethernet Port Interface for EID={EID}, PortNumber={PN}",
126             "EID", eid, "PN", portNumber);
127     }
128 
129     if (!associationInterface->initialize())
130     {
131         lg2::error(
132             "Error initializing Association Interface for Ethernet Port for EID={EID}, PortNumber={PN}",
133             "EID", eid, "PN", portNumber);
134     }
135 }
136 
processResponse(const std::error_code & sendRecvMsgResult,std::span<const uint8_t> response)137 void NvidiaEthPortMetrics::processResponse(
138     const std::error_code& sendRecvMsgResult, std::span<const uint8_t> response)
139 {
140     if (sendRecvMsgResult)
141     {
142         lg2::error(
143             "Error updating Ethernet Port Metrics: sending message over MCTP failed, "
144             "rc={RC}, EID={EID}, PortNumber={PN}",
145             "RC", sendRecvMsgResult.message(), "EID", eid, "PN", portNumber);
146         return;
147     }
148 
149     ocp::accelerator_management::CompletionCode cc{};
150     uint16_t reasonCode = 0;
151     std::vector<std::pair<uint8_t, uint64_t>> telemetryValues;
152 
153     const int rc = gpu::decodeGetEthernetPortTelemetryCountersResponse(
154         response, cc, reasonCode, telemetryValues);
155 
156     if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
157     {
158         lg2::error(
159             "Error updating Ethernet Port Metrics: decode failed, "
160             "rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}, PortNumber={PN}",
161             "RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, "EID",
162             eid, "PN", portNumber);
163         return;
164     }
165 
166     for (const auto& [tag, value] : telemetryValues)
167     {
168         if (tag < maxTelemetryValues && metricValueInterface[tag])
169         {
170             metricValueInterface[tag]->set_property("Value",
171                                                     static_cast<double>(value));
172         }
173     }
174 }
175 
update()176 void NvidiaEthPortMetrics::update()
177 {
178     const int rc = gpu::encodeGetEthernetPortTelemetryCountersRequest(
179         0, portNumber, request);
180 
181     if (rc != 0)
182     {
183         lg2::error(
184             "Error updating Ethernet Port Metrics: encode failed, rc={RC}, EID={EID}, PortNumber={PN}",
185             "RC", rc, "EID", eid, "PN", portNumber);
186         return;
187     }
188 
189     mctpRequester.sendRecvMsg(
190         eid, request,
191         [weak{weak_from_this()}](const std::error_code& ec,
192                                  std::span<const uint8_t> buffer) {
193             std::shared_ptr<NvidiaEthPortMetrics> self = weak.lock();
194             if (!self)
195             {
196                 lg2::error("Invalid reference to NvidiaEthPortMetrics");
197                 return;
198             }
199             self->processResponse(ec, buffer);
200         });
201 }
202