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