#include "occ_errors.hpp" #include "elog-errors.hpp" #include #include #include #include #include #include #include #include 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); if (fd < 0) { elog(phosphor::logging::org::open_power::OCC::Device:: OpenFailure::CALLOUT_ERRNO(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("Failed to register callback handler", entry("ERROR=%s", strerror(-r))); elog(); } } // 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(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( 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(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( 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("Failure seeking error file to START"); elog( 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