Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 1 | #include "Inventory.hpp" |
| 2 | |
| 3 | #include "Utils.hpp" |
| 4 | |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 5 | #include <boost/asio/io_context.hpp> |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 6 | #include <phosphor-logging/lg2.hpp> |
| 7 | |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 8 | #include <optional> |
| 9 | #include <string_view> |
| 10 | #include <utility> |
| 11 | |
| 12 | constexpr int maxRetryAttempts = 3; |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 13 | static constexpr const char* inventoryPrefix = |
| 14 | "/xyz/openbmc_project/inventory/"; |
| 15 | static constexpr const char* acceleratorIfaceName = |
| 16 | "xyz.openbmc_project.Inventory.Item.Accelerator"; |
| 17 | static constexpr const char* assetIfaceName = |
| 18 | "xyz.openbmc_project.Inventory.Decorator.Asset"; |
| 19 | |
| 20 | Inventory::Inventory( |
| 21 | const std::shared_ptr<sdbusplus::asio::connection>& /*conn*/, |
| 22 | sdbusplus::asio::object_server& objectServer, |
| 23 | const std::string& inventoryName, mctp::MctpRequester& mctpRequester, |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 24 | DeviceType deviceType, uint8_t eid, boost::asio::io_context& io) : |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 25 | name(escapeName(inventoryName)), mctpRequester(mctpRequester), |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 26 | deviceType(deviceType), eid(eid), retryTimer(io) |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 27 | { |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 28 | requestBuffer = std::make_shared<InventoryRequestBuffer>(); |
| 29 | responseBuffer = std::make_shared<InventoryResponseBuffer>(); |
| 30 | |
| 31 | std::string path = std::string(inventoryPrefix) + name; |
| 32 | acceleratorInterface = |
| 33 | objectServer.add_interface(path, acceleratorIfaceName); |
| 34 | assetIface = objectServer.add_interface(path, assetIfaceName); |
| 35 | |
| 36 | // Static properties |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 37 | if (deviceType == DeviceType::GPU) |
| 38 | { |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 39 | acceleratorInterface->register_property("Type", std::string("GPU")); |
| 40 | } |
| 41 | assetIface->register_property("Manufacturer", std::string("NVIDIA")); |
| 42 | |
| 43 | // Register properties which need to be fetched from the device |
| 44 | registerProperty(gpu::InventoryPropertyId::SERIAL_NUMBER, assetIfaceName, |
| 45 | "SerialNumber"); |
| 46 | registerProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER, |
| 47 | assetIfaceName, "PartNumber"); |
| 48 | |
| 49 | acceleratorInterface->initialize(); |
| 50 | assetIface->initialize(); |
| 51 | processNextProperty(); |
| 52 | } |
| 53 | |
| 54 | void Inventory::registerProperty(gpu::InventoryPropertyId propertyId, |
| 55 | const std::string& interfaceName, |
| 56 | const std::string& propertyName) |
| 57 | { |
| 58 | std::shared_ptr<sdbusplus::asio::dbus_interface> interface; |
| 59 | if (interfaceName == assetIfaceName) |
| 60 | { |
| 61 | interface = assetIface; |
| 62 | } |
| 63 | else if (interfaceName == acceleratorIfaceName) |
| 64 | { |
| 65 | interface = acceleratorInterface; |
| 66 | } |
| 67 | |
| 68 | if (interface) |
| 69 | { |
| 70 | interface->register_property(propertyName, std::string{}); |
| 71 | properties[propertyId] = {interface, propertyName, 0, true}; |
| 72 | } |
| 73 | } |
| 74 | |
| 75 | void Inventory::fetchBoardPartNumber() |
| 76 | { |
| 77 | fetchInventoryProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER); |
| 78 | } |
| 79 | |
| 80 | void Inventory::fetchSerialNumber() |
| 81 | { |
| 82 | fetchInventoryProperty(gpu::InventoryPropertyId::SERIAL_NUMBER); |
| 83 | } |
| 84 | |
| 85 | void Inventory::fetchInventoryProperty(gpu::InventoryPropertyId propertyId) |
| 86 | { |
| 87 | auto it = properties.find(propertyId); |
| 88 | if (it != properties.end()) |
| 89 | { |
| 90 | markPropertyPending(propertyId); |
| 91 | std::optional<gpu::InventoryPropertyId> nextProperty = |
| 92 | getNextPendingProperty(); |
| 93 | if (nextProperty && *nextProperty == propertyId) |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 94 | { |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 95 | processNextProperty(); |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 96 | } |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 97 | } |
| 98 | } |
| 99 | |
| 100 | void Inventory::markPropertyPending(gpu::InventoryPropertyId propertyId) |
| 101 | { |
| 102 | auto it = properties.find(propertyId); |
| 103 | if (it != properties.end()) |
| 104 | { |
| 105 | it->second.isPending = true; |
| 106 | it->second.retryCount = 0; |
| 107 | } |
| 108 | } |
| 109 | |
| 110 | void Inventory::markPropertyProcessed(gpu::InventoryPropertyId propertyId) |
| 111 | { |
| 112 | auto it = properties.find(propertyId); |
| 113 | if (it != properties.end()) |
| 114 | { |
| 115 | it->second.isPending = false; |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | std::optional<gpu::InventoryPropertyId> Inventory::getNextPendingProperty() |
| 120 | const |
| 121 | { |
| 122 | for (const auto& [propertyId, info] : properties) |
| 123 | { |
| 124 | if (info.isPending) |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 125 | { |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 126 | return propertyId; |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 127 | } |
| 128 | } |
Rohit PAI | 15e5d9f | 2025-06-12 17:11:46 +0530 | [diff] [blame^] | 129 | return std::nullopt; |
| 130 | } |
| 131 | |
| 132 | void Inventory::requestInventoryProperty(gpu::InventoryPropertyId propertyId) |
| 133 | { |
| 134 | int rc = gpu::encodeGetInventoryInformationRequest( |
| 135 | 0, static_cast<uint8_t>(propertyId), *requestBuffer); |
| 136 | if (rc != 0) |
| 137 | { |
| 138 | lg2::error("Failed to encode property ID {PROP_ID} request: rc={RC}", |
| 139 | "PROP_ID", static_cast<uint8_t>(propertyId), "RC", rc); |
| 140 | return; |
| 141 | } |
| 142 | |
| 143 | lg2::info( |
| 144 | "Sending inventory request for property ID {PROP_ID} to EID {EID}", |
| 145 | "PROP_ID", static_cast<uint8_t>(propertyId), "EID", eid); |
| 146 | |
| 147 | mctpRequester.sendRecvMsg(eid, *requestBuffer, *responseBuffer, |
| 148 | [this, propertyId](int sendRecvMsgResult) { |
| 149 | this->handleInventoryPropertyResponse( |
| 150 | propertyId, sendRecvMsgResult); |
| 151 | }); |
| 152 | } |
| 153 | |
| 154 | void Inventory::handleInventoryPropertyResponse( |
| 155 | gpu::InventoryPropertyId propertyId, int sendRecvMsgResult) |
| 156 | { |
| 157 | bool success = false; |
| 158 | if (sendRecvMsgResult == 0) |
| 159 | { |
| 160 | ocp::accelerator_management::CompletionCode cc{}; |
| 161 | uint16_t reasonCode = 0; |
| 162 | gpu::InventoryInfo info; |
| 163 | int rc = gpu::decodeGetInventoryInformationResponse( |
| 164 | *responseBuffer, cc, reasonCode, propertyId, info); |
| 165 | |
| 166 | lg2::info( |
| 167 | "Response for property ID {PROP_ID}, sendRecvMsgResult: {RESULT}, decode_rc: {RC}, completion_code: {CC}, reason_code: {REASON}", |
| 168 | "PROP_ID", static_cast<uint8_t>(propertyId), "RESULT", |
| 169 | sendRecvMsgResult, "RC", rc, "CC", static_cast<uint8_t>(cc), |
| 170 | "REASON", reasonCode); |
| 171 | |
| 172 | if (rc == 0 && |
| 173 | cc == ocp::accelerator_management::CompletionCode::SUCCESS && |
| 174 | std::holds_alternative<std::string>(info)) |
| 175 | { |
| 176 | std::string value = std::get<std::string>(info); |
| 177 | auto it = properties.find(propertyId); |
| 178 | if (it != properties.end()) |
| 179 | { |
| 180 | it->second.interface->set_property(it->second.propertyName, |
| 181 | value); |
| 182 | lg2::info( |
| 183 | "Successfully received property ID {PROP_ID} with value: {VALUE}", |
| 184 | "PROP_ID", static_cast<uint8_t>(propertyId), "VALUE", |
| 185 | value); |
| 186 | success = true; |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | if (!success) |
| 192 | { |
| 193 | auto it = properties.find(propertyId); |
| 194 | if (it != properties.end()) |
| 195 | { |
| 196 | it->second.retryCount++; |
| 197 | if (it->second.retryCount >= maxRetryAttempts) |
| 198 | { |
| 199 | lg2::error( |
| 200 | "Property ID {PROP_ID} failed after {ATTEMPTS} attempts", |
| 201 | "PROP_ID", static_cast<uint8_t>(propertyId), "ATTEMPTS", |
| 202 | maxRetryAttempts); |
| 203 | markPropertyProcessed(propertyId); |
| 204 | } |
| 205 | else |
| 206 | { |
| 207 | retryTimer.expires_after(retryDelay); |
| 208 | retryTimer.async_wait( |
| 209 | [this](const boost::system::error_code& ec) { |
| 210 | if (ec) |
| 211 | { |
| 212 | lg2::error("Retry timer error: {ERROR}", "ERROR", |
| 213 | ec.message()); |
| 214 | return; |
| 215 | } |
| 216 | this->processNextProperty(); |
| 217 | }); |
| 218 | return; |
| 219 | } |
| 220 | } |
| 221 | } |
| 222 | else |
| 223 | { |
| 224 | markPropertyProcessed(propertyId); |
| 225 | } |
| 226 | |
| 227 | processNextProperty(); |
| 228 | } |
| 229 | |
| 230 | void Inventory::processNextProperty() |
| 231 | { |
| 232 | std::optional<gpu::InventoryPropertyId> nextProperty = |
| 233 | getNextPendingProperty(); |
| 234 | if (nextProperty) |
| 235 | { |
| 236 | requestInventoryProperty(*nextProperty); |
| 237 | } |
| 238 | else |
| 239 | { |
| 240 | lg2::info("No pending properties found to process"); |
| 241 | } |
| 242 | } |
| 243 | |
| 244 | void Inventory::update() |
| 245 | { |
| 246 | fetchBoardPartNumber(); |
| 247 | fetchSerialNumber(); |
| 248 | } |
| 249 | |
| 250 | std::optional<std::string_view> Inventory::dbusPropertyNameForId( |
| 251 | gpu::InventoryPropertyId propertyId) |
| 252 | { |
| 253 | switch (propertyId) |
| 254 | { |
| 255 | case gpu::InventoryPropertyId::BOARD_PART_NUMBER: |
| 256 | return "PartNumber"; |
| 257 | case gpu::InventoryPropertyId::SERIAL_NUMBER: |
| 258 | return "SerialNumber"; |
| 259 | case gpu::InventoryPropertyId::MARKETING_NAME: |
| 260 | return "MarketingName"; |
| 261 | case gpu::InventoryPropertyId::DEVICE_PART_NUMBER: |
| 262 | return "PartNumber"; |
| 263 | // Add more as needed |
| 264 | default: |
| 265 | return std::nullopt; |
| 266 | } |
Rohit PAI | d87bf7f | 2025-06-11 08:52:29 +0530 | [diff] [blame] | 267 | } |