xref: /openbmc/openpower-occ-control/occ_errors.cpp (revision 636577f44fe3fc951538c1119ed0da8ac9a40932)
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     log<level::INFO>("Error file updated",
121                      entry("ERROR=%s", data.c_str()));
122     if (data.empty() ||
123             data.front() == NO_ERROR)
124     {
125         return;
126     }
127 
128     // This must be an error
129     if (callBack)
130     {
131         callBack();
132     }
133     return;
134 }
135 
136 // Reads so many bytes as passed in
137 std::string Error::readFile(int len) const
138 {
139     auto data = std::make_unique<char[]>(len+1);
140 
141     // This file get created soon after binding. A value of 0 is
142     // deemed success and anything else is a Failure
143     // Since all the sysfs files would have size of 4096, if we read 0
144     // bytes -or- value '0', then it just means we are fine
145     auto r = read(fd, data.get(), len);
146     if (r < 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     }
154 
155     // Need to seek to START, else the poll returns immediately telling
156     // there is data to be read
157     r = lseek(fd, 0, SEEK_SET);
158     if (r < 0)
159     {
160         log<level::ERR>("Failure seeking error file to START");
161         elog<ConfigFailure>(
162             phosphor::logging::org::open_power::OCC::Device::
163                 ConfigFailure::CALLOUT_ERRNO(errno),
164             phosphor::logging::org::open_power::OCC::Device::
165                 ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str()));
166     }
167     return std::string(data.get());
168 }
169 
170 } // namespace occ
171 } // namespace open_power
172