| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 1 | #include "Inventory.hpp" |
| 2 | |
| 3 | #include "Utils.hpp" |
| 4 | |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 5 | #include <MctpRequester.hpp> |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 6 | #include <NvidiaGpuMctpVdm.hpp> |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 7 | #include <OcpMctpVdm.hpp> |
| 8 | #include <boost/asio/io_context.hpp> |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 9 | #include <boost/uuid/uuid.hpp> |
| 10 | #include <boost/uuid/uuid_io.hpp> |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 11 | #include <phosphor-logging/lg2.hpp> |
| 12 | #include <sdbusplus/asio/connection.hpp> |
| 13 | #include <sdbusplus/asio/object_server.hpp> |
| 14 | |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 15 | #include <algorithm> |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 16 | #include <cstdint> |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 17 | #include <memory> |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 18 | #include <optional> |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 19 | #include <span> |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 20 | #include <string> |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 21 | #include <system_error> |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 22 | #include <unordered_map> |
| 23 | #include <variant> |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 24 | #include <vector> |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 25 | |
| 26 | constexpr const char* inventoryPrefix = "/xyz/openbmc_project/inventory/"; |
| 27 | constexpr const char* acceleratorIfaceName = |
| 28 | "xyz.openbmc_project.Inventory.Item.Accelerator"; |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 29 | static constexpr const char* assetIfaceName = |
| 30 | "xyz.openbmc_project.Inventory.Decorator.Asset"; |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 31 | static constexpr const char* uuidIfaceName = "xyz.openbmc_project.Common.UUID"; |
| Rohit PAI | 0ad5710 | 2025-06-13 19:29:20 +0530 | [diff] [blame] | 32 | static constexpr const char* revisionIfaceName = |
| 33 | "xyz.openbmc_project.Inventory.Decorator.Revision"; |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 34 | |
| 35 | Inventory::Inventory( |
| 36 | const std::shared_ptr<sdbusplus::asio::connection>& /*conn*/, |
| 37 | sdbusplus::asio::object_server& objectServer, |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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) |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 43 | { |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 44 | std::string path = inventoryPrefix + name; |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 45 | |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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"); |
| Rohit PAI | 0ad5710 | 2025-06-13 19:29:20 +0530 | [diff] [blame] | 53 | registerProperty(gpu::InventoryPropertyId::MARKETING_NAME, assetIface, |
| 54 | "Model"); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 55 | assetIface->initialize(); |
| 56 | |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 57 | uuidInterface = objectServer.add_interface(path, uuidIfaceName); |
| 58 | registerProperty(gpu::InventoryPropertyId::DEVICE_GUID, uuidInterface, |
| 59 | "UUID"); |
| 60 | uuidInterface->initialize(); |
| 61 | |
| Rohit PAI | 0ad5710 | 2025-06-13 19:29:20 +0530 | [diff] [blame] | 62 | revisionIface = objectServer.add_interface(path, revisionIfaceName); |
| 63 | registerProperty(gpu::InventoryPropertyId::DEVICE_PART_NUMBER, |
| 64 | revisionIface, "Version"); |
| 65 | revisionIface->initialize(); |
| 66 | |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 67 | // Static properties |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 68 | if (deviceType == gpu::DeviceIdentification::DEVICE_GPU) |
| 69 | { |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 70 | acceleratorInterface = |
| 71 | objectServer.add_interface(path, acceleratorIfaceName); |
| 72 | acceleratorInterface->register_property("Type", std::string("GPU")); |
| 73 | acceleratorInterface->initialize(); |
| 74 | } |
| Marc Olberding | ac92073 | 2025-09-28 21:56:54 -0700 | [diff] [blame] | 75 | } |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 76 | |
| Marc Olberding | ac92073 | 2025-09-28 21:56:54 -0700 | [diff] [blame] | 77 | void Inventory::init() |
| 78 | { |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 79 | processNextProperty(); |
| 80 | } |
| 81 | |
| 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 | |
| 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) |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 103 | { |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 104 | processNextProperty(); |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 105 | } |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 106 | } |
| 107 | } |
| 108 | |
| 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 | |
| 116 | void Inventory::markPropertyProcessed( |
| 117 | std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it) |
| 118 | { |
| 119 | it->second.isPending = false; |
| 120 | } |
| 121 | |
| 122 | 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 | |
| 135 | void Inventory::sendInventoryPropertyRequest( |
| 136 | gpu::InventoryPropertyId propertyId) |
| 137 | { |
| 138 | int rc = gpu::encodeGetInventoryInformationRequest( |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 139 | 0, static_cast<uint8_t>(propertyId), requestBuffer); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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 | |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 153 | mctpRequester.sendRecvMsg( |
| 154 | eid, requestBuffer, |
| Marc Olberding | fd4a377 | 2025-09-24 16:31:02 -0700 | [diff] [blame] | 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); |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 164 | }); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 165 | } |
| 166 | |
| 167 | void Inventory::handleInventoryPropertyResponse( |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 168 | gpu::InventoryPropertyId propertyId, const std::error_code& ec, |
| 169 | std::span<const uint8_t> buffer) |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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; |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 181 | if (!ec) |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 182 | { |
| 183 | ocp::accelerator_management::CompletionCode cc{}; |
| 184 | uint16_t reasonCode = 0; |
| 185 | gpu::InventoryValue info; |
| 186 | int rc = gpu::decodeGetInventoryInformationResponse( |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 187 | buffer, cc, reasonCode, propertyId, info); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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", |
| Marc Olberding | d0125c9 | 2025-10-08 14:37:19 -0700 | [diff] [blame] | 192 | ec.message(), "RC", rc, "CC", static_cast<uint8_t>(cc), "REASON", |
| 193 | reasonCode); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 194 | |
| 195 | if (rc == 0 && |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 196 | cc == ocp::accelerator_management::CompletionCode::SUCCESS) |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 197 | { |
| Rohit PAI | fb64f06 | 2025-06-13 18:20:02 +0530 | [diff] [blame] | 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 | } |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 269 | } |
| 270 | } |
| 271 | |
| 272 | if (!success) |
| 273 | { |
| 274 | it->second.retryCount++; |
| 275 | if (it->second.retryCount >= maxRetryAttempts) |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 276 | { |
| 277 | lg2::error( |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 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); |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 282 | } |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 283 | else |
| 284 | { |
| 285 | retryTimer.expires_after(retryDelay); |
| Marc Olberding | fd4a377 | 2025-09-24 16:31:02 -0700 | [diff] [blame] | 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 | }); |
| Rohit PAI | ada6baa | 2025-07-01 18:26:19 +0530 | [diff] [blame] | 302 | return; |
| 303 | } |
| 304 | } |
| 305 | else |
| 306 | { |
| 307 | markPropertyProcessed(it); |
| 308 | } |
| 309 | |
| 310 | processNextProperty(); |
| 311 | } |
| 312 | |
| 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); |
| Rohit PAI | 0a88826 | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 325 | } |
| 326 | } |