1 /*
2 * SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include "NvidiaPciePort.hpp"
7
8 #include "Utils.hpp"
9
10 #include <bits/basic_string.h>
11
12 #include <MctpRequester.hpp>
13 #include <NvidiaGpuMctpVdm.hpp>
14 #include <NvidiaPcieDevice.hpp>
15 #include <NvidiaPcieInterface.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 <cstddef>
23 #include <cstdint>
24 #include <functional>
25 #include <limits>
26 #include <memory>
27 #include <span>
28 #include <string>
29 #include <system_error>
30 #include <vector>
31
32 using std::string;
33
34 using namespace std::literals;
35
NvidiaPciePortInfo(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)36 NvidiaPciePortInfo::NvidiaPciePortInfo(
37 std::shared_ptr<sdbusplus::asio::connection>& conn,
38 mctp::MctpRequester& mctpRequester, const std::string& name,
39 const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
40 gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
41 sdbusplus::asio::object_server& objectServer) :
42 eid(eid), portType(portType), upstreamPortNumber(upstreamPortNumber),
43 portNumber(portNumber), path(path), conn(conn), mctpRequester(mctpRequester)
44 {
45 const sdbusplus::message::object_path dbusPath =
46 sdbusplus::message::object_path(pcieDevicePathPrefix) / pcieDeviceName /
47 name;
48
49 pciePortInterface = objectServer.add_interface(
50 dbusPath, "xyz.openbmc_project.Inventory.Connector.Port");
51
52 std::string portTypeStr;
53
54 if (portType == gpu::PciePortType::UPSTREAM)
55 {
56 portTypeStr = "UpstreamPort";
57 }
58 else
59 {
60 portTypeStr = "DownstreamPort";
61 }
62
63 pciePortInterface->register_property(
64 "PortType",
65 "xyz.openbmc_project.Inventory.Connector.Port.PortType." + portTypeStr);
66
67 pciePortInterface->register_property(
68 "PortProtocol",
69 std::string(
70 "xyz.openbmc_project.Inventory.Connector.Port.PortProtocol.PCIe"));
71
72 pciePortInterface->register_property("Speed",
73 std::numeric_limits<uint64_t>::max());
74
75 pciePortInterface->register_property("Width",
76 std::numeric_limits<size_t>::max());
77
78 if (!pciePortInterface->initialize())
79 {
80 lg2::error(
81 "Error initializing PCIe Device Interface for EID={EID}, PortType={PT}, PortNumber={PN}",
82 "EID", eid, "PT", static_cast<uint8_t>(portType), "PN", portNumber);
83 }
84
85 std::vector<Association> associations;
86 associations.emplace_back("connected_to", "connecting",
87 pcieDevicePathPrefix + pcieDeviceName);
88
89 associationInterface =
90 objectServer.add_interface(dbusPath, association::interface);
91 associationInterface->register_property("Associations", associations);
92
93 if (!associationInterface->initialize())
94 {
95 lg2::error(
96 "Error initializing Association Interface for PCIe Port Info for EID={EID}, PortType={PT}, PortNumber={PN}",
97 "EID", eid, "PT", static_cast<uint8_t>(portType), "PN", portNumber);
98 }
99 }
100
mapPcieGenToLinkSpeedBitsPerSecond(uint32_t value)101 uint64_t NvidiaPciePortInfo::mapPcieGenToLinkSpeedBitsPerSecond(uint32_t value)
102 {
103 static constexpr int gbpsToBps = 1 << 30;
104
105 switch (value)
106 {
107 case 1:
108 return 2.5 * gbpsToBps;
109 case 2:
110 return 5.0 * gbpsToBps;
111 case 3:
112 return 8.0 * gbpsToBps;
113 case 4:
114 return 16.0 * gbpsToBps;
115 case 5:
116 return 32.0 * gbpsToBps;
117 case 6:
118 return 64.0 * gbpsToBps;
119 default:
120 return 0;
121 }
122 }
123
processResponse(const std::error_code & sendRecvMsgResult,std::span<const uint8_t> response)124 void NvidiaPciePortInfo::processResponse(
125 const std::error_code& sendRecvMsgResult, std::span<const uint8_t> response)
126 {
127 if (sendRecvMsgResult)
128 {
129 lg2::error(
130 "Error updating PCIe Port Info: sending message over MCTP failed, "
131 "rc={RC}, EID={EID}, PortType={PT}, PortNumber={PN}",
132 "RC", sendRecvMsgResult.message(), "EID", eid, "PT",
133 static_cast<uint8_t>(portType), "PN", portNumber);
134 return;
135 }
136
137 ocp::accelerator_management::CompletionCode cc{};
138 uint16_t reasonCode = 0;
139 size_t numTelemetryValue = 0;
140
141 const int rc = gpu::decodeQueryScalarGroupTelemetryV2Response(
142 response, cc, reasonCode, numTelemetryValue, telemetryValues);
143
144 if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
145 {
146 lg2::error(
147 "Error updating PCIe Port Info: decode failed, "
148 "rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}, PortType={PT}, PortNumber={PN}",
149 "RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, "EID",
150 eid, "PT", static_cast<uint8_t>(portType), "PN", portNumber);
151 return;
152 }
153
154 if (telemetryValues.size() < 2)
155 {
156 lg2::error(
157 "Error updating PCIe Port Info: insufficient telemetry values, "
158 "NumValues={NUM}, EID={EID}, PortType={PT}, PortNumber={PN}",
159 "NUM", telemetryValues.size(), "EID", eid, "PT",
160 static_cast<uint8_t>(portType), "PN", portNumber);
161 return;
162 }
163
164 pciePortInterface->set_property(
165 "Speed", mapPcieGenToLinkSpeedBitsPerSecond(telemetryValues[0]));
166
167 pciePortInterface->set_property(
168 "Width", NvidiaPcieInterface::decodeLinkWidth(telemetryValues[1]));
169 }
170
update()171 void NvidiaPciePortInfo::update()
172 {
173 auto rc = gpu::encodeQueryScalarGroupTelemetryV2Request(
174 0, portType, upstreamPortNumber, portNumber, 1, request);
175
176 if (rc != 0)
177 {
178 lg2::error(
179 "Error updating PCIe Port Info: encode failed, rc={RC}, EID={EID}, PortType={PT}, PortNumber={PN}",
180 "RC", rc, "EID", eid, "PT", static_cast<uint8_t>(portType), "PN",
181 portNumber);
182 return;
183 }
184
185 mctpRequester.sendRecvMsg(
186 eid, request,
187 [weak{weak_from_this()}](const std::error_code& ec,
188 std::span<const uint8_t> buffer) {
189 std::shared_ptr<NvidiaPciePortInfo> self = weak.lock();
190 if (!self)
191 {
192 lg2::error("Invalid reference to NvidiaPciePortInfo");
193 return;
194 }
195 self->processResponse(ec, buffer);
196 });
197 }
198