blob: dfa8f0feacaa5859c582c1bed675a47c7c229755 [file]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaPcieInterface.hpp"
#include "Utils.hpp"
#include <bits/basic_string.h>
#include <MctpRequester.hpp>
#include <NvidiaGpuMctpVdm.hpp>
#include <NvidiaUtils.hpp>
#include <OcpMctpVdm.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <optional>
#include <span>
#include <string>
#include <system_error>
#include <vector>
using std::string;
using namespace std::literals;
NvidiaPcieInterface::NvidiaPcieInterface(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& path, uint8_t eid,
sdbusplus::asio::object_server& objectServer,
gpu::DeviceIdentification deviceType,
const std::optional<std::string>& networkAdapterName) :
eid(eid), path(path), conn(conn), mctpRequester(mctpRequester),
objectServer(objectServer), deviceType(deviceType)
{
int rc = 0;
switch (deviceType)
{
case gpu::DeviceIdentification::DEVICE_GPU:
request.resize(gpu::queryScalarGroupTelemetryV1RequestSize);
rc = gpu::encodeQueryScalarGroupTelemetryV1Request(
0, 0, gpu::PcieScalarGroupId::LinkSpeedWidth, request);
break;
case gpu::DeviceIdentification::DEVICE_PCIE:
request.resize(gpu::queryScalarGroupTelemetryV2RequestSize);
rc = gpu::encodeQueryScalarGroupTelemetryV2Request(
0, {}, 0, 0, gpu::PcieScalarGroupId::LinkSpeedWidth, request);
break;
default:
break;
}
if (rc != 0)
{
request.clear();
lg2::error(
"Failed to encode PCIe Interface request: rc={RC}, EID={EID}", "RC",
rc, "EID", eid);
}
const sdbusplus::object_path dbusPath = inventoryPrefix / escapeName(name);
pcieDeviceInterface = objectServer.add_interface(
dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeDevice");
std::vector<Association> associations;
associations.emplace_back("contained_by", "containing",
sdbusplus::object_path(path).parent_path());
switch (deviceType)
{
case gpu::DeviceIdentification::DEVICE_GPU:
pcieDeviceInterface->register_property(
"DeviceType",
std::string("xyz.openbmc_project.Inventory.Item.PCIeDevice."
"DeviceTypes.SingleFunction"));
break;
case gpu::DeviceIdentification::DEVICE_PCIE:
switchInterface = objectServer.add_interface(
dbusPath, "xyz.openbmc_project.Inventory.Item.PCIeSwitch");
break;
default:
break;
}
if (networkAdapterName.has_value())
{
const sdbusplus::object_path networkAdapterPath =
inventoryPrefix / (*networkAdapterName + "_NIC");
associations.emplace_back("connected_to", "connecting",
networkAdapterPath);
}
associationInterface =
objectServer.add_interface(dbusPath, association::interface);
associationInterface->register_property("Associations", associations);
pcieDeviceInterface->register_property(
"GenerationInUse",
std::string(
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
pcieDeviceInterface->register_property("LanesInUse",
std::numeric_limits<size_t>::max());
pcieDeviceInterface->register_property(
"GenerationSupported",
std::string(
"xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown"));
pcieDeviceInterface->register_property("MaxLanes", static_cast<size_t>(0));
if (!pcieDeviceInterface->initialize())
{
lg2::error("Error initializing PCIe Device interface, eid={EID}", "EID",
eid);
}
if (switchInterface && !switchInterface->initialize())
{
lg2::error("Error initializing Switch interface, eid={EID}", "EID",
eid);
}
if (associationInterface && !associationInterface->initialize())
{
lg2::error(
"Error initializing Association interface for PCIeSwitch, eid={EID}",
"EID", eid);
}
}
NvidiaPcieInterface::~NvidiaPcieInterface()
{
objectServer.remove_interface(pcieDeviceInterface);
objectServer.remove_interface(switchInterface);
objectServer.remove_interface(associationInterface);
}
string NvidiaPcieInterface::mapPcieGeneration(uint32_t value)
{
switch (value)
{
case 1:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen1";
case 2:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen2";
case 3:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen3";
case 4:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen4";
case 5:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen5";
case 6:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Gen6";
default:
return "xyz.openbmc_project.Inventory.Item.PCIeSlot.Generations.Unknown";
}
}
size_t NvidiaPcieInterface::decodeLinkWidth(uint32_t value)
{
return (value > 0) ? pow(2, value - 1) : 0;
}
void NvidiaPcieInterface::processResponse(const std::error_code& ec,
std::span<const uint8_t> response)
{
if (ec)
{
lg2::error(
"Error updating PCIe Interface: sending message over MCTP failed, "
"rc={RC}, EID={EID}",
"RC", ec.value(), "EID", eid);
return;
}
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
size_t numTelemetryValue = 0;
int rc = 0;
switch (deviceType)
{
case gpu::DeviceIdentification::DEVICE_GPU:
rc = gpu::decodeQueryScalarGroupTelemetryV1Response(
response, cc, reasonCode, numTelemetryValue, telemetryValues);
break;
case gpu::DeviceIdentification::DEVICE_PCIE:
rc = gpu::decodeQueryScalarGroupTelemetryV2Response(
response, cc, reasonCode, numTelemetryValue, telemetryValues);
break;
default:
return;
}
if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
{
lg2::error("Error updating PCIe Interface: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode,
"EID", eid);
return;
}
if (!telemetryValues.empty())
{
pcieDeviceInterface->set_property(
"GenerationInUse", mapPcieGeneration(telemetryValues[0]));
}
if (telemetryValues.size() > 1)
{
pcieDeviceInterface->set_property(
"LanesInUse",
decodeLinkWidth(static_cast<size_t>(telemetryValues[1])));
}
if (telemetryValues.size() > 3)
{
pcieDeviceInterface->set_property(
"GenerationSupported", mapPcieGeneration(telemetryValues[3]));
}
if (telemetryValues.size() > 4)
{
pcieDeviceInterface->set_property(
"MaxLanes",
decodeLinkWidth(static_cast<size_t>(telemetryValues[4])));
}
}
void NvidiaPcieInterface::update()
{
if (request.empty())
{
return;
}
mctpRequester.sendRecvMsg(
eid, request,
[eid{this->eid}, weak{weak_from_this()}](
const std::error_code& ec, std::span<const uint8_t> buffer) {
std::shared_ptr<NvidiaPcieInterface> self = weak.lock();
if (!self)
{
lg2::error(
"Invalid reference to NvidiaPcieInterface for EID {EID}",
"EID", eid);
return;
}
self->processResponse(ec, buffer);
});
}