| /* |
| * SPDX-FileCopyrightText: Copyright OpenBMC Authors |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include "DeviceManager.hpp" |
| |
| #include "NvidiaGpuDevice.hpp" |
| #include "NvidiaPcieDevice.hpp" |
| #include "NvidiaSmaDevice.hpp" |
| #include "Utils.hpp" |
| |
| #include <MctpRequester.hpp> |
| #include <NvidiaGpuMctpVdm.hpp> |
| #include <NvidiaSensorConfig.hpp> |
| #include <OcpMctpVdm.hpp> |
| #include <boost/asio/io_context.hpp> |
| #include <boost/container/flat_map.hpp> |
| #include <phosphor-logging/lg2.hpp> |
| #include <sdbusplus/asio/connection.hpp> |
| #include <sdbusplus/asio/object_server.hpp> |
| #include <sdbusplus/message.hpp> |
| #include <sdbusplus/message/native_types.hpp> |
| |
| #include <algorithm> |
| #include <array> |
| #include <cstdint> |
| #include <format> |
| #include <memory> |
| #include <span> |
| #include <stdexcept> |
| #include <string> |
| #include <string_view> |
| #include <system_error> |
| #include <utility> |
| #include <variant> |
| #include <vector> |
| |
| DeviceManager::DeviceManager(boost::asio::io_context& io, |
| sdbusplus::asio::object_server& objectServer, |
| std::shared_ptr<sdbusplus::asio::connection> conn, |
| mctp::MctpRequester& mctpRequester) : |
| io(io), objectServer(objectServer), conn(std::move(conn)), |
| mctpRequester(mctpRequester) |
| {} |
| |
| void DeviceManager::processQueryDeviceIdResponse( |
| const SensorConfigs& configs, const sdbusplus::object_path& path, |
| uint8_t eid, const std::error_code& sendRecvMsgResult, |
| std::span<const uint8_t> queryDeviceIdentificationResponse) |
| { |
| if (sendRecvMsgResult) |
| { |
| lg2::error( |
| "Error processing MCTP endpoint with eid {EID} : sending message over MCTP failed, rc={RC}", |
| "EID", eid, "RC", sendRecvMsgResult.message()); |
| return; |
| } |
| |
| ocp::accelerator_management::CompletionCode cc{}; |
| uint16_t reasonCode = 0; |
| uint8_t responseDeviceType = 0; |
| uint8_t responseInstanceId = 0; |
| |
| auto rc = gpu::decodeQueryDeviceIdentificationResponse( |
| queryDeviceIdentificationResponse, cc, reasonCode, responseDeviceType, |
| responseInstanceId); |
| |
| if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS) |
| { |
| lg2::error( |
| "Error processing MCTP endpoint with eid {EID} : decode failed, rc={RC}, cc={CC}, reasonCode={RESC}", |
| "EID", eid, "RC", rc, "CC", cc, "RESC", reasonCode); |
| return; |
| } |
| |
| switch (static_cast<gpu::DeviceIdentification>(responseDeviceType)) |
| { |
| case gpu::DeviceIdentification::DEVICE_GPU: |
| { |
| lg2::info( |
| "Found the GPU with EID {EID}, DeviceType {DEVTYPE}, InstanceId {IID}.", |
| "EID", eid, "DEVTYPE", responseDeviceType, "IID", |
| responseInstanceId); |
| |
| const std::string gpuName = std::format("Nvidia_GPU_{}", eid); |
| |
| std::shared_ptr<GpuDevice>& gpu = gpuDevices[gpuName]; |
| |
| if (gpu == nullptr) |
| { |
| gpu = std::make_shared<GpuDevice>(configs, gpuName, path, conn, |
| eid, io, mctpRequester, |
| objectServer); |
| |
| gpu->init(); |
| } |
| else |
| { |
| lg2::info( |
| "GPU Device with name {NAME} already exists. Skipping creating a new device.", |
| "NAME", gpuName); |
| } |
| |
| break; |
| } |
| |
| case gpu::DeviceIdentification::DEVICE_SMA: |
| { |
| lg2::info( |
| "Found the SMA Device with EID {EID}, DeviceType {DEVTYPE}, InstanceId {IID}.", |
| "EID", eid, "DEVTYPE", responseDeviceType, "IID", |
| responseInstanceId); |
| |
| const std::string smaName = std::format("Nvidia_SMA_{}", eid); |
| |
| std::shared_ptr<SmaDevice>& sma = smaDevices[smaName]; |
| |
| if (sma == nullptr) |
| { |
| sma = std::make_shared<SmaDevice>(configs, smaName, path, conn, |
| eid, io, mctpRequester, |
| objectServer); |
| |
| sma->init(); |
| } |
| else |
| { |
| lg2::info( |
| "SMA Device with name {NAME} already exists. Skipping creating a new device.", |
| "NAME", smaName); |
| } |
| |
| break; |
| } |
| |
| case gpu::DeviceIdentification::DEVICE_PCIE: |
| { |
| lg2::info( |
| "Found the PCIe Device with EID {EID}, DeviceType {DEVTYPE}, InstanceId {IID}.", |
| "EID", eid, "DEVTYPE", responseDeviceType, "IID", |
| responseInstanceId); |
| |
| const std::string pcieName = std::format("Nvidia_ConnectX_{}", eid); |
| |
| std::shared_ptr<PcieDevice>& pcie = pcieDevices[pcieName]; |
| |
| if (pcie == nullptr) |
| { |
| pcie = std::make_shared<PcieDevice>( |
| configs, pcieName, path, conn, eid, io, mctpRequester, |
| objectServer); |
| |
| pcie->init(); |
| } |
| else |
| { |
| lg2::info( |
| "PCIe Device with name {NAME} already exists. Skipping creating a new device.", |
| "NAME", pcieName); |
| } |
| |
| break; |
| } |
| |
| default: |
| lg2::error("Unknown device type {TYPE} for EID {EID}", "TYPE", |
| responseDeviceType, "EID", eid); |
| break; |
| } |
| } |
| |
| void DeviceManager::queryDeviceIdentification( |
| const SensorConfigs& configs, const sdbusplus::object_path& path, |
| uint8_t eid) |
| { |
| auto queryDeviceIdentificationRequest = std::make_shared< |
| std::array<uint8_t, gpu::queryDeviceIdentificationRequestSize>>(); |
| |
| auto rc = gpu::encodeQueryDeviceIdentificationRequest( |
| 0, *queryDeviceIdentificationRequest); |
| if (rc != 0) |
| { |
| lg2::error( |
| "Error processing MCTP endpoint with eid {EID} : encode failed, rc={RC}", |
| "EID", eid, "RC", rc); |
| return; |
| } |
| |
| mctpRequester.sendRecvMsg( |
| eid, *queryDeviceIdentificationRequest, |
| [this, configs, path, eid, queryDeviceIdentificationRequest]( |
| const std::error_code& ec, std::span<const uint8_t> response) { |
| processQueryDeviceIdResponse(configs, path, eid, ec, response); |
| }); |
| } |
| |
| void DeviceManager::processEndpoint( |
| const SensorConfigs& configs, const sdbusplus::object_path& path, |
| const boost::system::error_code& ec, const SensorBaseConfigMap& endpoint) |
| { |
| if (ec) |
| { |
| lg2::error("Error processing MCTP endpoint: Error:{ERROR}", "ERROR", |
| ec.message()); |
| return; |
| } |
| |
| auto hasEid = endpoint.find("EID"); |
| uint8_t eid{}; |
| |
| if (hasEid != endpoint.end()) |
| { |
| const auto* eidPtr = std::get_if<uint8_t>(&hasEid->second); |
| if (eidPtr != nullptr) |
| { |
| eid = *eidPtr; |
| } |
| else |
| { |
| lg2::error( |
| "Error processing MCTP endpoint: Property EID does not have valid type."); |
| return; |
| } |
| } |
| else |
| { |
| lg2::error( |
| "Error processing MCTP endpoint: Property EID not found in the configuration."); |
| return; |
| } |
| |
| auto hasMctpTypes = endpoint.find("SupportedMessageTypes"); |
| std::vector<uint8_t> mctpTypes{}; |
| |
| if (hasMctpTypes != endpoint.end()) |
| { |
| const auto* mctpTypePtr = |
| std::get_if<std::vector<uint8_t>>(&hasMctpTypes->second); |
| if (mctpTypePtr != nullptr) |
| { |
| mctpTypes = *mctpTypePtr; |
| } |
| else |
| { |
| lg2::error( |
| "Error processing MCTP endpoint with eid {EID} : Property SupportedMessageTypes does not have valid type.", |
| "EID", eid); |
| return; |
| } |
| } |
| else |
| { |
| lg2::error( |
| "Error processing MCTP endpoint with eid {EID} : Property SupportedMessageTypes not found in the configuration.", |
| "EID", eid); |
| return; |
| } |
| |
| if (std::find(mctpTypes.begin(), mctpTypes.end(), |
| ocp::accelerator_management::messageType) != mctpTypes.end()) |
| { |
| lg2::info("Found OCP MCTP VDM Endpoint with ID {EID}", "EID", eid); |
| queryDeviceIdentification(configs, path, eid); |
| } |
| } |
| |
| void DeviceManager::queryEndpoints( |
| const SensorConfigs& configs, const sdbusplus::object_path& path, |
| const boost::system::error_code& ec, const GetSubTreeType& ret) |
| { |
| if (ec) |
| { |
| lg2::error("Error processing MCTP endpoints: {ERROR}", "ERROR", |
| ec.message()); |
| return; |
| } |
| |
| if (ret.empty()) |
| { |
| return; |
| } |
| |
| for (const auto& [objPath, services] : ret) |
| { |
| for (const auto& [service, ifaces] : services) |
| { |
| for (const auto& iface : ifaces) |
| { |
| if (iface == "xyz.openbmc_project.MCTP.Endpoint") |
| { |
| conn->async_method_call( |
| [this, configs, |
| path](const boost::system::error_code& ec, |
| const SensorBaseConfigMap& endpoint) { |
| processEndpoint(configs, path, ec, endpoint); |
| }, |
| service, objPath, "org.freedesktop.DBus.Properties", |
| "GetAll", iface); |
| } |
| } |
| } |
| } |
| } |
| |
| void DeviceManager::discoverDevices(const SensorConfigs& configs, |
| const sdbusplus::object_path& path) |
| { |
| std::string searchPath{"/au/com/codeconstruct/"}; |
| std::vector<std::string> ifaceList{{"xyz.openbmc_project.MCTP.Endpoint"}}; |
| |
| conn->async_method_call( |
| [this, configs, |
| path](const boost::system::error_code& ec, const GetSubTreeType& ret) { |
| queryEndpoints(configs, path, ec, ret); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetSubTree", searchPath, 0, |
| ifaceList); |
| } |
| |
| void DeviceManager::processSensorConfigs(const ManagedObjectType& resp) |
| { |
| for (const auto& [path, interfaces] : resp) |
| { |
| for (const auto& [intf, cfg] : interfaces) |
| { |
| if (intf != configInterfaceName(sensorType)) |
| { |
| continue; |
| } |
| |
| SensorConfigs configs; |
| |
| configs.name = loadVariant<std::string>(cfg, "Name"); |
| |
| try |
| { |
| configs.pollRate = loadVariant<uint64_t>(cfg, "PollRate"); |
| } |
| catch (const std::invalid_argument&) |
| { |
| // PollRate is an optional config |
| configs.pollRate = sensorPollRateMs; |
| } |
| |
| try |
| { |
| configs.nicNetworkPortCount = |
| loadVariant<uint64_t>(cfg, "NicNetworkPortCount"); |
| } |
| catch (const std::invalid_argument&) |
| { |
| // NicNetworkPortCount is an optional config |
| configs.nicNetworkPortCount = 0; |
| } |
| |
| discoverDevices(configs, path); |
| |
| lg2::info( |
| "Detected configuration {NAME} of type {TYPE} at path: {PATH}.", |
| "NAME", configs.name, "TYPE", sensorType, "PATH", path); |
| } |
| } |
| } |
| |
| void DeviceManager::createSensors() |
| { |
| if (!conn) |
| { |
| lg2::error("Connection not created"); |
| return; |
| } |
| conn->async_method_call( |
| [this](boost::system::error_code ec, const ManagedObjectType& resp) { |
| if (ec) |
| { |
| lg2::error("Error contacting entity manager"); |
| return; |
| } |
| |
| processSensorConfigs(resp); |
| }, |
| entityManagerName, "/xyz/openbmc_project/inventory", |
| "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
| } |
| |
| void DeviceManager::onConfigInterfaceRemoved(sdbusplus::message_t& message) |
| { |
| sdbusplus::object_path removedPath; |
| std::vector<std::string> interfaces; |
| |
| message.read(removedPath, interfaces); |
| |
| // If the xyz.openbmc_project.Configuration.X interface was removed |
| // for one or more sensors, delete those sensor objects. |
| auto sensorIt = gpuDevices.begin(); |
| while (sensorIt != gpuDevices.end()) |
| { |
| if ((sensorIt->second->getPath() == removedPath) && |
| (std::find(interfaces.begin(), interfaces.end(), |
| configInterfaceName(sensorType)) != interfaces.end())) |
| { |
| sensorIt = gpuDevices.erase(sensorIt); |
| } |
| else |
| { |
| sensorIt++; |
| } |
| } |
| |
| auto smaSensorIt = smaDevices.begin(); |
| while (smaSensorIt != smaDevices.end()) |
| { |
| if ((smaSensorIt->second->getPath() == removedPath) && |
| (std::find(interfaces.begin(), interfaces.end(), |
| configInterfaceName(sensorType)) != interfaces.end())) |
| { |
| smaSensorIt = smaDevices.erase(smaSensorIt); |
| } |
| else |
| { |
| smaSensorIt++; |
| } |
| } |
| |
| auto pcieSensorIt = pcieDevices.begin(); |
| while (pcieSensorIt != pcieDevices.end()) |
| { |
| if ((pcieSensorIt->second->getPath() == removedPath) && |
| (std::find(interfaces.begin(), interfaces.end(), |
| configInterfaceName(sensorType)) != interfaces.end())) |
| { |
| pcieSensorIt = pcieDevices.erase(pcieSensorIt); |
| } |
| else |
| { |
| pcieSensorIt++; |
| } |
| } |
| } |