blob: b5544f3328c5ca0151588e905a1eec1db756c59e [file]
/*
* SPDX-FileCopyrightText: Copyright OpenBMC Authors
* SPDX-License-Identifier: Apache-2.0
*/
#include "NvidiaSmaDevice.hpp"
#include "NvidiaGpuTempSensor.hpp"
#include "NvidiaSensorConfig.hpp"
#include "NvidiaSmaLeakSensor.hpp"
#include "Thresholds.hpp"
#include "Utils.hpp"
#include <MctpRequester.hpp>
#include <NvidiaGpuMctpVdm.hpp>
#include <OcpMctpVdm.hpp>
#include <boost/asio/io_context.hpp>
#include <phosphor-logging/lg2.hpp>
#include <sdbusplus/asio/connection.hpp>
#include <sdbusplus/asio/object_server.hpp>
#include <sdbusplus/message/native_types.hpp>
#include <array>
#include <chrono>
#include <cstdint>
#include <format>
#include <memory>
#include <span>
#include <string>
#include <system_error>
#include <utility>
#include <vector>
SmaDevice::SmaDevice(const SensorConfigs& configs, const std::string& name,
const sdbusplus::object_path& path,
const std::shared_ptr<sdbusplus::asio::connection>& conn,
uint8_t eid, boost::asio::io_context& io,
mctp::MctpRequester& mctpRequester,
sdbusplus::asio::object_server& objectServer) :
eid(eid), sensorPollMs(std::chrono::milliseconds{configs.pollRate}),
waitTimer(io, std::chrono::steady_clock::duration(0)),
mctpRequester(mctpRequester), conn(conn), objectServer(objectServer),
configs(configs), name(escapeName(name)), path(path)
{}
void SmaDevice::init()
{
makeSensors();
}
void SmaDevice::makeSensors()
{
tempSensor = std::make_shared<NvidiaGpuTempSensor>(
conn, mctpRequester, name + "_TEMP_0", path, eid, smaTempSensorId,
objectServer, std::vector<thresholds::Threshold>{},
gpu::DeviceIdentification::DEVICE_SMA);
initLeakSensors();
lg2::info("Added MCA {NAME} Sensors with chassis path: {PATH}.", "NAME",
name, "PATH", path);
read();
}
void SmaDevice::read()
{
tempSensor->update();
for (auto& sensor : leakSensors)
{
sensor->update();
}
waitTimer.expires_after(std::chrono::milliseconds(sensorPollMs));
waitTimer.async_wait(
[weak{weak_from_this()}](const boost::system::error_code& ec) {
std::shared_ptr<SmaDevice> self = weak.lock();
if (!self)
{
lg2::error("Invalid SmaDevice reference");
return;
}
if (ec)
{
return;
}
self->read();
});
}
void SmaDevice::initLeakSensors()
{
auto leakReq = std::make_shared<
std::array<uint8_t, gpu::getLeakDetectionInfoRequestSize>>();
gpu::encodeGetLeakDetectionInfoRequest(0, *leakReq);
mctpRequester.sendRecvMsg(
eid, *leakReq,
[weak{weak_from_this()}, leakReq](const std::error_code& ec,
std::span<const uint8_t> response) {
auto self = weak.lock();
if (!self)
{
lg2::error("Invalid reference to SmaDevice");
return;
}
self->processLeakSensorsResponse(ec, response);
});
}
void SmaDevice::processLeakSensorsResponse(const std::error_code& ec,
std::span<const uint8_t> response)
{
if (ec)
{
lg2::error(
"Error creating Leak Sensor for {NAME}: sending message over MCTP failed, rc={RC}",
"NAME", name, "RC", ec.message());
return;
}
ocp::accelerator_management::CompletionCode cc{};
uint16_t reasonCode = 0;
std::vector<gpu::LeakSensorData> parsedSensors;
auto rc = gpu::decodeGetLeakDetectionInfoResponse(response, cc, reasonCode,
parsedSensors);
if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS)
{
lg2::error(
"Error creating Leak Sensor: decoding GetLeakDetectionInfo response for {NAME} failed, rc={RC}, cc={CC}, reasonCode={RESC}",
"NAME", name, "RC", rc, "CC", cc, "RESC", reasonCode);
return;
}
if (parsedSensors.empty())
{
lg2::error(
"Error creating Leak Sensor: decode success but no sensors returned for {NAME}",
"NAME", name);
return;
}
for (auto& parsedSensor : parsedSensors)
{
uint8_t sensorId = parsedSensor.sensorId;
std::string sensorName =
std::format("{}_LEAKDETECTOR_{}", name, sensorId);
// Use dynamic thresholds from hardware if available
std::vector<thresholds::Threshold> sensorThresholds;
if (parsedSensor.thresholds.size() >= 2)
{
// Index 0: Min leak threshold
sensorThresholds.emplace_back(thresholds::Level::CRITICAL,
thresholds::Direction::LOW,
parsedSensor.thresholds[0] / 1000.0);
// Index 1: Max leak threshold
sensorThresholds.emplace_back(thresholds::Level::WARNING,
thresholds::Direction::LOW,
parsedSensor.thresholds[1] / 1000.0);
}
if (parsedSensor.thresholds.size() >= 3)
{
// Index 2: Max normal threshold
sensorThresholds.emplace_back(thresholds::Level::CRITICAL,
thresholds::Direction::HIGH,
parsedSensor.thresholds[2] / 1000.0);
}
auto newSensor = std::make_shared<NvidiaSmaLeakSensor>(
conn, mctpRequester, sensorName, path.str, eid, sensorId,
objectServer, std::move(sensorThresholds),
gpu::DeviceIdentification::DEVICE_SMA);
newSensor->updateValue(parsedSensor.adcReadingMv / 1000.0);
leakSensors.emplace_back(std::move(newSensor));
}
}