xref: /openbmc/dbus-sensors/src/nvidia-gpu/NvidiaPciePort.cpp (revision 68a8e2dd92a09d7f1735cce0cbd4aa722816e8ae)
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