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