1 #include "occ_errors.hpp" 2 3 #include "elog-errors.hpp" 4 5 #include <errno.h> 6 #include <fcntl.h> 7 #include <sys/ioctl.h> 8 #include <unistd.h> 9 10 #include <org/open_power/OCC/Device/error.hpp> 11 #include <phosphor-logging/elog.hpp> 12 #include <phosphor-logging/log.hpp> 13 #include <xyz/openbmc_project/Common/error.hpp> 14 namespace open_power 15 { 16 namespace occ 17 { 18 19 // Value in error file indicating success 20 constexpr auto NO_ERROR = '0'; 21 22 using namespace phosphor::logging; 23 using namespace sdbusplus::org::open_power::OCC::Device::Error; 24 using InternalFailure = 25 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 26 27 // Populate the file descriptor on the error file 28 void Error::openFile() 29 { 30 using namespace phosphor::logging; 31 32 fd = open(file.c_str(), O_RDONLY | O_NONBLOCK); 33 if (fd < 0) 34 { 35 elog<OpenFailure>(phosphor::logging::org::open_power::OCC::Device:: 36 OpenFailure::CALLOUT_ERRNO(errno), 37 phosphor::logging::org::open_power::OCC::Device:: 38 OpenFailure::CALLOUT_DEVICE_PATH(file.c_str())); 39 } 40 } 41 42 // Attaches the FD to event loop and registers the callback handler 43 void Error::registerCallBack() 44 { 45 decltype(eventSource.get()) sourcePtr = nullptr; 46 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR, 47 processEvents, this); 48 eventSource.reset(sourcePtr); 49 50 if (r < 0) 51 { 52 log<level::ERR>("Failed to register callback handler", 53 entry("ERROR=%s", strerror(-r))); 54 elog<InternalFailure>(); 55 } 56 } 57 58 // Starts to watch for errors 59 void Error::addWatch(bool poll) 60 { 61 if (!watching) 62 { 63 // Open the file 64 openFile(); 65 66 if (poll) 67 { 68 // register the callback handler 69 registerCallBack(); 70 } 71 72 // Set we are watching the error 73 watching = true; 74 } 75 } 76 77 // Stops watching for errors 78 void Error::removeWatch() 79 { 80 if (watching) 81 { 82 // Close the file 83 if (fd >= 0) 84 { 85 close(fd); 86 } 87 88 // Reduce the reference count. Since there is only one instances 89 // of add_io, this will result empty loop 90 eventSource.reset(); 91 92 // We are no more watching the error 93 watching = false; 94 } 95 } 96 97 // Callback handler when there is an activity on the FD 98 int Error::processEvents(sd_event_source* es, int fd, uint32_t revents, 99 void* userData) 100 { 101 auto error = static_cast<Error*>(userData); 102 103 error->analyzeEvent(); 104 return 0; 105 } 106 107 // Reads the error file and analyzes the data 108 void Error::analyzeEvent() 109 { 110 // Get the number of bytes to read 111 int len = -1; 112 auto r = ioctl(fd, FIONREAD, &len); 113 if (r < 0) 114 { 115 elog<ConfigFailure>( 116 phosphor::logging::org::open_power::OCC::Device::ConfigFailure:: 117 CALLOUT_ERRNO(errno), 118 phosphor::logging::org::open_power::OCC::Device::ConfigFailure:: 119 CALLOUT_DEVICE_PATH(file.c_str())); 120 } 121 122 // A non-zero data indicates an error condition 123 // Let the caller take appropriate action on this 124 auto data = readFile(len); 125 bool error = !(data.empty() || data.front() == NO_ERROR); 126 if (callBack) 127 { 128 callBack(error); 129 } 130 return; 131 } 132 133 // Reads so many bytes as passed in 134 std::string Error::readFile(int len) const 135 { 136 auto data = std::make_unique<char[]>(len + 1); 137 auto retries = 3; 138 auto delay = std::chrono::milliseconds{100}; 139 140 // OCC / FSI have intermittent issues so retry all reads 141 while (true) 142 { 143 // This file get created soon after binding. A value of 0 is 144 // deemed success and anything else is a Failure 145 // Since all the sysfs files would have size of 4096, if we read 0 146 // bytes -or- value '0', then it just means we are fine 147 auto r = read(fd, data.get(), len); 148 if (r < 0) 149 { 150 retries--; 151 if (retries == 0) 152 { 153 elog<ReadFailure>( 154 phosphor::logging::org::open_power::OCC::Device:: 155 ReadFailure::CALLOUT_ERRNO(errno), 156 phosphor::logging::org::open_power::OCC::Device:: 157 ReadFailure::CALLOUT_DEVICE_PATH(file.c_str())); 158 break; 159 } 160 std::this_thread::sleep_for(delay); 161 continue; 162 } 163 break; 164 } 165 // Need to seek to START, else the poll returns immediately telling 166 // there is data to be read 167 auto r = lseek(fd, 0, SEEK_SET); 168 if (r < 0) 169 { 170 log<level::ERR>("Failure seeking error file to START"); 171 elog<ConfigFailure>( 172 phosphor::logging::org::open_power::OCC::Device::ConfigFailure:: 173 CALLOUT_ERRNO(errno), 174 phosphor::logging::org::open_power::OCC::Device::ConfigFailure:: 175 CALLOUT_DEVICE_PATH(file.c_str())); 176 } 177 return std::string(data.get()); 178 } 179 180 } // namespace occ 181 } // namespace open_power 182