BMC: Fan control for Idle Power saver
Read IPS active from sysfs and push onto dBus IPS properties
Tested: enable/disable IPS on WEB, occ instrospect ips
Signed-off-by: Sheldon Bailey <baileysh@us.ibm.com>
Change-Id: I12935a4c9f82bf004b1311e477f73ed8b1401a91
diff --git a/occ_device.hpp b/occ_device.hpp
index 3c11d13..5b66bf5 100644
--- a/occ_device.hpp
+++ b/occ_device.hpp
@@ -6,6 +6,7 @@
#include "occ_events.hpp"
#include "occ_ffdc.hpp"
#include "occ_presence.hpp"
+#include "powermode.hpp"
#include <org/open_power/OCC/Device/error.hpp>
@@ -45,7 +46,11 @@
* @param[in] instance - OCC instance number
*/
Device(EventPtr& event, const fs::path& path, Manager& manager,
- Status& status, unsigned int instance = 0) :
+ Status& status,
+#ifdef POWER10
+ std::unique_ptr<powermode::PowerMode>& powerModeRef,
+#endif
+ unsigned int instance = 0) :
config(getPathBack(path)),
devPath(path), instance(instance), statusObject(status),
managerObject(manager),
@@ -78,6 +83,10 @@
throttleMemTemp(event, path / "occ_mem_throttle",
std::bind(std::mem_fn(&Device::throttleMemTempCallback),
this, std::placeholders::_1))
+#ifdef POWER10
+ ,
+ pmode(powerModeRef)
+#endif
{
// Nothing to do here
}
@@ -129,6 +138,13 @@
throttleProcTemp.addWatch(poll);
}
+#ifdef POWER10
+ if (master())
+ {
+ pmode->addIpsWatch(poll);
+ }
+#endif
+
throttleProcPower.addWatch(poll);
throttleMemTemp.addWatch(poll);
@@ -164,6 +180,12 @@
throttleMemTemp.removeWatch();
throttleProcPower.removeWatch();
throttleProcTemp.removeWatch();
+#ifdef POWER10
+ if (master())
+ {
+ pmode->removeIpsWatch();
+ }
+#endif
}
/** @brief Starts to watch how many OCCs are present on the master */
@@ -229,6 +251,11 @@
Error throttleProcPower;
Error throttleMemTemp;
+#ifdef POWER10
+ /** @brief OCC PowerMode object */
+ std::unique_ptr<powermode::PowerMode>& pmode;
+#endif
+
/** @brief file writer to achieve bind and unbind
*
* @param[in] filename - Name of file to be written
diff --git a/occ_manager.cpp b/occ_manager.cpp
index f0c59bb..c5038de 100644
--- a/occ_manager.cpp
+++ b/occ_manager.cpp
@@ -247,7 +247,12 @@
{
// Create the power mode object
pmode = std::make_unique<powermode::PowerMode>(
- *this, powermode::PMODE_PATH, powermode::PIPS_PATH);
+ *this, powermode::PMODE_PATH, powermode::PIPS_PATH
+#ifdef POWER10
+ ,
+ event
+#endif
+ );
}
#endif
diff --git a/occ_status.hpp b/occ_status.hpp
index ef8fad0..9ae8fe7 100644
--- a/occ_status.hpp
+++ b/occ_status.hpp
@@ -100,7 +100,11 @@
fs::path(DEV_PATH) /
fs::path(sysfsName + "." + std::to_string(instance + 1)),
#endif
- managerRef, *this, instance),
+ managerRef, *this,
+#ifdef POWER10
+ powerModeRef,
+#endif
+ instance),
hostControlSignal(
utils::getBus(),
sdbusRule::type::signal() + sdbusRule::member("CommandComplete") +
diff --git a/powermode.cpp b/powermode.cpp
index ed17096..f091088 100644
--- a/powermode.cpp
+++ b/powermode.cpp
@@ -1,9 +1,15 @@
#include "powermode.hpp"
+#include "elog-errors.hpp"
+
+#include <fcntl.h>
#include <fmt/core.h>
+#include <sys/ioctl.h>
#include <com/ibm/Host/Target/server.hpp>
+#include <org/open_power/OCC/Device/error.hpp>
#include <phosphor-logging/log.hpp>
+#include <xyz/openbmc_project/Common/error.hpp>
#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
#include <cassert>
@@ -19,6 +25,7 @@
using namespace phosphor::logging;
using namespace std::literals::string_literals;
+using namespace sdbusplus::org::open_power::OCC::Device::Error;
using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
@@ -867,6 +874,194 @@
return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
}
+#ifdef POWER10
+
+// Starts to watch for IPS active state changes.
+bool PowerMode::openIpsFile()
+{
+ bool rc = true;
+ fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
+ const int open_errno = errno;
+ if (fd < 0)
+ {
+ log<level::ERR>(fmt::format("openIpsFile Error({})={} : File={}",
+ open_errno, strerror(open_errno),
+ ipsStatusFile.c_str())
+ .c_str());
+
+ close(fd);
+
+ using namespace sdbusplus::org::open_power::OCC::Device::Error;
+ report<OpenFailure>(
+ phosphor::logging::org::open_power::OCC::Device::OpenFailure::
+ CALLOUT_ERRNO(open_errno),
+ phosphor::logging::org::open_power::OCC::Device::OpenFailure::
+ CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
+
+ // We are no longer watching the error
+ active(false);
+
+ watching = false;
+ rc = false;
+ // NOTE: this will leave the system not reporting IPS active state to
+ // Fan Controls, Until an APP reload, or IPL and we will attempt again.
+ }
+ return rc;
+}
+
+// Starts to watch for IPS active state changes.
+void PowerMode::addIpsWatch(bool poll)
+{
+ // open file and register callback on file if we are not currently watching,
+ // and if poll=true, and if we are the master.
+ if ((!watching) && poll)
+ {
+ // Open the file
+ if (openIpsFile())
+ {
+ // register the callback handler which sets 'watching'
+ registerIpsStatusCallBack();
+ }
+ }
+}
+
+// Stops watching for IPS active state changes.
+void PowerMode::removeIpsWatch()
+{
+ // NOTE: we want to remove event, close file, and IPS active false no
+ // matter what the 'watching' flags is set to.
+
+ // We are no longer watching the error
+ active(false);
+
+ watching = false;
+
+ // Close file
+ close(fd);
+
+ // clears sourcePtr in the event source.
+ eventSource.reset();
+}
+
+// Attaches the FD to event loop and registers the callback handler
+void PowerMode::registerIpsStatusCallBack()
+{
+ decltype(eventSource.get()) sourcePtr = nullptr;
+
+ auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
+ ipsStatusCallBack, this);
+ if (r < 0)
+ {
+ log<level::ERR>(fmt::format("sd_event_add_io: Error({})={} : File={}",
+ r, strerror(-r), ipsStatusFile.c_str())
+ .c_str());
+
+ using InternalFailure =
+ sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
+ report<InternalFailure>();
+
+ removeIpsWatch();
+ // NOTE: this will leave the system not reporting IPS active state to
+ // Fan Controls, Until an APP reload, or IPL and we will attempt again.
+ }
+ else
+ {
+ // puts sourcePtr in the event source.
+ eventSource.reset(sourcePtr);
+ // Set we are watching the error
+ watching = true;
+ }
+}
+
+// Static function to redirect to non static analyze event function to be
+// able to read file and push onto dBus.
+int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
+ uint32_t /*revents*/, void* userData)
+{
+ auto pmode = static_cast<PowerMode*>(userData);
+ pmode->analyzeIpsEvent();
+ return 0;
+}
+
+// Function to Read SysFs file change on IPS state and push on dBus.
+void PowerMode::analyzeIpsEvent()
+{
+ // Need to seek to START, else the poll returns immediately telling
+ // there is data to be read. if not done this floods the system.
+ auto r = lseek(fd, 0, SEEK_SET);
+ const int open_errno = errno;
+ if (r < 0)
+ {
+ // NOTE: upon file access error we can not just re-open file, we have to
+ // remove and add to watch.
+ removeIpsWatch();
+ addIpsWatch(true);
+ }
+
+ // if we are 'watching' that is the file seek, or the re-open passed.. we
+ // can read the data
+ if (watching)
+ {
+ // This file gets created when polling OCCs. A value or length of 0 is
+ // deemed success. That means we would disable IPS active on dbus.
+ char data;
+ bool ipsState = false;
+ const auto len = read(fd, &data, sizeof(data));
+ const int readErrno = errno;
+ if (len <= 0)
+ {
+ removeIpsWatch();
+
+ log<level::ERR>(
+ fmt::format("IPS state Read Error({})={} : File={} : len={}",
+ readErrno, strerror(readErrno),
+ ipsStatusFile.c_str(), len)
+ .c_str());
+
+ report<ReadFailure>(
+ phosphor::logging::org::open_power::OCC::Device::ReadFailure::
+ CALLOUT_ERRNO(readErrno),
+ phosphor::logging::org::open_power::OCC::Device::ReadFailure::
+ CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
+
+ // NOTE: this will leave the system not reporting IPS active state
+ // to Fan Controls, Until an APP reload, or IPL and we will attempt
+ // again.
+ }
+ else
+ {
+ // Data returned in ASCII.
+ // convert to integer. atoi()
+ // from OCC_P10_FW_Interfaces spec
+ // Bit 6: IPS active 1 indicates enabled.
+ // Bit 7: IPS enabled. 1 indicates enabled.
+ // mask off bit 6 --> & 0x02
+ // Shift left one bit and store as bool. >> 1
+ ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
+ }
+
+ // This will only set IPS active dbus if different than current.
+ active(ipsState);
+ }
+ else
+ {
+ removeIpsWatch();
+
+ // If the Retry did not get to "watching = true" we already have an
+ // error log, just post trace.
+ log<level::ERR>(fmt::format("Retry on File seek Error({})={} : File={}",
+ open_errno, strerror(open_errno),
+ ipsStatusFile.c_str())
+ .c_str());
+
+ // NOTE: this will leave the system not reporting IPS active state to
+ // Fan Controls, Until an APP reload, or IPL and we will attempt again.
+ }
+
+ return;
+}
+#endif
+
} // namespace powermode
} // namespace occ
diff --git a/powermode.hpp b/powermode.hpp
index d15eea7..dbd68eb 100644
--- a/powermode.hpp
+++ b/powermode.hpp
@@ -39,6 +39,7 @@
constexpr auto PIPS_PATH = "/xyz/openbmc_project/control/host0/power_ips";
constexpr auto PIPS_INTERFACE =
"xyz.openbmc_project.Control.Power.IdlePowerSaver";
+constexpr auto IPS_ACTIVE_PROP = "Active";
constexpr auto IPS_ENABLED_PROP = "Enabled";
constexpr auto IPS_ENTER_UTIL = "EnterUtilizationPercent";
constexpr auto IPS_ENTER_TIME = "EnterDwellTime";
@@ -233,7 +234,12 @@
* @param[in] ipsPath - Idle Power Saver dbus path
*/
explicit PowerMode(const Manager& managerRef, const char* modePath,
- const char* ipsPath) :
+ const char* ipsPath
+#ifdef POWER10
+ ,
+ EventPtr& event
+#endif
+ ) :
ModeInterface(utils::getBus(), modePath, false),
IpsInterface(utils::getBus(), ipsPath, false), manager(managerRef),
pmodeMatch(utils::getBus(),
@@ -250,6 +256,10 @@
"/xyz/openbmc_project/inventory", PMODE_DEFAULT_INTERFACE),
[this](auto& msg) { this->defaultsReady(msg); }),
masterOccSet(false), masterActive(false)
+#ifdef POWER10
+ ,
+ event(event)
+#endif
{
// restore Power Mode to DBus
SysPwrMode currentMode;
@@ -309,6 +319,18 @@
masterActive = isActive;
};
+#ifdef POWER10
+ /** @brief Starts to monitor for IPS active state change conditions
+ *
+ * @param[in] poll - Indicates whether or not the IPS state file should
+ * actually be read for changes.
+ */
+ void addIpsWatch(bool poll = true);
+
+ /** @brief Removes IPS active watch */
+ void removeIpsWatch();
+#endif
+
private:
/** @brief OCC manager object */
const Manager& manager;
@@ -339,6 +361,19 @@
/** @brief True when the master OCC is active */
bool masterActive;
+#ifdef POWER10
+ /** @brief IPS status data filename to read */
+ const fs::path ipsStatusFile = std::filesystem::path{OCC_HWMON_PATH} /
+ std::filesystem::path{OCC_MASTER_NAME} /
+ "occ_ips_status";
+
+ /** @brief Current state of error watching */
+ bool watching = false;
+
+ /** @brief register for the callback from the POLL IPS changed event */
+ void registerIpsStatusCallBack();
+#endif
+
/** @brief Callback for pmode setting changes
*
* Process change and inform OCC
@@ -435,6 +470,34 @@
* @return true if restore was successful
*/
bool useDefaultIPSParms();
+
+#ifdef POWER10
+ /** @brief callback for the POLL IPS changed event
+ *
+ * @param[in] es - Populated event source
+ * @param[in] fd - Associated File descriptor
+ * @param[in] revents - Type of event
+ * @param[in] userData - User data that was passed during registration
+ */
+ static int ipsStatusCallBack(sd_event_source* es, int fd, uint32_t revents,
+ void* userData);
+
+ /** @brief Opens the IPS file and populates fd */
+ bool openIpsFile();
+
+ /** @brief sd_event wrapped in unique_ptr */
+ EventPtr& event;
+
+ /** @brief event source wrapped in unique_ptr */
+ EventSourcePtr eventSource;
+
+ /** @brief When the ips status event is received, analyzes it */
+ virtual void analyzeIpsEvent();
+
+ protected:
+ /** @brief File descriptor to watch for errors */
+ int fd = -1;
+#endif
};
} // namespace powermode