xref: /openbmc/dbus-sensors/src/nvidia-gpu/Inventory.cpp (revision 0ad57100758c6a6bdddb6e27eb8341a58f299358)
1 #include "Inventory.hpp"
2 
3 #include "Utils.hpp"
4 
5 #include <MctpRequester.hpp>
6 #include <NvidiaGpuMctpVdm.hpp>
7 #include <OcpMctpVdm.hpp>
8 #include <boost/asio/io_context.hpp>
9 #include <boost/uuid/uuid.hpp>
10 #include <boost/uuid/uuid_io.hpp>
11 #include <phosphor-logging/lg2.hpp>
12 #include <sdbusplus/asio/connection.hpp>
13 #include <sdbusplus/asio/object_server.hpp>
14 
15 #include <algorithm>
16 #include <cstdint>
17 #include <memory>
18 #include <optional>
19 #include <string>
20 #include <unordered_map>
21 #include <variant>
22 #include <vector>
23 
24 constexpr const char* inventoryPrefix = "/xyz/openbmc_project/inventory/";
25 constexpr const char* acceleratorIfaceName =
26     "xyz.openbmc_project.Inventory.Item.Accelerator";
27 static constexpr const char* assetIfaceName =
28     "xyz.openbmc_project.Inventory.Decorator.Asset";
29 static constexpr const char* uuidIfaceName = "xyz.openbmc_project.Common.UUID";
30 static constexpr const char* revisionIfaceName =
31     "xyz.openbmc_project.Inventory.Decorator.Revision";
32 
Inventory(const std::shared_ptr<sdbusplus::asio::connection> &,sdbusplus::asio::object_server & objectServer,const std::string & inventoryName,mctp::MctpRequester & mctpRequester,const gpu::DeviceIdentification deviceTypeIn,const uint8_t eid,boost::asio::io_context & io)33 Inventory::Inventory(
34     const std::shared_ptr<sdbusplus::asio::connection>& /*conn*/,
35     sdbusplus::asio::object_server& objectServer,
36     const std::string& inventoryName, mctp::MctpRequester& mctpRequester,
37     const gpu::DeviceIdentification deviceTypeIn, const uint8_t eid,
38     boost::asio::io_context& io) :
39     name(escapeName(inventoryName)), mctpRequester(mctpRequester),
40     deviceType(deviceTypeIn), eid(eid), retryTimer(io)
41 {
42     requestBuffer = std::make_shared<InventoryRequestBuffer>();
43     responseBuffer = std::make_shared<InventoryResponseBuffer>();
44 
45     std::string path = inventoryPrefix + name;
46 
47     assetIface = objectServer.add_interface(path, assetIfaceName);
48     assetIface->register_property("Manufacturer", std::string("NVIDIA"));
49     // Register properties which need to be fetched from the device
50     registerProperty(gpu::InventoryPropertyId::SERIAL_NUMBER, assetIface,
51                      "SerialNumber");
52     registerProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER, assetIface,
53                      "PartNumber");
54     registerProperty(gpu::InventoryPropertyId::MARKETING_NAME, assetIface,
55                      "Model");
56     assetIface->initialize();
57 
58     uuidInterface = objectServer.add_interface(path, uuidIfaceName);
59     registerProperty(gpu::InventoryPropertyId::DEVICE_GUID, uuidInterface,
60                      "UUID");
61     uuidInterface->initialize();
62 
63     revisionIface = objectServer.add_interface(path, revisionIfaceName);
64     registerProperty(gpu::InventoryPropertyId::DEVICE_PART_NUMBER,
65                      revisionIface, "Version");
66     revisionIface->initialize();
67 
68     // Static properties
69     if (deviceType == gpu::DeviceIdentification::DEVICE_GPU)
70     {
71         acceleratorInterface =
72             objectServer.add_interface(path, acceleratorIfaceName);
73         acceleratorInterface->register_property("Type", std::string("GPU"));
74         acceleratorInterface->initialize();
75     }
76 
77     processNextProperty();
78 }
79 
registerProperty(gpu::InventoryPropertyId propertyId,const std::shared_ptr<sdbusplus::asio::dbus_interface> & interface,const std::string & propertyName)80 void Inventory::registerProperty(
81     gpu::InventoryPropertyId propertyId,
82     const std::shared_ptr<sdbusplus::asio::dbus_interface>& interface,
83     const std::string& propertyName)
84 {
85     if (interface)
86     {
87         interface->register_property(propertyName, std::string{});
88         properties[propertyId] = {interface, propertyName, 0, true};
89     }
90 }
91 
processInventoryProperty(gpu::InventoryPropertyId propertyId)92 void Inventory::processInventoryProperty(gpu::InventoryPropertyId propertyId)
93 {
94     auto it = properties.find(propertyId);
95     if (it != properties.end())
96     {
97         markPropertyPending(it);
98         std::optional<gpu::InventoryPropertyId> nextProperty =
99             getNextPendingProperty();
100         if (nextProperty && *nextProperty == propertyId)
101         {
102             processNextProperty();
103         }
104     }
105 }
106 
markPropertyPending(std::unordered_map<gpu::InventoryPropertyId,PropertyInfo>::iterator it)107 void Inventory::markPropertyPending(
108     std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it)
109 {
110     it->second.isPending = true;
111     it->second.retryCount = 0;
112 }
113 
markPropertyProcessed(std::unordered_map<gpu::InventoryPropertyId,PropertyInfo>::iterator it)114 void Inventory::markPropertyProcessed(
115     std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it)
116 {
117     it->second.isPending = false;
118 }
119 
getNextPendingProperty() const120 std::optional<gpu::InventoryPropertyId> Inventory::getNextPendingProperty()
121     const
122 {
123     for (const auto& [propertyId, info] : properties)
124     {
125         if (info.isPending)
126         {
127             return propertyId;
128         }
129     }
130     return std::nullopt;
131 }
132 
sendInventoryPropertyRequest(gpu::InventoryPropertyId propertyId)133 void Inventory::sendInventoryPropertyRequest(
134     gpu::InventoryPropertyId propertyId)
135 {
136     int rc = gpu::encodeGetInventoryInformationRequest(
137         0, static_cast<uint8_t>(propertyId), *requestBuffer);
138     if (rc != 0)
139     {
140         lg2::error(
141             "Failed to encode property ID {PROP_ID} request for {NAME}: rc={RC}",
142             "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RC",
143             rc);
144         return;
145     }
146 
147     lg2::info(
148         "Sending inventory request for property ID {PROP_ID} to EID {EID} for {NAME}",
149         "PROP_ID", static_cast<uint8_t>(propertyId), "EID", eid, "NAME", name);
150 
151     mctpRequester.sendRecvMsg(eid, *requestBuffer, *responseBuffer,
152                               [this, propertyId](int sendRecvMsgResult) {
153                                   this->handleInventoryPropertyResponse(
154                                       propertyId, sendRecvMsgResult);
155                               });
156 }
157 
handleInventoryPropertyResponse(gpu::InventoryPropertyId propertyId,int sendRecvMsgResult)158 void Inventory::handleInventoryPropertyResponse(
159     gpu::InventoryPropertyId propertyId, int sendRecvMsgResult)
160 {
161     auto it = properties.find(propertyId);
162     if (it == properties.end())
163     {
164         lg2::error("Property ID {PROP_ID} for {NAME} not found", "PROP_ID",
165                    static_cast<uint8_t>(propertyId), "NAME", name);
166         processNextProperty();
167         return;
168     }
169 
170     bool success = false;
171     if (sendRecvMsgResult == 0)
172     {
173         ocp::accelerator_management::CompletionCode cc{};
174         uint16_t reasonCode = 0;
175         gpu::InventoryValue info;
176         int rc = gpu::decodeGetInventoryInformationResponse(
177             *responseBuffer, cc, reasonCode, propertyId, info);
178 
179         lg2::info(
180             "Response for property ID {PROP_ID} from {NAME}, sendRecvMsgResult: {RESULT}, decode_rc: {RC}, completion_code: {CC}, reason_code: {REASON}",
181             "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RESULT",
182             sendRecvMsgResult, "RC", rc, "CC", static_cast<uint8_t>(cc),
183             "REASON", reasonCode);
184 
185         if (rc == 0 &&
186             cc == ocp::accelerator_management::CompletionCode::SUCCESS)
187         {
188             std::string value;
189 
190             // Handle different property types based on property ID
191             switch (propertyId)
192             {
193                 case gpu::InventoryPropertyId::BOARD_PART_NUMBER:
194                 case gpu::InventoryPropertyId::SERIAL_NUMBER:
195                 case gpu::InventoryPropertyId::MARKETING_NAME:
196                 case gpu::InventoryPropertyId::DEVICE_PART_NUMBER:
197                     if (std::holds_alternative<std::string>(info))
198                     {
199                         value = std::get<std::string>(info);
200                     }
201                     else
202                     {
203                         lg2::error(
204                             "Property ID {PROP_ID} for {NAME} expected string but got different type",
205                             "PROP_ID", static_cast<uint8_t>(propertyId), "NAME",
206                             name);
207                         break;
208                     }
209                     break;
210 
211                 case gpu::InventoryPropertyId::DEVICE_GUID:
212                     if (std::holds_alternative<std::vector<uint8_t>>(info))
213                     {
214                         const auto& guidBytes =
215                             std::get<std::vector<uint8_t>>(info);
216                         if (guidBytes.size() >= 16)
217                         {
218                             boost::uuids::uuid uuid;
219                             std::copy(guidBytes.begin(), guidBytes.begin() + 16,
220                                       uuid.begin());
221                             value = boost::uuids::to_string(uuid);
222                         }
223                         else
224                         {
225                             lg2::error(
226                                 "Property ID {PROP_ID} for {NAME} GUID size {SIZE} is less than 16 bytes",
227                                 "PROP_ID", static_cast<uint8_t>(propertyId),
228                                 "NAME", name, "SIZE", guidBytes.size());
229                             break;
230                         }
231                     }
232                     else
233                     {
234                         lg2::error(
235                             "Property ID {PROP_ID} for {NAME} expected vector<uint8_t> but got different type",
236                             "PROP_ID", static_cast<uint8_t>(propertyId), "NAME",
237                             name);
238                         break;
239                     }
240                     break;
241 
242                 default:
243                     lg2::error("Unsupported property ID {PROP_ID} for {NAME}",
244                                "PROP_ID", static_cast<uint8_t>(propertyId),
245                                "NAME", name);
246                     break;
247             }
248 
249             if (!value.empty())
250             {
251                 it->second.interface->set_property(it->second.propertyName,
252                                                    value);
253                 lg2::info(
254                     "Successfully received property ID {PROP_ID} for {NAME} with value: {VALUE}",
255                     "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name,
256                     "VALUE", value);
257                 success = true;
258             }
259         }
260     }
261 
262     if (!success)
263     {
264         it->second.retryCount++;
265         if (it->second.retryCount >= maxRetryAttempts)
266         {
267             lg2::error(
268                 "Property ID {PROP_ID} for {NAME} failed after {ATTEMPTS} attempts",
269                 "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name,
270                 "ATTEMPTS", maxRetryAttempts);
271             markPropertyProcessed(it);
272         }
273         else
274         {
275             retryTimer.expires_after(retryDelay);
276             retryTimer.async_wait([this](const boost::system::error_code& ec) {
277                 if (ec)
278                 {
279                     lg2::error("Retry timer error for {NAME}: {ERROR}", "NAME",
280                                name, "ERROR", ec.message());
281                     return;
282                 }
283                 this->processNextProperty();
284             });
285             return;
286         }
287     }
288     else
289     {
290         markPropertyProcessed(it);
291     }
292 
293     processNextProperty();
294 }
295 
processNextProperty()296 void Inventory::processNextProperty()
297 {
298     std::optional<gpu::InventoryPropertyId> nextProperty =
299         getNextPendingProperty();
300     if (nextProperty)
301     {
302         sendInventoryPropertyRequest(*nextProperty);
303     }
304     else
305     {
306         lg2::info("No pending properties found to process for {NAME}", "NAME",
307                   name);
308     }
309 }
310