/* * SPDX-FileCopyrightText: Copyright OpenBMC Authors * SPDX-License-Identifier: Apache-2.0 */ #include "NvidiaGpuPowerSensor.hpp" #include "MctpRequester.hpp" #include "NvidiaSensorUtils.hpp" #include "SensorPaths.hpp" #include "Thresholds.hpp" #include "Utils.hpp" #include "sensor.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::literals; static constexpr double gpuPowerSensorMaxReading = 5000; static constexpr double gpuPowerSensorMinReading = std::numeric_limits::min(); NvidiaGpuPowerSensor::NvidiaGpuPowerSensor( std::shared_ptr& conn, mctp::MctpRequester& mctpRequester, const std::string& name, const std::string& sensorConfiguration, uint8_t eid, uint8_t sensorId, sdbusplus::asio::object_server& objectServer, std::vector&& thresholdData, const gpu::DeviceIdentification deviceType) : Sensor(escapeName(name), std::move(thresholdData), sensorConfiguration, "power", false, true, gpuPowerSensorMaxReading, gpuPowerSensorMinReading, conn), eid(eid), sensorId{sensorId}, mctpRequester(mctpRequester), objectServer(objectServer) { std::string dbusPath = sensorPathPrefix + "power/"s + escapeName(name); sensorInterface = objectServer.add_interface( dbusPath, "xyz.openbmc_project.Sensor.Value"); for (const auto& threshold : thresholds) { std::string interface = thresholds::getInterface(threshold.level); thresholdInterfaces[static_cast(threshold.level)] = objectServer.add_interface(dbusPath, interface); } association = objectServer.add_interface(dbusPath, association::interface); setInitialProperties(sensor_paths::unitWatts); const std::optional physicalContext = nvidia_sensor_utils::deviceTypeToPhysicalContext(deviceType); if (physicalContext) { commonPhysicalContextInterface = objectServer.add_interface( dbusPath, "xyz.openbmc_project.Common.PhysicalContext"); commonPhysicalContextInterface->register_property("Type", *physicalContext); if (!commonPhysicalContextInterface->initialize()) { lg2::error( "Error initializing PhysicalContext Interface for Power Sensor for eid {EID} and sensor id {SID}", "EID", eid, "SID", sensorId); } } } NvidiaGpuPowerSensor::~NvidiaGpuPowerSensor() { for (const auto& iface : thresholdInterfaces) { objectServer.remove_interface(iface); } objectServer.remove_interface(association); objectServer.remove_interface(sensorInterface); if (commonPhysicalContextInterface) { objectServer.remove_interface(commonPhysicalContextInterface); } } void NvidiaGpuPowerSensor::checkThresholds() { thresholds::checkThresholds(this); } void NvidiaGpuPowerSensor::processResponse(const std::error_code& ec, std::span buffer) { if (ec) { lg2::error( "Error updating Power Sensor for eid {EID} and sensor id {SID} : sending message over MCTP failed, rc={RC}", "EID", eid, "SID", sensorId, "RC", ec.message()); return; } ocp::accelerator_management::CompletionCode cc{}; uint16_t reasonCode = 0; uint32_t power = 0; const int rc = gpu::decodeGetPowerDrawResponse(buffer, cc, reasonCode, power); if (rc != 0 || cc != ocp::accelerator_management::CompletionCode::SUCCESS) { lg2::error( "Error updating Power Sensor eid {EID} and sensor id {SID} : decode failed, rc={RC}, cc={CC}, reasonCode={RESC}", "EID", eid, "SID", sensorId, "RC", rc, "CC", cc, "RESC", reasonCode); return; } // Reading from the device is in milliwatts and unit set on the dbus // is watts. updateValue(power / 1000.0); } void NvidiaGpuPowerSensor::update() { const int rc = gpu::encodeGetPowerDrawRequest( gpu::PlatformEnvironmentalCommands::GET_CURRENT_POWER_DRAW, 0, sensorId, averagingInterval, request); if (rc != 0) { lg2::error( "Error updating Temperature Sensor for eid {EID} and sensor id {SID} : encode failed, rc={RC}", "EID", eid, "SID", sensorId, "RC", rc); } mctpRequester.sendRecvMsg( eid, request, [weak{weak_from_this()}](const std::error_code& ec, std::span buffer) { std::shared_ptr self = weak.lock(); if (!self) { lg2::error("Invalid reference to NvidiaGpuPowerSensor"); return; } self->processResponse(ec, buffer); }); }