blob: f4a5f696216edc7fe5c9cee72144faea0552d288 [file] [log] [blame]
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +05301#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"
11namespace open_power
12{
13namespace occ
14{
15
16// Value in error file indicating success
17constexpr auto NO_ERROR = '0';
18
19using namespace phosphor::logging;
20using namespace sdbusplus::org::open_power::OCC::Device::Error;
21using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
22 Error::InternalFailure;
23
24// Populate the file descriptor on the error file
25void 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
41void Error::registerCallBack()
42{
43 decltype(eventSource.get()) sourcePtr = nullptr;
44 auto r = sd_event_add_io(event.get(), &sourcePtr, fd,
Vishwanatha Subbanna2c129132017-08-29 21:12:01 +053045 EPOLLPRI | EPOLLERR, processEvents, this);
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053046 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
57void Error::addWatch()
58{
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053059 if (!watching)
60 {
61 // Open the file
62 openFile();
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053063
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053064 // register the callback handler
65 registerCallBack();
66
67 // Set we are watching the error
68 watching = true;
69 }
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053070}
71
72// Stops watching for errors
73void Error::removeWatch()
74{
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053075 if (watching)
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053076 {
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053077 // Close the file
78 if (fd >= 0)
79 {
80 close(fd);
81 }
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053082
Vishwanatha Subbanna2dc9b1a2017-08-18 18:29:41 +053083 // 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 }
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053090}
91
92// Callback handler when there is an activity on the FD
93int Error::processEvents(sd_event_source* es, int fd,
94 uint32_t revents, void* userData)
95{
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +053096 auto error = static_cast<Error*>(userData);
97
98 error->analyzeEvent();
99 return 0;
100}
101
102// Reads the error file and analyzes the data
103void 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);
Eddie James482e31f2017-09-14 13:17:17 -0500120 bool error = !(data.empty() || data.front() == NO_ERROR);
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530121 if (callBack)
122 {
Eddie James482e31f2017-09-14 13:17:17 -0500123 callBack(error);
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530124 }
125 return;
126}
127
128// Reads so many bytes as passed in
129std::string Error::readFile(int len) const
130{
131 auto data = std::make_unique<char[]>(len+1);
Andrew Geissler11110872018-01-17 11:07:01 -0800132 auto retries = 3;
133 auto delay = std::chrono::milliseconds{100};
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530134
Andrew Geissler11110872018-01-17 11:07:01 -0800135 // OCC / FSI have intermittent issues so retry all reads
136 while (true)
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530137 {
Andrew Geissler11110872018-01-17 11:07:01 -0800138 // This file get created soon after binding. A value of 0 is
139 // deemed success and anything else is a Failure
140 // Since all the sysfs files would have size of 4096, if we read 0
141 // bytes -or- value '0', then it just means we are fine
142 auto r = read(fd, data.get(), len);
143 if (r < 0)
144 {
145 retries--;
146 if (retries == 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 break;
154 }
155 std::this_thread::sleep_for(delay);
156 continue;
157 }
158 break;
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530159 }
Vishwanatha Subbanna2c129132017-08-29 21:12:01 +0530160 // Need to seek to START, else the poll returns immediately telling
161 // there is data to be read
Andrew Geissler11110872018-01-17 11:07:01 -0800162 auto r = lseek(fd, 0, SEEK_SET);
Vishwanatha Subbanna2c129132017-08-29 21:12:01 +0530163 if (r < 0)
164 {
165 log<level::ERR>("Failure seeking error file to START");
166 elog<ConfigFailure>(
167 phosphor::logging::org::open_power::OCC::Device::
168 ConfigFailure::CALLOUT_ERRNO(errno),
169 phosphor::logging::org::open_power::OCC::Device::
170 ConfigFailure::CALLOUT_DEVICE_PATH(file.c_str()));
171 }
Vishwanatha Subbannaee4d83d2017-06-29 18:35:00 +0530172 return std::string(data.get());
173}
174
175} // namespace occ
176} // namespace open_power