blob: 06e2c09e9149e28cc7d0b3b7fd0d2cab35ec7eac [file] [edit]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaPciePort.hpp"
#include "Utils.hpp"
#include <bits/basic_string.h>
#include <MctpRequester.hpp>
#include <NvidiaGpuMctpVdm.hpp>
#include <NvidiaPcieInterface.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 <cstddef>
#include <cstdint>
#include <functional>
#include <limits>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <vector>
using std::string;
using namespace std::literals;
NvidiaPciePortInfo::NvidiaPciePortInfo(
std::shared_ptr<sdbusplus::asio::connection>& conn,
mctp::MctpRequester& mctpRequester, const std::string& name,
const std::string& pcieDeviceName, const std::string& path, uint8_t eid,
gpu::PciePortType portType, uint8_t upstreamPortNumber, uint8_t portNumber,
sdbusplus::asio::object_server& objectServer,
gpu::DeviceIdentification deviceType) :
eid(eid), portType(portType), upstreamPortNumber(upstreamPortNumber),
portNumber(portNumber), 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, portType, this->upstreamPortNumber, portNumber,
gpu::PcieScalarGroupId::LinkSpeedWidth, request);
break;
default:
break;
}
if (rc != 0)
{
request.clear();
lg2::error(
"Failed to encode PCIe Port Info request: rc={RC}, EID={EID}, PortType={PT}, PortNumber={PN}",
"RC", rc, "EID", eid, "PT", static_cast<uint8_t>(portType), "PN",
portNumber);
}
const sdbusplus::object_path dbusPath =
inventoryPrefix / pcieDeviceName / name;
pciePortInterface = objectServer.add_interface(
dbusPath, "xyz.openbmc_project.Inventory.Connector.Port");
std::string portTypeStr;
if (portType == gpu::PciePortType::UPSTREAM)
{
portTypeStr = "Upstream";
}
else
{
portTypeStr = "Downstream";
}
pciePortInterface->register_property(
"PortType",
"xyz.openbmc_project.Inventory.Connector.Port.PortType." + portTypeStr);
pciePortInterface->register_property(
"PortProtocol",
std::string(
"xyz.openbmc_project.Inventory.Connector.Port.PortProtocol.PCIe"));
pciePortInterface->register_property("Speed",
std::numeric_limits<uint64_t>::max());
pciePortInterface->register_property("Width",
std::numeric_limits<size_t>::max());
if (!pciePortInterface->initialize())
{
lg2::error(
"Error initializing PCIe Device interface, eid={EID}, portType={PT}, portNumber={PN}",
"EID", eid, "PT", static_cast<uint8_t>(portType), "PN", portNumber);
}
std::vector<Association> associations;
associations.emplace_back("connected_to", "connecting",
inventoryPrefix / pcieDeviceName);
associationInterface =
objectServer.add_interface(dbusPath, association::interface);
associationInterface->register_property("Associations", associations);
if (!associationInterface->initialize())
{
lg2::error(
"Error initializing Association interface for PCIe Port Info, eid={EID}, portType={PT}, portNumber={PN}",
"EID", eid, "PT", static_cast<uint8_t>(portType), "PN", portNumber);
}
}
NvidiaPciePortInfo::~NvidiaPciePortInfo()
{
objectServer.remove_interface(pciePortInterface);
objectServer.remove_interface(associationInterface);
}
uint64_t NvidiaPciePortInfo::mapPcieGenToLinkSpeedBitsPerSecond(uint32_t value)
{
static constexpr uint64_t gbpsToBps = 1000000000;
switch (value)
{
case 1:
return 2.5 * gbpsToBps;
case 2:
return 5.0 * gbpsToBps;
case 3:
return 8.0 * gbpsToBps;
case 4:
return 16.0 * gbpsToBps;
case 5:
return 32.0 * gbpsToBps;
case 6:
return 64.0 * gbpsToBps;
default:
return 0;
}
}
void NvidiaPciePortInfo::processResponse(
const std::error_code& sendRecvMsgResult, std::span<const uint8_t> response)
{
if (sendRecvMsgResult)
{
lg2::error(
"Error updating PCIe Port Info: sending message over MCTP failed, "
"rc={RC}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}",
"RC", sendRecvMsgResult.message(), "EID", eid, "PT",
static_cast<uint8_t>(portType), "UP", upstreamPortNumber, "PN",
portNumber);
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 Port Info: decode failed, "
"rc={RC}, cc={CC}, reasonCode={RESC}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}",
"RC", rc, "CC", static_cast<uint8_t>(cc), "RESC", reasonCode, "EID",
eid, "PT", static_cast<uint8_t>(portType), "UP", upstreamPortNumber,
"PN", portNumber);
return;
}
if (telemetryValues.size() < 2)
{
lg2::error(
"Error updating PCIe Port Info: insufficient telemetry values, "
"NumValues={NUM}, EID={EID}, PortType={PT}, UpstreamPortNumber={UP}, PortNumber={PN}",
"NUM", telemetryValues.size(), "EID", eid, "PT",
static_cast<uint8_t>(portType), "UP", upstreamPortNumber, "PN",
portNumber);
return;
}
pciePortInterface->set_property(
"Speed", mapPcieGenToLinkSpeedBitsPerSecond(telemetryValues[0]));
pciePortInterface->set_property(
"Width", NvidiaPcieInterface::decodeLinkWidth(telemetryValues[1]));
}
void NvidiaPciePortInfo::update()
{
if (request.empty())
{
return;
}
mctpRequester.sendRecvMsg(
eid, request,
[weak{weak_from_this()}](const std::error_code& ec,
std::span<const uint8_t> buffer) {
std::shared_ptr<NvidiaPciePortInfo> self = weak.lock();
if (!self)
{
lg2::error("Invalid reference to NvidiaPciePortInfo");
return;
}
self->processResponse(ec, buffer);
});
}