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