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