From d7477b4b7b3324cf5b9d95052738cecb64232eef Mon Sep 17 00:00:00 2001 From: Sheldon Bailey Date: Tue, 11 Nov 2025 20:49:56 +0000 Subject: [PATCH 1/2] Move OCC poll processing to occ_control This is so we can make changes to the OCC poll response without kernel updates. Signed-off-by: Sheldon Bailey --- meson.build | 2 + meson.options | 7 + occ_manager.cpp | 567 +------------------ occ_manager.hpp | 103 +--- occ_poll_handler.cpp | 1259 ++++++++++++++++++++++++++++++++++++++++++ occ_poll_handler.hpp | 315 +++++++++++ occ_status.cpp | 217 ++++---- occ_status.hpp | 22 +- powercap.cpp | 66 +-- powercap.hpp | 3 +- 10 files changed, 1725 insertions(+), 836 deletions(-) create mode 100644 occ_poll_handler.cpp create mode 100644 occ_poll_handler.hpp diff --git a/meson.build b/meson.build index 4feb8f5..f7c15cc 100644 --- a/meson.build +++ b/meson.build @@ -39,6 +39,7 @@ conf_data.set('PS_DERATING_FACTOR', get_option('ps-derating-factor')) conf_data.set_quoted('OCC_HWMON_PATH', '/sys/bus/platform/drivers/occ-hwmon/') conf_data.set_quoted('DEV_PATH', '/sys/bus/platform/devices/') +conf_data.set('ENABLE_APP_POLL_SUPPORT', get_option('app-poll-support').allowed()) conf_data.set('PHAL_SUPPORT', get_option('phal-support').allowed()) if get_option('transport-implementation') == 'mctp-demux' @@ -120,6 +121,7 @@ sources += [ 'powermode.cpp', 'pldm.cpp', 'utils.cpp', + 'occ_poll_handler.cpp', ] if get_option('phal-support').allowed() diff --git a/meson.options b/meson.options index 4d75390..a7c0ae4 100644 --- a/meson.options +++ b/meson.options @@ -45,3 +45,10 @@ option( value: 'disabled', ) +option( + 'app-poll-support', + type: 'feature', + description: 'Enable direct POLL support', + value: 'disabled', +) + diff --git a/occ_manager.cpp b/occ_manager.cpp index ed6453c..6b0cd82 100644 --- a/occ_manager.cpp +++ b/occ_manager.cpp @@ -22,12 +22,6 @@ namespace open_power namespace occ { -constexpr uint32_t fruTypeNotAvailable = 0xFF; -constexpr auto fruTypeSuffix = "fru_type"; -constexpr auto faultSuffix = "fault"; -constexpr auto inputSuffix = "input"; -constexpr auto maxSuffix = "max"; - const auto HOST_ON_FILE = "/run/openbmc/host@0-on"; const std::string Manager::dumpFile = "/tmp/occ_control_dump.json"; @@ -35,28 +29,6 @@ using namespace phosphor::logging; using namespace std::literals::chrono_literals; using json = nlohmann::json; -template -T readFile(const std::string& path) -{ - std::ifstream ifs; - ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit | - std::ifstream::eofbit); - T data; - - try - { - ifs.open(path); - ifs >> data; - ifs.close(); - } - catch (const std::exception& e) - { - auto err = errno; - throw std::system_error(err, std::generic_category()); - } - - return data; -} void Manager::createPldmHandle() { @@ -544,7 +516,12 @@ void Manager::statusCallBack(instanceID instance, bool status) "COUNT", activeCount, "INST", instance, "STATUS", status); } // Clear OCC sensors - setSensorValueToNaN(instance); + for (auto& obj : statusObjects) + { + // int instance = obj->getOccInstanceID(); + obj->setSensorValueToNaN(); + } + } if (waitingForAllOccActiveSensors) @@ -840,22 +817,17 @@ void Manager::pollerTimerExpired() } return; } - for (auto& obj : statusObjects) { if (!obj->occActive()) { // OCC is not running yet - auto id = obj->getOccInstanceID(); - setSensorValueToNaN(id); + obj->setSensorValueToNaN(); continue; } - // Read sysfs to force kernel to poll OCC - obj->readOccState(); - - // Read occ sensor values - getSensorValues(obj); + // get OCC Poll Response and handle actions needed. + obj->PollHandler(); } if (activeCount > 0) @@ -871,522 +843,6 @@ void Manager::pollerTimerExpired() } } -void Manager::readTempSensors(const fs::path& path, uint32_t occInstance) -{ - // There may be more than one sensor with the same FRU type - // and label so make two passes: the first to read the temps - // from sysfs, and the second to put them on D-Bus after - // resolving any conflicts. - std::map sensorData; - - std::regex expr{"temp\\d+_label$"}; // Example: temp5_label - for (auto& file : fs::directory_iterator(path)) - { - if (!std::regex_search(file.path().string(), expr)) - { - continue; - } - - uint32_t labelValue{0}; - - try - { - labelValue = readFile(file.path()); - } - catch (const std::system_error& e) - { - lg2::debug( - "readTempSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", file.path().string(), "ERROR", e.code().value()); - continue; - } - - const std::string& tempLabel = "label"; - const std::string filePathString = file.path().string().substr( - 0, file.path().string().length() - tempLabel.length()); - - uint32_t fruTypeValue{0}; - try - { - fruTypeValue = readFile(filePathString + fruTypeSuffix); - } - catch (const std::system_error& e) - { - lg2::debug( - "readTempSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + fruTypeSuffix, "ERROR", - e.code().value()); - continue; - } - - std::string sensorPath = - OCC_SENSORS_ROOT + std::string("/temperature/"); - - std::string dvfsTempPath; - - if (fruTypeValue == VRMVdd) - { - sensorPath.append( - "vrm_vdd" + std::to_string(occInstance) + "_temp"); - } - else if (fruTypeValue == processorIoRing) - { - sensorPath.append( - "proc" + std::to_string(occInstance) + "_ioring_temp"); - dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/proc" + - std::to_string(occInstance) + "_ioring_dvfs_temp"; - } - else - { - uint16_t type = (labelValue & 0xFF000000) >> 24; - uint16_t instanceID = labelValue & 0x0000FFFF; - - if (type == OCC_DIMM_TEMP_SENSOR_TYPE) - { - if (fruTypeValue == fruTypeNotAvailable) - { - // Not all DIMM related temps are available to read - // (no _input file in this case) - continue; - } - auto iter = dimmTempSensorName.find(fruTypeValue); - if (iter == dimmTempSensorName.end()) - { - lg2::error( - "readTempSensors: Fru type error! fruTypeValue = {FRU}) ", - "FRU", fruTypeValue); - continue; - } - - sensorPath.append( - "dimm" + std::to_string(instanceID) + iter->second); - - dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/" + - dimmDVFSSensorName.at(fruTypeValue); - } - else if (type == OCC_CPU_TEMP_SENSOR_TYPE) - { - if (fruTypeValue == processorCore) - { - // The OCC reports small core temps, of which there are - // two per big core. All current P10 systems are in big - // core mode, so use a big core name. - uint16_t coreNum = instanceID / 2; - uint16_t tempNum = instanceID % 2; - sensorPath.append("proc" + std::to_string(occInstance) + - "_core" + std::to_string(coreNum) + "_" + - std::to_string(tempNum) + "_temp"); - - dvfsTempPath = - std::string{OCC_SENSORS_ROOT} + "/temperature/proc" + - std::to_string(occInstance) + "_core_dvfs_temp"; - } - else - { - continue; - } - } - else - { - continue; - } - } - - // The dvfs temp file only needs to be read once per chip per type. - if (!dvfsTempPath.empty() && - !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath)) - { - try - { - auto dvfsValue = readFile(filePathString + maxSuffix); - - dbus::OccDBusSensors::getOccDBus().setDvfsTemp( - dvfsTempPath, dvfsValue * std::pow(10, -3)); - } - catch (const std::system_error& e) - { - lg2::debug( - "readTempSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + maxSuffix, "ERROR", - e.code().value()); - } - } - - uint32_t faultValue{0}; - try - { - faultValue = readFile(filePathString + faultSuffix); - } - catch (const std::system_error& e) - { - lg2::debug( - "readTempSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + faultSuffix, "ERROR", - e.code().value()); - continue; - } - - double tempValue{0}; - // NOTE: if OCC sends back 0xFF, kernal sets this fault value to 1. - if (faultValue != 0) - { - tempValue = std::numeric_limits::quiet_NaN(); - } - else - { - // Read the temperature - try - { - tempValue = readFile(filePathString + inputSuffix); - } - catch (const std::system_error& e) - { - lg2::debug( - "readTempSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + inputSuffix, "ERROR", - e.code().value()); - - // if errno == EAGAIN(Resource temporarily unavailable) then set - // temp to 0, to avoid using old temp, and affecting FAN - // Control. - if (e.code().value() == EAGAIN) - { - tempValue = 0; - } - // else the errno would be something like - // EBADF(Bad file descriptor) - // or ENOENT(No such file or directory) - else - { - continue; - } - } - } - - // If this object path already has a value, only overwite - // it if the previous one was an NaN or a smaller value. - auto existing = sensorData.find(sensorPath); - if (existing != sensorData.end()) - { - // Multiple sensors found for this FRU type - if ((std::isnan(existing->second) && (tempValue == 0)) || - ((existing->second == 0) && std::isnan(tempValue))) - { - // One of the redundant sensors has failed (0xFF/nan), and the - // other sensor has no reading (0), so set the FRU to NaN to - // force fan increase - tempValue = std::numeric_limits::quiet_NaN(); - existing->second = tempValue; - } - if (std::isnan(existing->second) || (tempValue > existing->second)) - { - existing->second = tempValue; - } - } - else - { - // First sensor for this FRU type - sensorData[sensorPath] = tempValue; - } - } - - // Now publish the values on D-Bus. - for (const auto& [objectPath, value] : sensorData) - { - dbus::OccDBusSensors::getOccDBus().setValue(objectPath, - value * std::pow(10, -3)); - - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - objectPath, !std::isnan(value)); - - if (existingSensors.find(objectPath) == existingSensors.end()) - { - dbus::OccDBusSensors::getOccDBus().setChassisAssociation( - objectPath, {"all_sensors"}); - } - existingSensors[objectPath] = occInstance; - } -} - -std::optional Manager::getPowerLabelFunctionID( - const std::string& value) -{ - // If the value is "system", then the FunctionID is "system". - if (value == "system") - { - return value; - } - - // If the value is not "system", then the label value have 3 numbers, of - // which we only care about the middle one: - // __ - // eg: The value is "0_10_5" , then the FunctionID is "10". - if (value.find("_") == std::string::npos) - { - return std::nullopt; - } - - auto powerLabelValue = value.substr((value.find("_") + 1)); - - if (powerLabelValue.find("_") == std::string::npos) - { - return std::nullopt; - } - - return powerLabelValue.substr(0, powerLabelValue.find("_")); -} - -void Manager::readPowerSensors(const fs::path& path, uint32_t id) -{ - std::regex expr{"power\\d+_label$"}; // Example: power5_label - for (auto& file : fs::directory_iterator(path)) - { - if (!std::regex_search(file.path().string(), expr)) - { - continue; - } - - std::string labelValue; - try - { - labelValue = readFile(file.path()); - } - catch (const std::system_error& e) - { - lg2::debug( - "readPowerSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", file.path().string(), "ERROR", e.code().value()); - continue; - } - - auto functionID = getPowerLabelFunctionID(labelValue); - if (functionID == std::nullopt) - { - continue; - } - - const std::string& tempLabel = "label"; - const std::string filePathString = file.path().string().substr( - 0, file.path().string().length() - tempLabel.length()); - - std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); - - auto iter = powerSensorName.find(*functionID); - if (iter == powerSensorName.end()) - { - continue; - } - sensorPath.append(iter->second); - - double tempValue{0}; - - try - { - tempValue = readFile(filePathString + inputSuffix); - } - catch (const std::system_error& e) - { - lg2::debug( - "readPowerSensors: Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + inputSuffix, "ERROR", - e.code().value()); - continue; - } - - dbus::OccDBusSensors::getOccDBus().setUnit( - sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); - - dbus::OccDBusSensors::getOccDBus().setValue( - sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3)); - - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, true); - - if (existingSensors.find(sensorPath) == existingSensors.end()) - { - std::vector fTypeList = {"all_sensors"}; - if (iter->second == "total_power") - { - // Set sensor purpose as TotalPower - dbus::OccDBusSensors::getOccDBus().setPurpose( - sensorPath, - "xyz.openbmc_project.Sensor.Purpose.SensorPurpose.TotalPower"); - } - dbus::OccDBusSensors::getOccDBus().setChassisAssociation( - sensorPath, fTypeList); - } - existingSensors[sensorPath] = id; - } - return; -} - -void Manager::readExtnSensors(const fs::path& path, uint32_t id) -{ - std::regex expr{"extn\\d+_label$"}; // Example: extn5_label - for (auto& file : fs::directory_iterator(path)) - { - if (!std::regex_search(file.path().string(), expr)) - { - continue; - } - - // Read in Label value of the sensor from file. - std::string labelValue; - try - { - labelValue = readFile(file.path()); - } - catch (const std::system_error& e) - { - lg2::debug( - "readExtnSensors:label Failed reading {PATH}, errno = {ERROR}", - "PATH", file.path().string(), "ERROR", e.code().value()); - continue; - } - const std::string& tempLabel = "label"; - const std::string filePathString = file.path().string().substr( - 0, file.path().string().length() - tempLabel.length()); - - std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); - - // Labels of EXTN sections from OCC interface Document - // have different formats. - // 0x464d494e : FMIN 0x46444953 : FDIS - // 0x46424153 : FBAS 0x46555400 : FUT - // 0x464d4158 : FMAX 0x434c4950 : CLIP - // 0x4d4f4445 : MODE 0x574f4643 : WOFC - // 0x574f4649 : WOFI 0x5057524d : PWRM - // 0x50575250 : PWRP 0x45525248 : ERRH - // Label indicating byte 5 and 6 is the current (mem,proc) power in - // Watts. - if ((labelValue == EXTN_LABEL_PWRM_MEMORY_POWER) || - (labelValue == EXTN_LABEL_PWRP_PROCESSOR_POWER)) - { - // Build the dbus String for this chiplet power asset. - if (labelValue == EXTN_LABEL_PWRP_PROCESSOR_POWER) - { - labelValue = "_power"; - } - else // else EXTN_LABEL_PWRM_MEMORY_POWER - { - labelValue = "_mem_power"; - } - sensorPath.append("chiplet" + std::to_string(id) + labelValue); - - // Read in data value of the sensor from file. - // Read in as string due to different format of data in sensors. - std::string extnValue; - try - { - extnValue = readFile(filePathString + inputSuffix); - } - catch (const std::system_error& e) - { - lg2::debug( - "readExtnSensors:value Failed reading {PATH}, errno = {ERROR}", - "PATH", filePathString + inputSuffix, "ERROR", - e.code().value()); - continue; - } - - // For Power field, Convert last 4 bytes of hex string into number - // value. - std::stringstream ssData; - ssData << std::hex << extnValue.substr(extnValue.length() - 4); - uint16_t MyHexNumber; - ssData >> MyHexNumber; - - // Convert output/DC power to input/AC power in Watts (round up) - MyHexNumber = - std::round(((MyHexNumber / (PS_DERATING_FACTOR / 100.0)))); - - dbus::OccDBusSensors::getOccDBus().setUnit( - sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); - - dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, - MyHexNumber); - - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, true); - - if (existingSensors.find(sensorPath) == existingSensors.end()) - { - dbus::OccDBusSensors::getOccDBus().setChassisAssociation( - sensorPath, {"all_sensors"}); - } - - existingSensors[sensorPath] = id; - } // End Extended Power Sensors. - } // End For loop on files for Extended Sensors. - return; -} - -void Manager::setSensorValueToNaN(uint32_t id) const -{ - for (const auto& [sensorPath, occId] : existingSensors) - { - if (occId == id) - { - dbus::OccDBusSensors::getOccDBus().setValue( - sensorPath, std::numeric_limits::quiet_NaN()); - - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, true); - } - } - return; -} - -void Manager::setSensorValueToNonFunctional(uint32_t id) const -{ - for (const auto& [sensorPath, occId] : existingSensors) - { - if (occId == id) - { - dbus::OccDBusSensors::getOccDBus().setValue( - sensorPath, std::numeric_limits::quiet_NaN()); - - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, false); - } - } - return; -} - -void Manager::getSensorValues(std::unique_ptr& occ) -{ - static bool tracedError[8] = {0}; - const fs::path sensorPath = occ->getHwmonPath(); - const uint32_t id = occ->getOccInstanceID(); - - if (fs::exists(sensorPath)) - { - // Read temperature sensors - readTempSensors(sensorPath, id); - // Read Extended sensors - readExtnSensors(sensorPath, id); - - if (occ->isMasterOcc()) - { - // Read power sensors - readPowerSensors(sensorPath, id); - } - tracedError[id] = false; - } - else - { - if (!tracedError[id]) - { - lg2::error( - "Manager::getSensorValues: OCC{INST} sensor path missing: {PATH}", - "INST", id, "PATH", sensorPath); - tracedError[id] = true; - } - } - - return; -} - // Read the altitude from DBus void Manager::readAltitude() { @@ -1698,11 +1154,12 @@ void Manager::validateOccMaster() } } -void Manager::updatePcapBounds() const +void Manager::updatePcapBounds(bool& parmsChanged,uint32_t& capSoftMin, + uint32_t& capHardMin, uint32_t& capMax) const { if (pcap) { - pcap->updatePcapBounds(); + pcap->updatePcapBounds(parmsChanged, capSoftMin, capHardMin, capMax); } } diff --git a/occ_manager.hpp b/occ_manager.hpp index 3758952..05fa219 100644 --- a/occ_manager.hpp +++ b/occ_manager.hpp @@ -27,18 +27,6 @@ namespace open_power namespace occ { -enum occFruType -{ - processorCore = 0, - internalMemCtlr = 1, - dimm = 2, - memCtrlAndDimm = 3, - VRMVdd = 6, - PMIC = 7, - memCtlrExSensor = 8, - processorIoRing = 9 -}; - /** @brief Default time, in seconds, between OCC poll commands */ constexpr unsigned int defaultPollingInterval = 5; @@ -50,9 +38,6 @@ constexpr auto ALTITUDE_PATH = "/xyz/openbmc_project/sensors/altitude/Altitude"; constexpr auto ALTITUDE_INTERFACE = "xyz.openbmc_project.Sensor.Value"; constexpr auto ALTITUDE_PROP = "Value"; -constexpr auto EXTN_LABEL_PWRM_MEMORY_POWER = "5057524d"; -constexpr auto EXTN_LABEL_PWRP_PROCESSOR_POWER = "50575250"; - /** @class Manager * @brief Builds and manages OCC objects */ @@ -128,19 +113,8 @@ struct Manager uint16_t& altitude) const; /** @brief Notify pcap object to update bounds */ - void updatePcapBounds() const; - - /** - * @brief Set all sensor values of this OCC to NaN. - * @param[in] id - Id of the OCC. - * */ - void setSensorValueToNaN(uint32_t id) const; - - /** @brief Set all sensor values of this OCC to NaN and non functional. - * - * @param[in] id - Id of the OCC. - */ - void setSensorValueToNonFunctional(uint32_t id) const; + void updatePcapBounds(bool& parmsChanged,uint32_t& capSoftMin, + uint32_t& capHardMin, uint32_t& capMax) const; /** @brief Clear any state flags that need to be reset when the host state * is off */ @@ -385,79 +359,6 @@ struct Manager */ std::vector findOCCsInDev(); - /** - * @brief Gets the occ sensor values. - * @param[in] occ - pointer to OCCs Status object - * */ - void getSensorValues(std::unique_ptr& occ); - - /** - * @brief Trigger OCC driver to read the temperature sensors. - * @param[in] path - path of the OCC sensors. - * @param[in] id - Id of the OCC. - * */ - void readTempSensors(const fs::path& path, uint32_t id); - - /** - * @brief Trigger OCC driver to read the extended sensors. - * @param[in] path - path of the OCC sensors. - * @param[in] id - Id of the OCC. - * */ - void readExtnSensors(const fs::path& path, uint32_t id); - - /** - * @brief Trigger OCC driver to read the power sensors. - * @param[in] path - path of the OCC sensors. - * @param[in] id - Id of the OCC. - * */ - void readPowerSensors(const fs::path& path, uint32_t id); - - /** @brief Store the existing OCC sensors on D-BUS */ - std::map existingSensors; - - /** @brief Get FunctionID from the `powerX_label` file. - * @param[in] value - the value of the `powerX_label` file. - * @returns FunctionID of the power sensors. - */ - std::optional getPowerLabelFunctionID( - const std::string& value); - - /** @brief The power sensor names map */ - const std::map powerSensorName = { - {"system", "total_power"}, {"1", "p0_mem_power"}, - {"2", "p1_mem_power"}, {"3", "p2_mem_power"}, - {"4", "p3_mem_power"}, {"5", "p0_power"}, - {"6", "p1_power"}, {"7", "p2_power"}, - {"8", "p3_power"}, {"9", "p0_cache_power"}, - {"10", "p1_cache_power"}, {"11", "p2_cache_power"}, - {"12", "p3_cache_power"}, {"13", "io_a_power"}, - {"14", "io_b_power"}, {"15", "io_c_power"}, - {"16", "fans_a_power"}, {"17", "fans_b_power"}, - {"18", "storage_a_power"}, {"19", "storage_b_power"}, - {"23", "mem_cache_power"}, {"25", "p0_mem_0_power"}, - {"26", "p0_mem_1_power"}, {"27", "p0_mem_2_power"}, - {"35", "pcie_dcm0_power"}, {"36", "pcie_dcm1_power"}, - {"37", "pcie_dcm2_power"}, {"38", "pcie_dcm3_power"}, - {"39", "io_dcm0_power"}, {"40", "io_dcm1_power"}, - {"41", "io_dcm2_power"}, {"42", "io_dcm3_power"}, - {"43", "avdd_total_power"}}; - - /** @brief The dimm temperature sensor names map */ - const std::map dimmTempSensorName = { - {internalMemCtlr, "_intmb_temp"}, - {dimm, "_dram_temp"}, - {memCtrlAndDimm, "_dram_extmb_temp"}, - {PMIC, "_pmic_temp"}, - {memCtlrExSensor, "_extmb_temp"}}; - - /** @brief The dimm DVFS temperature sensor names map */ - const std::map dimmDVFSSensorName = { - {internalMemCtlr, "dimm_intmb_dvfs_temp"}, - {dimm, "dimm_dram_dvfs_temp"}, - {memCtrlAndDimm, "dimm_dram_extmb_dvfs_temp"}, - {PMIC, "dimm_pmic_dvfs_temp"}, - {memCtlrExSensor, "dimm_extmb_dvfs_temp"}}; - /** @brief Read the altitude from DBus */ void readAltitude(); diff --git a/occ_poll_handler.cpp b/occ_poll_handler.cpp new file mode 100644 index 0000000..84e266b --- /dev/null +++ b/occ_poll_handler.cpp @@ -0,0 +1,1259 @@ + + +#include "occ_poll_handler.hpp" +#include "occ_dbus.hpp" +#include "occ_command.hpp" +#include "occ_status.hpp" + +#include +#include + + +namespace open_power +{ +namespace occ +{ + +using namespace phosphor::logging; +using namespace std::literals::chrono_literals; + +constexpr uint32_t fruTypeNotAvailable = 0xFF; +constexpr auto fruTypeSuffix = "fru_type"; +constexpr auto faultSuffix = "fault"; +constexpr auto inputSuffix = "input"; +constexpr auto maxSuffix = "max"; + + +template +T readFile(const std::string& path) +{ + std::ifstream ifs; + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit | + std::ifstream::eofbit); + T data; + + try + { + ifs.open(path); + ifs >> data; + ifs.close(); + } + catch (const std::exception& e) + { + auto err = errno; + throw std::system_error(err, std::generic_category()); + } + + return data; +} + + +//############################################################################## +// COMMON Functions. +//############################################################################## +OccPollHandler::OccPollHandler(Status& status) : + statusObject(status), + occInstanceID(status.getOccInstanceID()) +{ + +} + +std::optional OccPollHandler::getPowerLabelFunctionID( + const std::string& value) +{ + // If the value is "system", then the FunctionID is "system". + if (value == "system") + { + return value; + } + + // If the value is not "system", then the label value have 3 numbers, of + // which we only care about the middle one: + // __ + // eg: The value is "0_10_5" , then the FunctionID is "10". + if (value.find("_") == std::string::npos) + { + return std::nullopt; + } + + auto powerLabelValue = value.substr((value.find("_") + 1)); + + if (powerLabelValue.find("_") == std::string::npos) + { + return std::nullopt; + } + + return powerLabelValue.substr(0, powerLabelValue.find("_")); +}//end getPowerLabelFunctionID + +bool OccPollHandler::BuildTempDbusPaths(std::string& sensorPath, + std::string& dvfsTempPath, + const uint32_t SensorID, + const uint32_t fruTypeValue, + const uint32_t occInstance) +{ + bool returnSensorFound = true; + sensorPath = OCC_SENSORS_ROOT + std::string("/temperature/"); + + if (fruTypeValue == VRMVdd) + { + sensorPath.append( + "vrm_vdd" + std::to_string(occInstance) + "_temp"); + } + else if (fruTypeValue == processorIoRing) + { + sensorPath.append( + "proc" + std::to_string(occInstance) + "_ioring_temp"); + dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/proc" + + std::to_string(occInstance) + "_ioring_dvfs_temp"; + } + else + { + uint16_t type = (SensorID & 0xFF000000) >> 24; + uint16_t instanceID = SensorID & 0x0000FFFF; + + if (type == OCC_DIMM_TEMP_SENSOR_TYPE) + { + if (fruTypeValue == fruTypeNotAvailable) + { + // Not all DIMM related temps are available to read + returnSensorFound = false; + } + else + { + auto iter = dimmTempSensorName.find(fruTypeValue); + if (iter == dimmTempSensorName.end()) + { + returnSensorFound = false; + } + else + { + sensorPath.append( + "dimm" + std::to_string(instanceID) + iter->second); + dvfsTempPath = std::string{OCC_SENSORS_ROOT} + "/temperature/" + + dimmDVFSSensorName.at(fruTypeValue); + } + } + } + else if (type == OCC_CPU_TEMP_SENSOR_TYPE) + { + if (fruTypeValue == processorCore) + { + // The OCC reports small core temps, of which there are + // two per big core. All current P10 systems are in big + // core mode, so use a big core name. + uint16_t coreNum = instanceID / 2; + uint16_t tempNum = instanceID % 2; + sensorPath.append("proc" + std::to_string(occInstance) + + "_core" + std::to_string(coreNum) + "_" + + std::to_string(tempNum) + "_temp"); + dvfsTempPath = + std::string{OCC_SENSORS_ROOT} + "/temperature/proc" + + std::to_string(occInstance) + "_core_dvfs_temp"; + } + else + { + returnSensorFound = false; + } + } + else + { + returnSensorFound = false; + } + } + + return returnSensorFound; +}//end BuildTempDbusPaths + +//############################################################################## +// OCC-CONTROL POLL Functions. +//############################################################################## +#ifdef ENABLE_APP_POLL_SUPPORT + + +void OccPollHandler::sendOccPollCmd() +{ + // New Poll data, clear out storage vector. + if (!PollRspData.empty()) + { + PollRspData.clear(); + } + + OccCommand occCmd(occInstanceID, (fs::path(OCC_CONTROL_ROOT) / + (std::string(OCC_NAME) + std::to_string(occInstanceID))) + .c_str()); + + std::vector cmd = {0x00, 0x00, 0x01, 0x20}; + + + occCmd.send(cmd, PollRspData); + +}//end OCCC sendOccPollCmd + +bool OccPollHandler::pollReadStateStatus(unsigned int& state, int& lastOccReadStatus) +{ + bool stateWasRead = true; + + lastOccReadStatus = 0; + state = PollRspStatus; + + return stateWasRead; + +}//end OCCC pollReadStateStatus + +void OccPollHandler::HandlePollAction() +{ + sendOccPollCmd(); + + if (PollRspData.size() >= OCC_RSP_HDR_LENGTH) + { + uint16_t index = 0; + const uint8_t sizeSensorLabel = SensorLabel[0].size(); + + // Add offset to jump over Data we do not need. + index += 5; + + // Data format OCC_RSP_HDR_LENGTH 32 bytes + //---------------------- + // ExtStatus: 2 byte + // OccsPresent: 1 byte + // ConfigNeed: 1 byte + // State: 1 byte <-Index + 4 + PollRspStatus = PollRspData[index+4]; + // Mode: 1 byte + // IPS: 1 byte + //---------------------- + // ElogID: 1 byte + // ElogAddr: 4 byte + // Elog Len: 2 byte + // Elog Source: 1 byte + // GPU config: 1 byte + // Code Level: 16 byte + //---------------------- + index += 32; + //---------------------- + // Sensor Tag: 6 byte + // Sensor Blocks:1 byte + // Sensor Vers: 1 byte + //---------------------- + // Total Header is 40 bytes. -> OCC_RSP_HDR_LENGTH + + std::vector dataLabel = {0x53, 0x45, 0x4E, 0x53, 0x4F, 0x52}; // SENSOR TAG + if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+6, dataLabel.begin())) + { + index += 6; //Add offset of SENSOR TAG + + uint8_t numBlocks = PollRspData[index++]; // SENSOR Blocks + uint8_t pollVersion = PollRspData[index++]; // SENSOR Vers + + if (pollVersion == POLL_VERSION_FORMAT_1) + { + for (uint16_t i = 0; i < numBlocks; i++) + { + if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[TEMP].begin())) + { + index += sizeSensorLabel; + PushTempSensorsToDbus(index); + } + else if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[FREQ].begin())) + { + index += sizeSensorLabel; + PushFreqSensorsToDbus(index); + } + else if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[POWR].begin())) + { + index += sizeSensorLabel; + PushPowrSensorsToDbus(index); + } + else if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[CAPS].begin())) + { + index += sizeSensorLabel; + PushCapsSensorsToDbus(index); + } + else if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[EXTN].begin())) + { + index += sizeSensorLabel; + PushExtnSensorsToDbus(index); + } + else if (std::equal(PollRspData.begin()+index, PollRspData.begin()+index+sizeSensorLabel, SensorLabel[EXTT].begin())) + { + index += sizeSensorLabel; + PushExttSensorsToDbus(index); + } + } + } + else + { + if (TraceOncePollHeader) + { + lg2::error("OccPollHandler::HandlePollAction: unsuported poll format:{BYTE}", + "BYTE", lg2::hex, pollVersion); + TraceOncePollHeader = false; + } + } + } + else + { + if (TraceOncePollHeader) + { + lg2::error("OccPollHandler::HandlePollAction: unsuported header:"); + dump_hex(dataLabel); + TraceOncePollHeader = false; + } + } + } +}//end OCCC HandlePollAction + +void OccPollHandler::PushTempSensorsToDbus(uint16_t& index ) +{ + // Temp Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + if (SensorFormat == TEMP_SENSOR_FORMAT_16) + { + for (uint16_t i = 0; i < NumberSensors; i++) + { + // Temp Sensor Record Data format for POLL + // SensorID: 4 byte + // fruTypeValue: 1 byte + // tempValue: 1 byte + //---------------------- + + uint32_t SensorID = (static_cast(PollRspData[index]) << 24) | + (static_cast(PollRspData[index + 1]) << 16) | + (static_cast(PollRspData[index + 2]) << 8) | + (static_cast(PollRspData[index + 3])); + + auto tempValue = PollRspData[index+5]; + + uint32_t fruTypeValue = PollRspData[index+4]; + + std::string sensorPath = ""; + std::string dvfsTempPath = ""; + // if Dbus sensor found , and good FruType and value not 0, then continue + if ((BuildTempDbusPaths(sensorPath, dvfsTempPath, SensorID, fruTypeValue, occInstanceID))) + { + + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, tempValue ); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, !std::isnan(tempValue)); + if (!dvfsTempPath.empty() && !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath)) + { + auto dvfsValue = PollRspData[index+5]; + dbus::OccDBusSensors::getOccDBus().setDvfsTemp(dvfsTempPath, dvfsValue ); + } + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + + } + index += bytesPerSensor; + } + } + else + { + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushTempSensorsToDbus: unsuported format:{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + + index += bytesPerSensor * NumberSensors; + } + + +}//end OCCC PushTempSensorsToDbus + +void OccPollHandler::PushFreqSensorsToDbus(uint16_t& index ) +{ + // Freq Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + if (SensorFormat == FREQ_SENSOR_FORMAT) + { + for (uint16_t i = 0; i < NumberSensors; i++) + { + // Freq Sensor Record Data format for POLL + // SensorID: 4 byte + // Frequency: 2 byte + //---------------------- + + index += bytesPerSensor; + } + } + else + { + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushFreqSensorsToDbus: unsuported format :{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + index += bytesPerSensor * NumberSensors; + } +}//end OCCC PushFreqSensorsToDbus + +void OccPollHandler::PushPowrSensorsToDbus(uint16_t& index ) +{ + // Powr Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + if (SensorFormat == POWR_SENSOR_FORMAT) + { + for (uint16_t i = 0; i < NumberSensors; i++) + { + // Powr Sensor Record Data format for POLL + // SensorID: 4 byte + // functionalID: 1 byte + // ApsChannel: 2 byte + // UpdateTag: 4 byte + // Accumulator: 8 byte + // SensorCurrent: 2 byte + //---------------------- + + uint8_t functionalID = (static_cast(PollRspData[index + 4])); + uint16_t SensorCurrent = (static_cast(PollRspData[index + 20]) << 8) | + (static_cast(PollRspData[index + 21])); + + std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); + + std::stringstream ss; + ss << static_cast(functionalID); + std::string functionID = ss.str(); + + auto iter = powerSensorName.find(functionID); + if (iter != powerSensorName.end()) + { + sensorPath.append(iter->second); + + dbus::OccDBusSensors::getOccDBus().setUnit(sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, SensorCurrent); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, true); + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + } + else + { + if ((functionalID != 0) && (TraceOncePwrFuncId)) + { + lg2::error("OccPollHandler::PushPowrSensorsToDbus: functionID({FXID}) unsuported power sensor name", "FXID", functionID); + TraceOncePwrFuncId = false; + } + } + index += bytesPerSensor; + } + } + else + { + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushPowrSensorsToDbus: unsuported format:{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + + index += bytesPerSensor * NumberSensors; + } + +}//end OCCC PushPowrSensorsToDbus + +void OccPollHandler::PushCapsSensorsToDbus(uint16_t& index ) +{ + // Caps Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + if (SensorFormat == CAPS_SENSOR_FORMAT) + { + for (uint16_t i = 0; i < NumberSensors; i++) + { + // Caps Sensor Record Data format for POLL + // SensorCap: 2 byte + // PollRspMaxPower: 2 byte + // NCap: 2 byte + // MaxCap: 2 byte + // HardMinCap: 2 byte + // SoftMinCap: 2 byte + // UserCap: 2 byte + // UserSourse: 1 byte + //---------------------- + + PollRspMaxPower = (static_cast(PollRspData[index + 2]) << 8) | + (static_cast(PollRspData[index + 3])); + + std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); + sensorPath.append("total_power"); + + dbus::OccDBusSensors::getOccDBus().setUnit(sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, PollRspMaxPower); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, true); + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + dbus::OccDBusSensors::getOccDBus().setPurpose(sensorPath,"xyz.openbmc_project.Sensor.Purpose.SensorPurpose.TotalPower"); + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + + + PollRspHardMin = (static_cast(PollRspData[index + 8]) << 8) | + (static_cast(PollRspData[index + 9])); + + PollRspSoftMin = (static_cast(PollRspData[index + 10]) << 8) | + (static_cast(PollRspData[index + 11])); + + index += bytesPerSensor; + } + } + else + { + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushCapsSensorFormat: unsuported format :{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + + index += bytesPerSensor * NumberSensors; + } + + + +}//end OCCC PushCapsSensorsToDbus + +void OccPollHandler::PushExtnSensorsToDbus(uint16_t& index ) +{ + // Extn Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + if (SensorFormat == EXTN_SENSOR_FORMAT) + { + for (uint16_t i = 0; i < NumberSensors; i++) + { + // Extn Sensor Record Data format for POLL + // SensorID: 4 byte + // Flags: 1 byte + // Reserved: 1 byte + // Value: 6 byte + // PWRM and PWRP last 2 bytes. + //---------------------- + + const uint32_t SensorName = (static_cast(PollRspData[index]) << 24) | + (static_cast(PollRspData[index + 1]) << 16) | + (static_cast(PollRspData[index + 2]) << 8) | + (static_cast(PollRspData[index + 3])); + + bool push_power_to_dbus = false; + std::string sensorPath = OCC_SENSORS_ROOT; + uint32_t SensorValue = 0; + + switch (SensorName) + { + case EXTN_LABEL_FMIN: break; + case EXTN_LABEL_FDIS: break; + case EXTN_LABEL_FBAS: break; + case EXTN_LABEL_FUT: break; + case EXTN_LABEL_FMAX: break; + case EXTN_LABEL_CLIP: break; + case EXTN_LABEL_MODE: break; + case EXTN_LABEL_WOFC: break; + case EXTN_LABEL_WOFI: break; + case EXTN_LABEL_PWRM: + push_power_to_dbus = true; + sensorPath.append("/power/chiplet" + std::to_string(occInstanceID) + "_mem_power"); + break; + case EXTN_LABEL_PWRP: + push_power_to_dbus = true; + sensorPath.append("/power/chiplet" + std::to_string(occInstanceID) + "_power"); + break; + case EXTN_LABEL_ERRH: break; + default: + lg2::error("OccPollHandler::PushExtnSensorsToDbus: EXTN label name FAILED:{BYTE}", + "BYTE", lg2::hex, SensorName); + break; + } + + if (push_power_to_dbus == true) + { + // For Power field, Convert last 4 bytes into number value + SensorValue = (static_cast(PollRspData[index + 10]) << 8) | + (static_cast(PollRspData[index + 11])); + + // Convert output/DC power to input/AC power in Watts (round up) + uint16_t extnSensorValue = + std::round(((SensorValue / (PS_DERATING_FACTOR / 100.0)))); + + dbus::OccDBusSensors::getOccDBus().setUnit(sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, extnSensorValue ); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, !std::isnan(extnSensorValue)); + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + } + index += bytesPerSensor; + } + + } + else + { + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushExtnSensorsToDbus: unsuported format:{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + + index += bytesPerSensor * NumberSensors; + + } + + +}//end OCCC PushExtnSensorsToDbus + +void OccPollHandler::PushExttSensorsToDbus(uint16_t& index ) +{ + // Extn Sensor Header Data format for POLL + // SensorFormat: 1 byte + // bytesPerSensor: 1 byte + // NumberSensors: 1 byte + //---------------------- + + uint8_t SensorFormat = PollRspData[index++]; + uint8_t bytesPerSensor = PollRspData[index++]; + uint8_t NumberSensors = PollRspData[index++]; + + + if (TraceOncePollSensor) + { + lg2::error("OccPollHandler::PushExttSensorsToDbus: unsuported format:{BYTE}", + "BYTE", lg2::hex, SensorFormat); + TraceOncePollSensor = false; + } + + //Temp to jump over section until format data put in. + index += bytesPerSensor * NumberSensors; + + // Extt PROPOSED Sensor Record Data format for POLL + // SensorID: 4 byte + // FruType: 1 byte + // Value: 1 byte + //---------------------- + +}//end OCCC PushExttSensorsToDbus + +bool OccPollHandler::pollReadPcapBounds(uint32_t& capSoftMin, uint32_t& capHardMin, uint32_t& capMax) +{ + bool parmsChanged = true; + + HandlePollAction(); + + capSoftMin = PollRspSoftMin; + capHardMin = PollRspHardMin; + capMax = PollRspMaxPower; + + return parmsChanged; +} //end OCCC pollReadPcapBounds + + +#else +//############################################################################## +// KERNEL POLL Functions. +//############################################################################## +bool OccPollHandler::pollReadStateStatus(unsigned int& state, int& lastOccReadStatus) +{ + bool stateWasRead = false; + + const fs::path filename = + fs::path(DEV_PATH) / + fs::path(sysfsName + "." + std::to_string(occInstanceID + 1)) / "occ_state"; + + std::ifstream file; + + // open file. + file.open(filename, std::ios::in); + const int openErrno = errno; + + // File is open and state can be used. + if (file.is_open() && file.good()) + { + stateWasRead = true; + file >> state; + // Read the error code (if any) to check status of the read + std::ios_base::iostate readState = file.rdstate(); + if (readState) + { + // There was a failure reading the file + if (lastOccReadStatus != -1) + { + // Trace error bits + std::string errorBits = ""; + if (readState & std::ios_base::eofbit) + { + errorBits += " EOF"; + } + if (readState & std::ios_base::failbit) + { + errorBits += " failbit"; + } + if (readState & std::ios_base::badbit) + { + errorBits += " badbit"; + } + lg2::error( + "readOccState: Failed to read OCC{INST} state: Read error on I/O operation - {ERROR}", + "INST", occInstanceID, "ERROR", errorBits); + lastOccReadStatus = -1; + } + stateWasRead = false; + } + + // If not able to read, OCC may be offline + if ((!stateWasRead) && (openErrno != lastOccReadStatus)) + { + lg2::error( + "OccPollHandler::pollReadStateStatus: open/read failed trying to read OCC{INST} state (open errno={ERROR})", + "INST", occInstanceID, "ERROR", openErrno); + lastOccReadStatus = openErrno; + } + + } + + file.close(); + + + return stateWasRead; +}//end Kernel pollReadStateStatus + +void OccPollHandler::HandlePollAction() +{ + static bool tracedError[8] = {0}; + const fs::path sensorPath = statusObject.getHwmonPath(); + + + if (fs::exists(sensorPath)) + { + // Read temperature sensors and push to dbus + pushTempSensorsToDbus(sensorPath, occInstanceID); + + // Read Extended sensors and push to dbus + pushExtnSensorsToDbus(sensorPath, occInstanceID); + + if (statusObject.isMasterOcc()) + { + // Read power sensors and push to dbus + pushPowrSensorsToDbus(sensorPath, occInstanceID); + + } + tracedError[occInstanceID] = false; + + } + else + { + if (!tracedError[occInstanceID]) + { + lg2::error( + "OccPollHandler::getSensorValues: OCC{INST} sensor path missing: {PATH}", + "INST", occInstanceID, "PATH", sensorPath); + tracedError[occInstanceID] = true; + } + } + + return; +}//end Kernel HandlePollAction + +bool OccPollHandler::pollReadPcapBounds(uint32_t& capSoftMin, uint32_t& capHardMin, uint32_t& capMax) +{ + + // Build the hwmon string to write the power cap bounds + fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"}); + fs::path softMinName = + getPcapFilename(std::regex{"power\\d+_cap_min_soft$"}); + fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"}); + + // Read the power cap bounds from sysfs files (from OCC) + uint64_t cap; + bool parmsChanged = false; + std::ifstream softMinFile(softMinName, std::ios::in); + if (softMinFile) + { + softMinFile >> cap; + softMinFile.close(); + // Convert to input/AC Power in Watts (round up) + capSoftMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9); + parmsChanged = true; + } + else + { + lg2::error( + "updatePcapBounds: unable to find pcap_min_soft file: {FILE} (errno={ERR})", + "FILE", pcapBasePathname, "ERR", errno); + } + + std::ifstream minFile(minName, std::ios::in); + if (minFile) + { + minFile >> cap; + minFile.close(); + // Convert to input/AC Power in Watts (round up) + capHardMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9); + parmsChanged = true; + } + else + { + lg2::error( + "updatePcapBounds: unable to find cap_min file: {FILE} (errno={ERR})", + "FILE", pcapBasePathname, "ERR", errno); + } + + std::ifstream maxFile(maxName, std::ios::in); + if (maxFile) + { + maxFile >> cap; + maxFile.close(); + // Convert to input/AC Power in Watts (truncate remainder) + capMax = cap / (PS_DERATING_FACTOR / 100.0) / 1000000; + parmsChanged = true; + } + else + { + lg2::error( + "updatePcapBounds: unable to find cap_max file: {FILE} (errno={ERR})", + "FILE", pcapBasePathname, "ERR", errno); + } + + return parmsChanged; +} //end Kernel pollReadPcapBounds + +fs::path OccPollHandler::getPcapFilename(const std::regex& expr) +{ + if (pcapBasePathname.empty()) + { + pcapBasePathname = statusObject.getHwmonPath(); + } + + if (fs::exists(pcapBasePathname)) + { + // Search for pcap file based on the supplied expr + for (auto& file : fs::directory_iterator(pcapBasePathname)) + { + if (std::regex_search(file.path().string(), expr)) + { + // Found match + return file; + } + } + } + else + { + lg2::error("Power Cap base filename not found: {FILE}", "FILE", + pcapBasePathname); + } + + // return empty path + return fs::path{}; +} //end kernel getPcapFilename + +void OccPollHandler::pushTempSensorsToDbus(const fs::path& path, uint32_t occInstance) +{ + // There may be more than one sensor with the same FRU type + // and label so make two passes: the first to read the temps + // from sysfs, and the second to put them on D-Bus after + // resolving any conflicts. + std::map sensorData; + + std::regex expr{"temp\\d+_label$"}; // Example: temp5_label + for (auto& file : fs::directory_iterator(path)) + { + if (!std::regex_search(file.path().string(), expr)) + { + continue; + } + + uint32_t labelValue{0}; + + try + { + labelValue = readFile(file.path()); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushTempSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", file.path().string(), "ERROR", e.code().value()); + continue; + } + + const std::string& tempLabel = "label"; + const std::string filePathString = file.path().string().substr( + 0, file.path().string().length() - tempLabel.length()); + + uint32_t fruTypeValue{0}; + try + { + fruTypeValue = readFile(filePathString + fruTypeSuffix); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushTempSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + fruTypeSuffix, "ERROR", + e.code().value()); + continue; + } + + + std::string sensorPath = ""; + std::string dvfsTempPath = ""; + // if no Dbus sensor found then continue + if ( !BuildTempDbusPaths(sensorPath, dvfsTempPath, labelValue, fruTypeValue, occInstance)) + { + continue; + } + + // The dvfs temp file only needs to be read once per chip per type. + if (!dvfsTempPath.empty() && + !dbus::OccDBusSensors::getOccDBus().hasDvfsTemp(dvfsTempPath)) + { + try + { + auto dvfsValue = readFile(filePathString + maxSuffix); + dbus::OccDBusSensors::getOccDBus().setDvfsTemp(dvfsTempPath, dvfsValue * std::pow(10, -3)); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushTempSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + maxSuffix, "ERROR", + e.code().value()); + } + } + + uint32_t faultValue{0}; + try + { + faultValue = readFile(filePathString + faultSuffix); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushTempSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + faultSuffix, "ERROR", + e.code().value()); + continue; + } + + double tempValue{0}; + // NOTE: if OCC sends back 0xFF, kernal sets this fault value to 1. + if (faultValue != 0) + { + tempValue = std::numeric_limits::quiet_NaN(); + } + else + { + // Read the temperature + try + { + tempValue = readFile(filePathString + inputSuffix); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushTempSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + inputSuffix, "ERROR", + e.code().value()); + + // if errno == EAGAIN(Resource temporarily unavailable) then set + // temp to 0, to avoid using old temp, and affecting FAN + // Control. + if (e.code().value() == EAGAIN) + { + tempValue = 0; + } + // else the errno would be something like + // EBADF(Bad file descriptor) + // or ENOENT(No such file or directory) + else + { + continue; + } + } + } + + // If this object path already has a value, only overwite + // it if the previous one was an NaN or a smaller value. + auto existing = sensorData.find(sensorPath); + if (existing != sensorData.end()) + { + // Multiple sensors found for this FRU type + if ((std::isnan(existing->second) && (tempValue == 0)) || + ((existing->second == 0) && std::isnan(tempValue))) + { + // One of the redundant sensors has failed (0xFF/nan), and the + // other sensor has no reading (0), so set the FRU to NaN to + // force fan increase + tempValue = std::numeric_limits::quiet_NaN(); + existing->second = tempValue; + } + if (std::isnan(existing->second) || (tempValue > existing->second)) + { + existing->second = tempValue; + } + } + else + { + // First sensor for this FRU type + sensorData[sensorPath] = tempValue; + } + } + + // Now publish the values on D-Bus. + for (const auto& [objectPath, value] : sensorData) + { + + dbus::OccDBusSensors::getOccDBus().setValue(objectPath, value * std::pow(10, -3)); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(objectPath, !std::isnan(value)); + if (statusObject.existingSensors.find(objectPath) == statusObject.existingSensors.end()) + { + try + { + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + objectPath, {"all_sensors"}); + } + catch(const std::exception& e) + { + lg2::error("OccPollHandler::pushTempSensorsToDbus:setChassisAssociation FAILED OCC:{INST} watch:{ERROR} PATH:{PATH}", + "INST", occInstanceID, "ERROR", e.what(), "PATH", objectPath ); + } + } + statusObject.existingSensors[objectPath] = occInstance; + + } + + return; +} //end kernel pushTempSensorsToDbus + +void OccPollHandler::pushExtnSensorsToDbus(const fs::path& path, uint32_t occInstanceID) +{ + + std::regex expr{"extn\\d+_label$"}; // Example: extn5_label + for (auto& file : fs::directory_iterator(path)) + { + if (!std::regex_search(file.path().string(), expr)) + { + continue; + } + + // Read in Label value of the sensor from file. + std::string labelValue; + uint32_t SensorName = 0; + try + { + labelValue = readFile(file.path()); + + std::stringstream ssData; + ssData << std::hex << labelValue.substr(labelValue.length() - 8); + ssData >> SensorName; + } + catch (const std::system_error& e) + { + lg2::debug( + "pushExtnSensorsToDbus:label Failed reading {PATH}, errno = {ERROR}", + "PATH", file.path().string(), "ERROR", e.code().value()); + continue; + } + const std::string& tempLabel = "label"; + const std::string filePathString = file.path().string().substr( + 0, file.path().string().length() - tempLabel.length()); + + std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); + + // Labels of EXTN sections from OCC interface Document + // have different formats. + if ((SensorName == EXTN_LABEL_PWRM) || + (SensorName == EXTN_LABEL_PWRP)) + { + // Label indicating byte 5 and 6 is the current (mem,proc) power in + // Watts. + + // Build the dbus String for this chiplet power asset. + if (SensorName == EXTN_LABEL_PWRP) + { + labelValue = "_power"; + } + else // else EXTN_LABEL_PWRM_MEMORY_POWER + { + labelValue = "_mem_power"; + } + sensorPath.append("chiplet" + std::to_string(occInstanceID) + labelValue); + + // Read in data value of the sensor from file. + // Read in as string due to different format of data in sensors. + std::string extnValue; + try + { + extnValue = readFile(filePathString + inputSuffix); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushExtnSensorsToDbus:value Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + inputSuffix, "ERROR", + e.code().value()); + continue; + } + + // For Power field, Convert last 4 bytes of hex string into number + // value. + std::stringstream ssData; + ssData << std::hex << extnValue.substr(extnValue.length() - 4); + uint16_t extnSensorValue; + ssData >> extnSensorValue; + + // Convert output/DC power to input/AC power in Watts (round up) + extnSensorValue = + std::round(((extnSensorValue / (PS_DERATING_FACTOR / 100.0)))); + + dbus::OccDBusSensors::getOccDBus().setUnit(sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, extnSensorValue); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, true); + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + + } // End Extended Power Sensors. + } // End For loop on files for Extended Sensors. + + return; +} //end kernel pushExtnSensorsToDbus + +void OccPollHandler::pushPowrSensorsToDbus(const fs::path& path, uint32_t occInstanceID) +{ + + std::regex expr{"power\\d+_label$"}; // Example: power5_label + for (auto& file : fs::directory_iterator(path)) + { + if (!std::regex_search(file.path().string(), expr)) + { + continue; + } + + std::string labelValue; + try + { + labelValue = readFile(file.path()); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushPowrSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", file.path().string(), "ERROR", e.code().value()); + continue; + } + + auto functionID = getPowerLabelFunctionID(labelValue); + if (functionID == std::nullopt) + { + continue; + } + + const std::string& tempLabel = "label"; + const std::string filePathString = file.path().string().substr( + 0, file.path().string().length() - tempLabel.length()); + + std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); + + auto iter = powerSensorName.find(*functionID); + if (iter == powerSensorName.end()) + { + continue; + } + sensorPath.append(iter->second); + + double tempValue{0}; + + try + { + tempValue = readFile(filePathString + inputSuffix); + } + catch (const std::system_error& e) + { + lg2::debug( + "pushPowrSensorsToDbus: Failed reading {PATH}, errno = {ERROR}", + "PATH", filePathString + inputSuffix, "ERROR", + e.code().value()); + continue; + } + + dbus::OccDBusSensors::getOccDBus().setUnit(sensorPath, "xyz.openbmc_project.Sensor.Value.Unit.Watts"); + dbus::OccDBusSensors::getOccDBus().setValue(sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3)); + dbus::OccDBusSensors::getOccDBus().setOperationalStatus(sensorPath, true); + if (statusObject.existingSensors.find(sensorPath) == statusObject.existingSensors.end()) + { + if (iter->second == "total_power") + { + dbus::OccDBusSensors::getOccDBus().setPurpose(sensorPath,"xyz.openbmc_project.Sensor.Purpose.SensorPurpose.TotalPower"); + } + dbus::OccDBusSensors::getOccDBus().setChassisAssociation( + sensorPath, {"all_sensors"}); + } + statusObject.existingSensors[sensorPath] = occInstanceID; + + } + + return; +} //end kernel pushPowrSensorsToDbus + +#endif +//############################################################################## +// END +//############################################################################## + + +} // namespace occ +} // namespace open_power diff --git a/occ_poll_handler.hpp b/occ_poll_handler.hpp new file mode 100644 index 0000000..ccc27ad --- /dev/null +++ b/occ_poll_handler.hpp @@ -0,0 +1,315 @@ +#pragma once + +#include "config.h" + +#include +#include +#include +#include + +#include "occ_errors.hpp" +#include "utils.hpp" + + +namespace open_power +{ +namespace occ +{ + +using namespace std::literals::chrono_literals; + +class Status; + +constexpr auto POLL_VERSION_FORMAT_1 = 1; +constexpr auto TEMP_SENSOR_FORMAT_16 = 16; +constexpr auto FREQ_SENSOR_FORMAT = 2; +constexpr auto POWR_SENSOR_FORMAT = 2; +constexpr auto CAPS_SENSOR_FORMAT = 3; +constexpr auto EXTN_SENSOR_FORMAT = 1; +constexpr auto EXTT_SENSOR_FORMAT = 10; + +constexpr auto EXTN_LABEL_FMIN = 0x464D494E; +constexpr auto EXTN_LABEL_FDIS = 0x46444953; +constexpr auto EXTN_LABEL_FBAS = 0x46424153; +constexpr auto EXTN_LABEL_FUT = 0x46555400; +constexpr auto EXTN_LABEL_FMAX = 0x464D4158; +constexpr auto EXTN_LABEL_CLIP = 0x434C4950; +constexpr auto EXTN_LABEL_MODE = 0x4D4F4445; +constexpr auto EXTN_LABEL_WOFC = 0x574F4643; +constexpr auto EXTN_LABEL_WOFI = 0x574F4649; +constexpr auto EXTN_LABEL_PWRM = 0x5057524d; +constexpr auto EXTN_LABEL_PWRP = 0x50575250; +constexpr auto EXTN_LABEL_ERRH = 0x45525248; + + +/** @class OccPollHandler + * @brief Implements POLLing OCCs + */ +class OccPollHandler +{ + public: + OccPollHandler() = delete; + OccPollHandler(const OccPollHandler&) = delete; + OccPollHandler& operator=(const OccPollHandler&) = delete; + OccPollHandler(OccPollHandler&&) = default; + OccPollHandler& operator=(OccPollHandler&&) = default; + + OccPollHandler(Status& status); + + /** Store the associated Status instance */ + Status& statusObject; + + const uint32_t occInstanceID; + + +//############################################################################## +// COMMON PUBLIC INTERFACES +//############################################################################## + /** @brief Done every 5 Sec.: From OCC Poll push data needed to the dbus. + * + */ + void HandlePollAction(); + + /** + * @brief Done when OCC goes active: From OCC Poll data returns OCC state + * @param[out] state - Returned State of OCC + * @param[out] lastOccReadStatus - Returned (-1) if invalid (0) if valid) + * + * @returns true if data gathering was success + * */ + bool pollReadStateStatus(unsigned int& state, int& lastOccReadStatus); + + /** @brief Done One time: From OCC Poll data returns Pcap Information. + * @param[out] capSoftMin - Returned soft Minimum cap + * @param[out] capHardMin - Returned hard Minimum cap + * @param[out] capMax. - Returned max cap + * + * @returns true if data gathering was success + */ + bool pollReadPcapBounds(uint32_t& capSoftMin, uint32_t& capHardMin, uint32_t& capMax); + + + /** @brief clear OCC flags to allow trace of POLL parsing errors. + * + */ + void clearOccPollTraceFlags() + { + TraceOncePollHeader = false; + TraceOncePollSensor = false; + TraceOncePwrFuncId = false; + } + + + private: + +//############################################################################## +// OCC-CONTROL Private POLL Functions. +//############################################################################## +#ifdef ENABLE_APP_POLL_SUPPORT + + // Length of POLL Header Section. + const uint16_t OCC_RSP_HDR_LENGTH = 40; + + // Object variable to store Response data. + std::vector PollRspData; + + // Data stored for quick return of information after a POLL and Handling the POLL. + uint16_t PollRspStatus = 0; + + uint16_t PollRspSoftMin = 0; + uint16_t PollRspHardMin = 0; + uint16_t PollRspMaxPower = 0; + + /** @brief Create CMD to Poll OCC and send. Store POLL response data for use. + * + */ + void sendOccPollCmd(); + + /** @brief ???? + * + */ + void pollOccNow(); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushTempSensorsToDbus(uint16_t& index ); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushFreqSensorsToDbus(uint16_t& index ); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushPowrSensorsToDbus(uint16_t& index ); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushCapsSensorsToDbus(uint16_t& index ); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushExtnSensorsToDbus(uint16_t& index ); + + /** + * @brief . + * @param[in] index - index into PollRspData. + * */ + void PushExttSensorsToDbus(uint16_t& index ); + + // /** @brief The POLL sensor labels map */ + enum + { + TEMP = 0, + FREQ, + POWR, + CAPS, + EXTN, + EXTT, + sizeSensorLabelList + }; + const std::vector SensorLabel[sizeSensorLabelList] = { + {0x54, 0x45, 0x4D, 0x50, 0x00}, // TEMP + {0x46, 0x52, 0x45, 0x51, 0x00}, // FREQ + {0x50, 0x4F, 0x57, 0x52, 0x00}, // POWR + {0x43, 0x41, 0x50, 0x53, 0x00}, // CAPS + {0x45, 0x58, 0x54, 0x4E, 0x00}, // EXTN + {0x45, 0x58, 0x54, 0x54, 0x00}, // EXTT + }; + +#else +//############################################################################## +// KERNEL Private POLL Functions. +//############################################################################## + /** + * @brief Trigger OCC driver to read the temperature sensors and push to dbus. + * @param[in] path - path of the OCC sensors. + * @param[in] id - Id of the OCC. + * */ + void pushTempSensorsToDbus(const fs::path& path, uint32_t occInstance); + + /** + * @brief Trigger OCC driver to read the extended sensors and push to dbus. + * @param[in] path - path of the OCC sensors. + * @param[in] id - Id of the OCC. + * */ + void pushExtnSensorsToDbus(const fs::path& path, uint32_t id); + + /** + * @brief Trigger OCC driver to read the power sensors and push to dbus. + * @param[in] path - path of the OCC sensors. + * @param[in] id - Id of the OCC. + * */ + void pushPowrSensorsToDbus(const fs::path& path, uint32_t id); + /** + * @brief Returns the filename to use for the user power cap + * + * The file is of the form "powerX_cap_user", where X is any + * number. + * + * @param[in] expr - Regular expression of file to find + * + * @return full path/filename, or empty path if not found. + */ + fs::path getPcapFilename(const std::regex& expr); + + /** @brief Path to the sysfs files holding the cap properties **/ + fs::path pcapBasePathname; + + +#endif + + +//############################################################################## +// COMMON Private. +//############################################################################## + + // Flags to prevent flooding traces every 5 Sec. + bool TraceOncePollHeader = true; + bool TraceOncePollSensor = true; + bool TraceOncePwrFuncId = true; + + enum occFruType + { + processorCore = 0, + internalMemCtlr = 1, + dimm = 2, + memCtrlAndDimm = 3, + VRMVdd = 6, + PMIC = 7, + memCtlrExSensor = 8, + processorIoRing = 9 + }; + + + /** @brief Get FunctionID from the `powerX_label` file. + * @param[in] value - the value of the `powerX_label` file. + * @returns FunctionID of the power sensors. + */ + std::optional getPowerLabelFunctionID(const std::string& value); + + + /** @brief Get ???? + * @param[in] ???? + * @returns bool ???? + */ + bool BuildTempDbusPaths(std::string& sensorPath, + std::string& dvfsTempPath, + const uint32_t SensorID, + const uint32_t fruTypeValue, + const uint32_t occInstance); + + /** @brief The dimm temperature sensor names map used by */ + const std::map dimmTempSensorName = { + {internalMemCtlr, "_intmb_temp"}, + {dimm, "_dram_temp"}, + {memCtrlAndDimm, "_dram_extmb_temp"}, + {PMIC, "_pmic_temp"}, + {memCtlrExSensor, "_extmb_temp"}}; + + /** @brief The dimm DVFS temperature sensor names map */ + const std::map dimmDVFSSensorName = { + {internalMemCtlr, "dimm_intmb_dvfs_temp"}, + {dimm, "dimm_dram_dvfs_temp"}, + {memCtrlAndDimm, "dimm_dram_extmb_dvfs_temp"}, + {PMIC, "dimm_pmic_dvfs_temp"}, + {memCtlrExSensor, "dimm_extmb_dvfs_temp"}}; + + /** @brief The power sensor names map */ + const std::map powerSensorName = { + {"system", "total_power"}, {"1", "p0_mem_power"}, + {"2", "p1_mem_power"}, {"3", "p2_mem_power"}, + {"4", "p3_mem_power"}, {"5", "p0_power"}, + {"6", "p1_power"}, {"7", "p2_power"}, + {"8", "p3_power"}, {"9", "p0_cache_power"}, + {"10", "p1_cache_power"}, {"11", "p2_cache_power"}, + {"12", "p3_cache_power"}, {"13", "io_a_power"}, + {"14", "io_b_power"}, {"15", "io_c_power"}, + {"16", "fans_a_power"}, {"17", "fans_b_power"}, + {"18", "storage_a_power"}, {"19", "storage_b_power"}, + {"23", "mem_cache_power"}, {"25", "p0_mem_0_power"}, + {"26", "p0_mem_1_power"}, {"27", "p0_mem_2_power"}, + {"34", "pcie_power"}, + {"35", "pcie_dcm0_power"}, {"36", "pcie_dcm1_power"}, + {"37", "pcie_dcm2_power"}, {"38", "pcie_dcm3_power"}, + {"39", "io_dcm0_power"}, {"40", "io_dcm1_power"}, + {"41", "io_dcm2_power"}, {"42", "io_dcm3_power"}, + {"43", "avdd_total_power"}}; + +//############################################################################## +// END +//############################################################################## + +}; // + +} // namespace occ +} // namespace open_power \ No newline at end of file diff --git a/occ_status.cpp b/occ_status.cpp index 4be7c93..0af64d1 100644 --- a/occ_status.cpp +++ b/occ_status.cpp @@ -1,5 +1,6 @@ #include "occ_status.hpp" +#include "occ_dbus.hpp" #include "occ_manager.hpp" #include "occ_sensor.hpp" #include "powermode.hpp" @@ -60,7 +61,10 @@ bool Status::occActive(bool value) if (device.master()) { // Update powercap bounds from OCC - manager.updatePcapBounds(); + uint32_t capSoftMin = 0, capHardMin = 0, capMax = 0; + bool parmsChanged = occPollHandler.pollReadPcapBounds(capSoftMin, capHardMin, capMax); + + manager.updatePcapBounds(parmsChanged, capSoftMin, capHardMin, capMax); } // Call into Manager to let know that we have bound @@ -76,7 +80,7 @@ bool Status::occActive(bool value) { sensorsValid = false; // Sensors not supported (update to NaN and not functional) - manager.setSensorValueToNaN(instance); + setSensorValueToNaN(); } if (pmode && device.master()) @@ -104,6 +108,9 @@ bool Status::occActive(bool value) // Clear throttles (OCC not active after disabling device) updateThrottle(false, THROTTLED_ALL); + + // CALL into poll_handler to clear any trace poll flags. + occPollHandler.clearOccPollTraceFlags(); } } else if (value && !device.active()) @@ -214,14 +221,18 @@ void Status::hostControlEvent(sdbusplus::message_t& msg) } // Called from Manager::pollerTimerExpired() in preperation to POLL OCC. -void Status::readOccState() +void Status::PollHandler() { if (stateValid) { // Reset retry count (since state is good) currentOccReadRetriesCount = occReadRetries; } + occReadStateNow(); + + occPollHandler.HandlePollAction(); + } // Special processing that needs to happen once the OCCs change to ACTIVE state @@ -391,144 +402,86 @@ fs::path Status::getHwmonPath() void Status::occReadStateNow() { unsigned int state; - const fs::path filename = - fs::path(DEV_PATH) / - fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state"; - - std::ifstream file; bool stateWasRead = false; - // open file. - file.open(filename, std::ios::in); - const int openErrno = errno; + stateWasRead = occPollHandler.pollReadStateStatus(state, lastOccReadStatus); - // File is open and state can be used. - if (file.is_open() && file.good()) + if (stateWasRead && (state != lastState)) { - stateWasRead = true; - file >> state; - // Read the error code (if any) to check status of the read - std::ios_base::iostate readState = file.rdstate(); - if (readState) + // Trace OCC state changes + lg2::info( + "Status::readOccState: OCC{INST} state {STATE} (lastState: {PRIOR})", + "INST", instance, "STATE", lg2::hex, state, "PRIOR", lg2::hex, + lastState); + lastState = state; + + if (OccState(state) == OccState::ACTIVE) { - // There was a failure reading the file - if (lastOccReadStatus != -1) + if (pmode && device.master()) + { + // Set the master OCC on the PowerMode object + pmode->setMasterOcc(path); + // Enable mode changes + pmode->setMasterActive(); + + // Special processing by master OCC when it goes active + occsWentActive(); + } + + CmdStatus status = sendAmbient(); + if (status != CmdStatus::SUCCESS) { - // Trace error bits - std::string errorBits = ""; - if (readState & std::ios_base::eofbit) - { - errorBits += " EOF"; - } - if (readState & std::ios_base::failbit) - { - errorBits += " failbit"; - } - if (readState & std::ios_base::badbit) - { - errorBits += " badbit"; - } lg2::error( - "readOccState: Failed to read OCC{INST} state: Read error on I/O operation - {ERROR}", - "INST", instance, "ERROR", errorBits); - lastOccReadStatus = -1; + "readOccState: Sending Ambient failed with status {STATUS}", + "STATUS", status); } - stateWasRead = false; } - if (stateWasRead && (state != lastState)) + // If OCC in known Good State. + if ((OccState(state) == OccState::ACTIVE) || + (OccState(state) == OccState::CHARACTERIZATION) || + (OccState(state) == OccState::OBSERVATION)) { - // Trace OCC state changes - lg2::info( - "Status::readOccState: OCC{INST} state {STATE} (lastState: {PRIOR})", - "INST", instance, "STATE", lg2::hex, state, "PRIOR", lg2::hex, - lastState); - lastState = state; - - if (OccState(state) == OccState::ACTIVE) - { - if (pmode && device.master()) - { - // Set the master OCC on the PowerMode object - pmode->setMasterOcc(path); - // Enable mode changes - pmode->setMasterActive(); - - // Special processing by master OCC when it goes active - occsWentActive(); - } - - CmdStatus status = sendAmbient(); - if (status != CmdStatus::SUCCESS) - { - lg2::error( - "readOccState: Sending Ambient failed with status {STATUS}", - "STATUS", status); - } - } + // Good OCC State then sensors valid again + stateValid = true; + sensorsValid = true; - // If OCC in known Good State. - if ((OccState(state) == OccState::ACTIVE) || - (OccState(state) == OccState::CHARACTERIZATION) || - (OccState(state) == OccState::OBSERVATION)) + if (safeStateDelayTimer.isEnabled()) { - // Good OCC State then sensors valid again - stateValid = true; - sensorsValid = true; - - if (safeStateDelayTimer.isEnabled()) - { - // stop safe delay timer (no longer in SAFE state) - safeStateDelayTimer.setEnabled(false); - } + // stop safe delay timer (no longer in SAFE state) + safeStateDelayTimer.setEnabled(false); } - else + } + else + { + // OCC is in SAFE or some other unsupported state + if (!safeStateDelayTimer.isEnabled()) { - // OCC is in SAFE or some other unsupported state - if (!safeStateDelayTimer.isEnabled()) - { - lg2::error( - "readOccState: Invalid OCC{INST} state of {STATE} (last state: {PRIOR}), starting safe state delay timer", - "INST", instance, "STATE", lg2::hex, state, "PRIOR", - lg2::hex, lastState); - // start safe delay timer (before requesting reset) - using namespace std::literals::chrono_literals; - safeStateDelayTimer.restartOnce(60s); - } + lg2::error( + "readOccState: Invalid OCC{INST} state of {STATE} (last state: {PRIOR}), starting safe state delay timer", + "INST", instance, "STATE", lg2::hex, state, "PRIOR", + lg2::hex, lastState); + // start safe delay timer (before requesting reset) + using namespace std::literals::chrono_literals; + safeStateDelayTimer.restartOnce(60s); + } - if (sensorsValid) - { - sensorsValid = false; - // Sensors not supported (update to NaN and not functional) - manager.setSensorValueToNaN(instance); - } + if (sensorsValid) + { + sensorsValid = false; + // Sensors not supported (update to NaN and not functional) + setSensorValueToNaN(); } } } - else - { - // Unable to read state - stateValid = false; - } - - file.close(); - // if failed to read the OCC state -> Attempt retry + // if failed to read the OCC state -> Attempt retry if (!stateWasRead) { if (sensorsValid) { sensorsValid = false; - manager.setSensorValueToNaN(instance); - } - - // If not able to read, OCC may be offline - if (openErrno != lastOccReadStatus) - { - lg2::error( - "Status::readOccState: open/read failed trying to read OCC{INST} state (open errno={ERROR})", - "INST", instance, "ERROR", openErrno); - lastOccReadStatus = openErrno; + setSensorValueToNaN(); } // See occReadRetries for number of retry attempts. @@ -566,6 +519,38 @@ void Status::occReadStateNow() } } +void Status::setSensorValueToNaN() const +{ + for (const auto& [sensorPath, occId] : existingSensors) + { + if (occId == instance) + { + dbus::OccDBusSensors::getOccDBus().setValue( + sensorPath, std::numeric_limits::quiet_NaN()); + + dbus::OccDBusSensors::getOccDBus().setOperationalStatus( + sensorPath, true); + } + } + return; +} + +void Status::setSensorValueToNonFunctional() const +{ + for (const auto& [sensorPath, occId] : existingSensors) + { + if (occId == instance) + { + dbus::OccDBusSensors::getOccDBus().setValue( + sensorPath, std::numeric_limits::quiet_NaN()); + + dbus::OccDBusSensors::getOccDBus().setOperationalStatus( + sensorPath, false); + } + } + return; +} + // Update processor throttle status on dbus void Status::updateThrottle(const bool isThrottled, const uint8_t newReason) { diff --git a/occ_status.hpp b/occ_status.hpp index 606c65e..abebdc7 100644 --- a/occ_status.hpp +++ b/occ_status.hpp @@ -7,6 +7,7 @@ #include "powercap.hpp" #include "powermode.hpp" #include "utils.hpp" +#include "occ_poll_handler.hpp" #include #include @@ -94,6 +95,7 @@ class Status : public Interface fs::path(DEV_PATH) / fs::path(sysfsName + "." + std::to_string(instance + 1)), managerRef, *this, powerModeRef, instance), + occPollHandler( *this ), hostControlSignal( utils::getBus(), sdbusRule::type::signal() + sdbusRule::member("CommandComplete") + @@ -161,7 +163,7 @@ class Status : public Interface } /** @brief Read OCC state (will trigger kernel to poll the OCC) */ - void readOccState(); + void PollHandler(); /** @brief Called when device errors are detected * @@ -227,6 +229,21 @@ class Status : public Interface */ void updateThrottle(const bool isThrottled, const uint8_t reason); + /** + * @brief Set all sensor values of this OCC to NaN. + * @param[in] id - Id of the OCC. + * */ + void setSensorValueToNaN() const; + + /** @brief Set all sensor values of this OCC to NaN and non functional. + * + * @param[in] id - Id of the OCC. + */ + void setSensorValueToNonFunctional() const; + + /** @brief Store the existing OCC sensors on D-BUS */ + std::map existingSensors; + private: /** @brief OCC dbus object path */ std::string path; @@ -272,6 +289,9 @@ class Status : public Interface /** @brief OCC device object to do bind and unbind */ Device device; + /** @brief OCC device object to do bind and unbind */ + OccPollHandler occPollHandler; + /** @brief Subscribe to host control signal * * Once the OCC reset is requested, BMC sends that message to host. diff --git a/powercap.cpp b/powercap.cpp index 2116d49..bd9753d 100644 --- a/powercap.cpp +++ b/powercap.cpp @@ -101,69 +101,9 @@ void OccPersistCapData::load() } } -void PowerCap::updatePcapBounds() +void PowerCap::updatePcapBounds(bool& parmsChanged,uint32_t& capSoftMin, + uint32_t& capHardMin, uint32_t& capMax) { - // Build the hwmon string to write the power cap bounds - fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"}); - fs::path softMinName = - getPcapFilename(std::regex{"power\\d+_cap_min_soft$"}); - fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"}); - - // Read the current limits from persistent data - uint32_t capSoftMin, capHardMin, capMax; - persistedData.getCapLimits(capSoftMin, capHardMin, capMax); - - // Read the power cap bounds from sysfs files (from OCC) - uint64_t cap; - bool parmsChanged = false; - std::ifstream softMinFile(softMinName, std::ios::in); - if (softMinFile) - { - softMinFile >> cap; - softMinFile.close(); - // Convert to input/AC Power in Watts (round up) - capSoftMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9); - parmsChanged = true; - } - else - { - lg2::error( - "updatePcapBounds: unable to find pcap_min_soft file: {FILE} (errno={ERR})", - "FILE", pcapBasePathname, "ERR", errno); - } - - std::ifstream minFile(minName, std::ios::in); - if (minFile) - { - minFile >> cap; - minFile.close(); - // Convert to input/AC Power in Watts (round up) - capHardMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9); - parmsChanged = true; - } - else - { - lg2::error( - "updatePcapBounds: unable to find cap_min file: {FILE} (errno={ERR})", - "FILE", pcapBasePathname, "ERR", errno); - } - - std::ifstream maxFile(maxName, std::ios::in); - if (maxFile) - { - maxFile >> cap; - maxFile.close(); - // Convert to input/AC Power in Watts (truncate remainder) - capMax = cap / (PS_DERATING_FACTOR / 100.0) / 1000000; - parmsChanged = true; - } - else - { - lg2::error( - "updatePcapBounds: unable to find cap_max file: {FILE} (errno={ERR})", - "FILE", pcapBasePathname, "ERR", errno); - } - if (parmsChanged) { // Save the power cap bounds to dbus @@ -171,6 +111,7 @@ void PowerCap::updatePcapBounds() persistedData.updateCapLimits(capSoftMin, capHardMin, capMax); } + // // Validate user power cap (if enabled) is within the bounds const uint32_t dbusUserCap = getPcap(); const bool pcapEnabled = getPcapEnabled(); @@ -420,6 +361,7 @@ void PowerCap::pcapChanged(sdbusplus::message_t& msg) // Validate the cap is within supported range uint32_t capSoftMin, capHardMin, capMax; persistedData.getCapLimits(capSoftMin, capHardMin, capMax); + if (((pcap > 0) && (pcap < capSoftMin)) || ((pcap == 0) && (pcapEnabled))) { lg2::error( diff --git a/powercap.hpp b/powercap.hpp index fab3918..aaa29de 100644 --- a/powercap.hpp +++ b/powercap.hpp @@ -165,7 +165,8 @@ class PowerCap : public CapLimitsInterface uint32_t getOccInput(uint32_t pcap, bool pcapEnabled); /** @brief Read the power cap bounds from sysfs and update DBus */ - void updatePcapBounds(); + void updatePcapBounds(bool& parmsChanged,uint32_t& capSoftMin, + uint32_t& capHardMin, uint32_t& capMax); private: /** @brief Persisted power cap limits */ From 61b4767653ccc0518fa79f2109b9324e533082db Mon Sep 17 00:00:00 2001 From: Sheldon Bailey Date: Tue, 11 Nov 2025 20:49:56 +0000 Subject: [PATCH 2/2] Move OCC poll processing to occ_control This is so we can make changes to the OCC poll response without kernel updates. Signed-off-by: Sheldon Bailey --- occ_manager.cpp | 7 ++++--- occ_poll_handler.cpp | 5 +++++ occ_status.cpp | 22 ++++++++-------------- occ_status.hpp | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/occ_manager.cpp b/occ_manager.cpp index 6b0cd82..c3e751b 100644 --- a/occ_manager.cpp +++ b/occ_manager.cpp @@ -518,10 +518,11 @@ void Manager::statusCallBack(instanceID instance, bool status) // Clear OCC sensors for (auto& obj : statusObjects) { - // int instance = obj->getOccInstanceID(); - obj->setSensorValueToNaN(); + if (instance == obj->getOccInstanceID()) + { + obj->setSensorValueToNaN(); + } } - } if (waitingForAllOccActiveSensors) diff --git a/occ_poll_handler.cpp b/occ_poll_handler.cpp index 84e266b..041dcf8 100644 --- a/occ_poll_handler.cpp +++ b/occ_poll_handler.cpp @@ -684,6 +684,7 @@ void OccPollHandler::PushExttSensorsToDbus(uint16_t& index ) }//end OCCC PushExttSensorsToDbus +// Called once when the master OCC goes active. This means parms always changed. bool OccPollHandler::pollReadPcapBounds(uint32_t& capSoftMin, uint32_t& capHardMin, uint32_t& capMax) { bool parmsChanged = true; @@ -804,6 +805,10 @@ void OccPollHandler::HandlePollAction() return; }//end Kernel HandlePollAction +<<<<<<< HEAD +======= +// Called once when the master OCC goes active. This means parms always changed. +>>>>>>> b9eece9 (Move OCC poll processing to occ_control) bool OccPollHandler::pollReadPcapBounds(uint32_t& capSoftMin, uint32_t& capHardMin, uint32_t& capMax) { diff --git a/occ_status.cpp b/occ_status.cpp index 0af64d1..ff0cbec 100644 --- a/occ_status.cpp +++ b/occ_status.cpp @@ -523,14 +523,11 @@ void Status::setSensorValueToNaN() const { for (const auto& [sensorPath, occId] : existingSensors) { - if (occId == instance) - { - dbus::OccDBusSensors::getOccDBus().setValue( - sensorPath, std::numeric_limits::quiet_NaN()); + dbus::OccDBusSensors::getOccDBus().setValue( + sensorPath, std::numeric_limits::quiet_NaN()); - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, true); - } + dbus::OccDBusSensors::getOccDBus().setOperationalStatus( + sensorPath, true); } return; } @@ -539,14 +536,11 @@ void Status::setSensorValueToNonFunctional() const { for (const auto& [sensorPath, occId] : existingSensors) { - if (occId == instance) - { - dbus::OccDBusSensors::getOccDBus().setValue( - sensorPath, std::numeric_limits::quiet_NaN()); + dbus::OccDBusSensors::getOccDBus().setValue( + sensorPath, std::numeric_limits::quiet_NaN()); - dbus::OccDBusSensors::getOccDBus().setOperationalStatus( - sensorPath, false); - } + dbus::OccDBusSensors::getOccDBus().setOperationalStatus( + sensorPath, false); } return; } diff --git a/occ_status.hpp b/occ_status.hpp index abebdc7..ccec29b 100644 --- a/occ_status.hpp +++ b/occ_status.hpp @@ -162,7 +162,7 @@ class Status : public Interface return device.master(); } - /** @brief Read OCC state (will trigger kernel to poll the OCC) */ + /** @brief Read OCC POLL data(by trigger kernel or by direct OCC cmd) */ void PollHandler(); /** @brief Called when device errors are detected