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 log<level::INFO>("Error file updated"); 97 auto error = static_cast<Error*>(userData); 98 99 error->analyzeEvent(); 100 return 0; 101 } 102 103 // Reads the error file and analyzes the data 104 void Error::analyzeEvent() 105 { 106 // Get the number of bytes to read 107 int len = -1; 108 auto r = ioctl(fd, FIONREAD, &len); 109 if (r < 0) 110 { 111 elog<ConfigFailure>( 112 phosphor::logging::org::open_power::OCC::Device:: 113 ConfigFailure::CALLOUT_ERRNO(errno), 114 phosphor::logging::org::open_power::OCC::Device:: 115 ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str())); 116 } 117 118 // A non-zero data indicates an error condition 119 // Let the caller take appropriate action on this 120 auto data = readFile(len); 121 if (data.empty() || 122 data.front() == NO_ERROR) 123 { 124 return; 125 } 126 127 // This must be an error 128 if (callBack) 129 { 130 callBack(); 131 } 132 return; 133 } 134 135 // Reads so many bytes as passed in 136 std::string Error::readFile(int len) const 137 { 138 auto data = std::make_unique<char[]>(len+1); 139 140 // This file get created soon after binding. A value of 0 is 141 // deemed success and anything else is a Failure 142 // Since all the sysfs files would have size of 4096, if we read 0 143 // bytes -or- value '0', then it just means we are fine 144 auto r = read(fd, data.get(), len); 145 if (r < 0) 146 { 147 elog<ReadFailure>( 148 phosphor::logging::org::open_power::OCC::Device:: 149 ReadFailure::CALLOUT_ERRNO(errno), 150 phosphor::logging::org::open_power::OCC::Device:: 151 ReadFailure::CALLOUT_DEVICE_PATH(file.c_str())); 152 } 153 154 // Need to seek to START, else the poll returns immediately telling 155 // there is data to be read 156 r = lseek(fd, 0, SEEK_SET); 157 if (r < 0) 158 { 159 log<level::ERR>("Failure seeking error file to START"); 160 elog<ConfigFailure>( 161 phosphor::logging::org::open_power::OCC::Device:: 162 ConfigFailure::CALLOUT_ERRNO(errno), 163 phosphor::logging::org::open_power::OCC::Device:: 164 ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str())); 165 } 166 return std::string(data.get()); 167 } 168 169 } // namespace occ 170 } // namespace open_power 171