| #include "Inventory.hpp" |
| |
| #include "Utils.hpp" |
| |
| #include <MctpRequester.hpp> |
| #include <NvidiaGpuMctpVdm.hpp> |
| #include <OcpMctpVdm.hpp> |
| #include <boost/asio/io_context.hpp> |
| #include <boost/uuid/uuid.hpp> |
| #include <boost/uuid/uuid_io.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| |
| #include <algorithm> |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <unordered_map> |
| #include <variant> |
| #include <vector> |
| |
| constexpr const char* inventoryPrefix = "/xyz/openbmc_project/inventory/"; |
| constexpr const char* acceleratorIfaceName = |
| "xyz.openbmc_project.Inventory.Item.Accelerator"; |
| static constexpr const char* assetIfaceName = |
| "xyz.openbmc_project.Inventory.Decorator.Asset"; |
| static constexpr const char* uuidIfaceName = "xyz.openbmc_project.Common.UUID"; |
| static constexpr const char* revisionIfaceName = |
| "xyz.openbmc_project.Inventory.Decorator.Revision"; |
| |
| Inventory::Inventory( |
| const std::shared_ptr<sdbusplus::asio::connection>& /*conn*/, |
| 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) : |
| name(escapeName(inventoryName)), mctpRequester(mctpRequester), |
| deviceType(deviceTypeIn), eid(eid), retryTimer(io) |
| { |
| requestBuffer = std::make_shared<InventoryRequestBuffer>(); |
| responseBuffer = std::make_shared<InventoryResponseBuffer>(); |
| |
| std::string path = inventoryPrefix + name; |
| |
| assetIface = objectServer.add_interface(path, assetIfaceName); |
| assetIface->register_property("Manufacturer", std::string("NVIDIA")); |
| // Register properties which need to be fetched from the device |
| registerProperty(gpu::InventoryPropertyId::SERIAL_NUMBER, assetIface, |
| "SerialNumber"); |
| registerProperty(gpu::InventoryPropertyId::BOARD_PART_NUMBER, assetIface, |
| "PartNumber"); |
| registerProperty(gpu::InventoryPropertyId::MARKETING_NAME, assetIface, |
| "Model"); |
| assetIface->initialize(); |
| |
| uuidInterface = objectServer.add_interface(path, uuidIfaceName); |
| registerProperty(gpu::InventoryPropertyId::DEVICE_GUID, uuidInterface, |
| "UUID"); |
| uuidInterface->initialize(); |
| |
| revisionIface = objectServer.add_interface(path, revisionIfaceName); |
| registerProperty(gpu::InventoryPropertyId::DEVICE_PART_NUMBER, |
| revisionIface, "Version"); |
| revisionIface->initialize(); |
| |
| // Static properties |
| if (deviceType == gpu::DeviceIdentification::DEVICE_GPU) |
| { |
| acceleratorInterface = |
| objectServer.add_interface(path, acceleratorIfaceName); |
| acceleratorInterface->register_property("Type", std::string("GPU")); |
| acceleratorInterface->initialize(); |
| } |
| |
| processNextProperty(); |
| } |
| |
| void Inventory::registerProperty( |
| gpu::InventoryPropertyId propertyId, |
| const std::shared_ptr<sdbusplus::asio::dbus_interface>& interface, |
| const std::string& propertyName) |
| { |
| if (interface) |
| { |
| interface->register_property(propertyName, std::string{}); |
| properties[propertyId] = {interface, propertyName, 0, true}; |
| } |
| } |
| |
| void Inventory::processInventoryProperty(gpu::InventoryPropertyId propertyId) |
| { |
| auto it = properties.find(propertyId); |
| if (it != properties.end()) |
| { |
| markPropertyPending(it); |
| std::optional<gpu::InventoryPropertyId> nextProperty = |
| getNextPendingProperty(); |
| if (nextProperty && *nextProperty == propertyId) |
| { |
| processNextProperty(); |
| } |
| } |
| } |
| |
| void Inventory::markPropertyPending( |
| std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it) |
| { |
| it->second.isPending = true; |
| it->second.retryCount = 0; |
| } |
| |
| void Inventory::markPropertyProcessed( |
| std::unordered_map<gpu::InventoryPropertyId, PropertyInfo>::iterator it) |
| { |
| it->second.isPending = false; |
| } |
| |
| std::optional<gpu::InventoryPropertyId> Inventory::getNextPendingProperty() |
| const |
| { |
| for (const auto& [propertyId, info] : properties) |
| { |
| if (info.isPending) |
| { |
| return propertyId; |
| } |
| } |
| return std::nullopt; |
| } |
| |
| void Inventory::sendInventoryPropertyRequest( |
| gpu::InventoryPropertyId propertyId) |
| { |
| int rc = gpu::encodeGetInventoryInformationRequest( |
| 0, static_cast<uint8_t>(propertyId), *requestBuffer); |
| if (rc != 0) |
| { |
| lg2::error( |
| "Failed to encode property ID {PROP_ID} request for {NAME}: rc={RC}", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RC", |
| rc); |
| return; |
| } |
| |
| lg2::info( |
| "Sending inventory request for property ID {PROP_ID} to EID {EID} for {NAME}", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "EID", eid, "NAME", name); |
| |
| mctpRequester.sendRecvMsg(eid, *requestBuffer, *responseBuffer, |
| [this, propertyId](int sendRecvMsgResult) { |
| this->handleInventoryPropertyResponse( |
| propertyId, sendRecvMsgResult); |
| }); |
| } |
| |
| void Inventory::handleInventoryPropertyResponse( |
| gpu::InventoryPropertyId propertyId, int sendRecvMsgResult) |
| { |
| auto it = properties.find(propertyId); |
| if (it == properties.end()) |
| { |
| lg2::error("Property ID {PROP_ID} for {NAME} not found", "PROP_ID", |
| static_cast<uint8_t>(propertyId), "NAME", name); |
| processNextProperty(); |
| return; |
| } |
| |
| bool success = false; |
| if (sendRecvMsgResult == 0) |
| { |
| ocp::accelerator_management::CompletionCode cc{}; |
| uint16_t reasonCode = 0; |
| gpu::InventoryValue info; |
| int rc = gpu::decodeGetInventoryInformationResponse( |
| *responseBuffer, cc, reasonCode, propertyId, info); |
| |
| lg2::info( |
| "Response for property ID {PROP_ID} from {NAME}, sendRecvMsgResult: {RESULT}, decode_rc: {RC}, completion_code: {CC}, reason_code: {REASON}", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, "RESULT", |
| sendRecvMsgResult, "RC", rc, "CC", static_cast<uint8_t>(cc), |
| "REASON", reasonCode); |
| |
| if (rc == 0 && |
| cc == ocp::accelerator_management::CompletionCode::SUCCESS) |
| { |
| std::string value; |
| |
| // Handle different property types based on property ID |
| switch (propertyId) |
| { |
| case gpu::InventoryPropertyId::BOARD_PART_NUMBER: |
| case gpu::InventoryPropertyId::SERIAL_NUMBER: |
| case gpu::InventoryPropertyId::MARKETING_NAME: |
| case gpu::InventoryPropertyId::DEVICE_PART_NUMBER: |
| if (std::holds_alternative<std::string>(info)) |
| { |
| value = std::get<std::string>(info); |
| } |
| else |
| { |
| lg2::error( |
| "Property ID {PROP_ID} for {NAME} expected string but got different type", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", |
| name); |
| break; |
| } |
| break; |
| |
| case gpu::InventoryPropertyId::DEVICE_GUID: |
| if (std::holds_alternative<std::vector<uint8_t>>(info)) |
| { |
| const auto& guidBytes = |
| std::get<std::vector<uint8_t>>(info); |
| if (guidBytes.size() >= 16) |
| { |
| boost::uuids::uuid uuid; |
| std::copy(guidBytes.begin(), guidBytes.begin() + 16, |
| uuid.begin()); |
| value = boost::uuids::to_string(uuid); |
| } |
| else |
| { |
| lg2::error( |
| "Property ID {PROP_ID} for {NAME} GUID size {SIZE} is less than 16 bytes", |
| "PROP_ID", static_cast<uint8_t>(propertyId), |
| "NAME", name, "SIZE", guidBytes.size()); |
| break; |
| } |
| } |
| else |
| { |
| lg2::error( |
| "Property ID {PROP_ID} for {NAME} expected vector<uint8_t> but got different type", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", |
| name); |
| break; |
| } |
| break; |
| |
| default: |
| lg2::error("Unsupported property ID {PROP_ID} for {NAME}", |
| "PROP_ID", static_cast<uint8_t>(propertyId), |
| "NAME", name); |
| break; |
| } |
| |
| if (!value.empty()) |
| { |
| it->second.interface->set_property(it->second.propertyName, |
| value); |
| lg2::info( |
| "Successfully received property ID {PROP_ID} for {NAME} with value: {VALUE}", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, |
| "VALUE", value); |
| success = true; |
| } |
| } |
| } |
| |
| if (!success) |
| { |
| it->second.retryCount++; |
| if (it->second.retryCount >= maxRetryAttempts) |
| { |
| lg2::error( |
| "Property ID {PROP_ID} for {NAME} failed after {ATTEMPTS} attempts", |
| "PROP_ID", static_cast<uint8_t>(propertyId), "NAME", name, |
| "ATTEMPTS", maxRetryAttempts); |
| markPropertyProcessed(it); |
| } |
| else |
| { |
| retryTimer.expires_after(retryDelay); |
| retryTimer.async_wait([this](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| lg2::error("Retry timer error for {NAME}: {ERROR}", "NAME", |
| name, "ERROR", ec.message()); |
| return; |
| } |
| this->processNextProperty(); |
| }); |
| return; |
| } |
| } |
| else |
| { |
| markPropertyProcessed(it); |
| } |
| |
| processNextProperty(); |
| } |
| |
| void Inventory::processNextProperty() |
| { |
| std::optional<gpu::InventoryPropertyId> nextProperty = |
| getNextPendingProperty(); |
| if (nextProperty) |
| { |
| sendInventoryPropertyRequest(*nextProperty); |
| } |
| else |
| { |
| lg2::info("No pending properties found to process for {NAME}", "NAME", |
| name); |
| } |
| } |