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/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