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