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 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 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 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 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 114 void Inventory::markPropertyProcessed( 115 std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it) 116 { 117 it->second.isPending = false; 118 } 119 120 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 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 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 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