blob: c6f450d36bc8aef3547bd7bc325670d774aca55a [file] [log] [blame]
#include "occ_errors.hpp"
#include "elog-errors.hpp"
#include <errno.h>
#include <fcntl.h>
#include <fmt/core.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <org/open_power/OCC/Device/error.hpp>
#include <phosphor-logging/elog.hpp>
#include <phosphor-logging/log.hpp>
#include <xyz/openbmc_project/Common/error.hpp>
namespace open_power
{
namespace occ
{
// Value in error file indicating success
constexpr auto NO_ERROR = '0';
using namespace phosphor::logging;
using namespace sdbusplus::org::open_power::OCC::Device::Error;
using InternalFailure =
sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
// Populate the file descriptor on the error file
void Error::openFile()
{
using namespace phosphor::logging;
fd = open(file.c_str(), O_RDONLY | O_NONBLOCK);
const int open_errno = errno;
if (fd < 0)
{
log<level::ERR>(
fmt::format("Error::openFile: open failed (errno={})", open_errno)
.c_str());
elog<OpenFailure>(phosphor::logging::org::open_power::OCC::Device::
OpenFailure::CALLOUT_ERRNO(open_errno),
phosphor::logging::org::open_power::OCC::Device::
OpenFailure::CALLOUT_DEVICE_PATH(file.c_str()));
}
}
// Attaches the FD to event loop and registers the callback handler
void Error::registerCallBack()
{
decltype(eventSource.get()) sourcePtr = nullptr;
auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
processEvents, this);
eventSource.reset(sourcePtr);
if (r < 0)
{
log<level::ERR>("Failed to register callback handler",
entry("ERROR=%s", strerror(-r)));
elog<InternalFailure>();
}
}
// Starts to watch for errors
void Error::addWatch(bool poll)
{
if (!watching)
{
// Open the file
openFile();
if (poll)
{
// register the callback handler
registerCallBack();
}
// Set we are watching the error
watching = true;
}
}
// Stops watching for errors
void Error::removeWatch()
{
if (watching)
{
// Close the file
if (fd >= 0)
{
close(fd);
}
// Reduce the reference count. Since there is only one instances
// of add_io, this will result empty loop
eventSource.reset();
// We are no more watching the error
watching = false;
}
}
// Callback handler when there is an activity on the FD
int Error::processEvents(sd_event_source* /*es*/, int /*fd*/,
uint32_t /*revents*/, void* userData)
{
auto error = static_cast<Error*>(userData);
error->analyzeEvent();
return 0;
}
// Reads the error file and analyzes the data
void Error::analyzeEvent()
{
// Get the number of bytes to read
int len = -1;
auto r = ioctl(fd, FIONREAD, &len);
if (r < 0)
{
elog<ConfigFailure>(
phosphor::logging::org::open_power::OCC::Device::ConfigFailure::
CALLOUT_ERRNO(errno),
phosphor::logging::org::open_power::OCC::Device::ConfigFailure::
CALLOUT_DEVICE_PATH(file.c_str()));
}
// A non-zero data indicates an error condition
// Let the caller take appropriate action on this
auto data = readFile(len);
bool error = !(data.empty() || data.front() == NO_ERROR);
if (callBack)
{
callBack(error);
}
return;
}
// Reads so many bytes as passed in
std::string Error::readFile(int len) const
{
auto data = std::make_unique<char[]>(len + 1);
auto retries = 3;
auto delay = std::chrono::milliseconds{100};
// OCC / FSI have intermittent issues so retry all reads
while (true)
{
// This file get created soon after binding. A value of 0 is
// deemed success and anything else is a Failure
// Since all the sysfs files would have size of 4096, if we read 0
// bytes -or- value '0', then it just means we are fine
auto r = read(fd, data.get(), len);
if (r < 0)
{
retries--;
if (retries == 0)
{
elog<ReadFailure>(
phosphor::logging::org::open_power::OCC::Device::
ReadFailure::CALLOUT_ERRNO(errno),
phosphor::logging::org::open_power::OCC::Device::
ReadFailure::CALLOUT_DEVICE_PATH(file.c_str()));
break;
}
std::this_thread::sleep_for(delay);
continue;
}
break;
}
// Need to seek to START, else the poll returns immediately telling
// there is data to be read
auto r = lseek(fd, 0, SEEK_SET);
if (r < 0)
{
log<level::ERR>("Failure seeking error file to START");
elog<ConfigFailure>(
phosphor::logging::org::open_power::OCC::Device::ConfigFailure::
CALLOUT_ERRNO(errno),
phosphor::logging::org::open_power::OCC::Device::ConfigFailure::
CALLOUT_DEVICE_PATH(file.c_str()));
}
return std::string(data.get());
}
} // namespace occ
} // namespace open_power