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