blob: b02816f7cb13550327337775fc665c245023069c [file] [log] [blame]
Cheng C Yang58b2b532019-05-31 00:19:45 +08001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16
Ed Tanous8a57ec02020-10-09 12:46:52 -070017#include <PSUEvent.hpp>
Ed Tanous6cb732a2021-02-18 15:33:51 -080018#include <SensorPaths.hpp>
James Feist8086aba2020-08-25 16:00:59 -070019#include <boost/asio/io_service.hpp>
20#include <boost/asio/read_until.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070021#include <boost/container/flat_map.hpp>
Patrick Williams0c42f402021-08-27 16:05:45 -050022#include <phosphor-logging/lg2.hpp>
Cheng C Yang58b2b532019-05-31 00:19:45 +080023#include <sdbusplus/asio/connection.hpp>
24#include <sdbusplus/asio/object_server.hpp>
James Feist38fb5982020-05-28 10:09:54 -070025
26#include <iostream>
27#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070028#include <stdexcept>
29#include <string>
30#include <utility>
31#include <variant>
32#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080033
34PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080035 sdbusplus::asio::object_server& objectServer,
36 std::shared_ptr<sdbusplus::asio::connection>& conn,
37 boost::asio::io_service& io, const std::string& psuName,
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000038 const PowerState& powerState,
Cheng C Yang58b2b532019-05-31 00:19:45 +080039 boost::container::flat_map<std::string, std::vector<std::string>>&
40 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080041 boost::container::flat_map<
42 std::string,
43 boost::container::flat_map<std::string, std::vector<std::string>>>&
44 groupEventPathList,
Lei YU7170a232021-02-04 16:19:27 +080045 const std::string& combineEventName, double pollRate) :
Cheng C Yang58b2b532019-05-31 00:19:45 +080046 objServer(objectServer)
47{
Ed Tanous6cb732a2021-02-18 15:33:51 -080048 std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName);
Cheng C Yang58b2b532019-05-31 00:19:45 +080049 eventInterface = objServer.add_interface(
Ed Tanous6cb732a2021-02-18 15:33:51 -080050 "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" +
Cheng C Yang58b2b532019-05-31 00:19:45 +080051 combineEventName,
52 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070053 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080054
55 if (!eventInterface->initialize())
56 {
57 std::cerr << "error initializing event interface\n";
58 }
59
60 std::shared_ptr<std::set<std::string>> combineEvent =
61 std::make_shared<std::set<std::string>>();
62 for (const auto& pathList : eventPathList)
63 {
Yong Li591b1e42019-12-19 17:46:31 +080064 std::shared_ptr<std::set<std::string>> assert =
65 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080066 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080067
Cheng C Yang58b2b532019-05-31 00:19:45 +080068 const std::string& eventName = pathList.first;
69 std::string eventPSUName = eventName + psuName;
70 for (const auto& path : pathList.second)
71 {
Yong Libf8b1da2020-04-15 16:32:50 +080072 auto p = std::make_shared<PSUSubEvent>(
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000073 eventInterface, path, conn, io, powerState, eventName,
74 eventName, assert, combineEvent, state, psuName, pollRate);
Yong Libf8b1da2020-04-15 16:32:50 +080075 p->setupRead();
76
77 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080078 asserts.emplace_back(assert);
79 states.emplace_back(state);
80 }
81 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080082
83 for (const auto& groupPathList : groupEventPathList)
84 {
85 for (const auto& pathList : groupPathList.second)
86 {
87 std::shared_ptr<std::set<std::string>> assert =
88 std::make_shared<std::set<std::string>>();
89 std::shared_ptr<bool> state = std::make_shared<bool>(false);
90
91 const std::string& groupEventName = pathList.first;
92 std::string eventPSUName = groupEventName + psuName;
93 for (const auto& path : pathList.second)
94 {
Yong Libf8b1da2020-04-15 16:32:50 +080095 auto p = std::make_shared<PSUSubEvent>(
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000096 eventInterface, path, conn, io, powerState, groupEventName,
Lei YU7170a232021-02-04 16:19:27 +080097 groupPathList.first, assert, combineEvent, state, psuName,
98 pollRate);
Yong Libf8b1da2020-04-15 16:32:50 +080099 p->setupRead();
100 events[eventPSUName].emplace_back(p);
101
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800102 asserts.emplace_back(assert);
103 states.emplace_back(state);
104 }
105 }
106 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800107}
108
109PSUCombineEvent::~PSUCombineEvent()
110{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800111 // Clear unique_ptr first
112 for (auto& event : events)
113 {
114 for (auto& subEventPtr : event.second)
115 {
116 subEventPtr.reset();
117 }
118 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800119 events.clear();
120 objServer.remove_interface(eventInterface);
121}
122
Cheng C Yang9b53a622019-08-27 22:13:58 +0800123static boost::container::flat_map<std::string,
124 std::pair<std::string, std::string>>
125 logID = {
126 {"PredictiveFailure",
127 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
128 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
129 {"Failure",
130 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
131 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800132 {"OpenBMC.0.1.PowerSupplyPowerLost",
133 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800134 {"FanFault",
135 {"OpenBMC.0.1.PowerSupplyFanFailed",
136 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000137 {"ConfigureError",
138 {"OpenBMC.0.1.PowerSupplyConfigurationError",
139 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800140
Cheng C Yang58b2b532019-05-31 00:19:45 +0800141PSUSubEvent::PSUSubEvent(
142 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800143 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000144 boost::asio::io_service& io, const PowerState& powerState,
145 const std::string& groupEventName, const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800146 std::shared_ptr<std::set<std::string>> asserts,
147 std::shared_ptr<std::set<std::string>> combineEvent,
Lei YU7170a232021-02-04 16:19:27 +0800148 std::shared_ptr<bool> state, const std::string& psuName, double pollRate) :
Yong Libf8b1da2020-04-15 16:32:50 +0800149 std::enable_shared_from_this<PSUSubEvent>(),
Ed Tanous8a57ec02020-10-09 12:46:52 -0700150 eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
151 combineEvent(std::move(combineEvent)), assertState(std::move(state)),
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000152 errCount(0), path(path), eventName(eventName), readState(powerState),
153 waitTimer(io), inputDev(io), psuName(psuName),
154 groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800155{
Lei YU7170a232021-02-04 16:19:27 +0800156 if (pollRate > 0.0)
157 {
158 eventPollMs = static_cast<unsigned int>(pollRate * 1000);
159 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800160 fd = open(path.c_str(), O_RDONLY);
161 if (fd < 0)
162 {
163 std::cerr << "PSU sub event failed to open file\n";
164 return;
165 }
166 inputDev.assign(fd);
167
Cheng C Yang5665db22019-07-12 00:57:30 +0800168 auto found = logID.find(eventName);
169 if (found == logID.end())
170 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800171 assertMessage.clear();
172 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800173 }
174 else
175 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800176 assertMessage = found->second.first;
177 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800178 }
179
180 auto fanPos = path.find("fan");
181 if (fanPos != std::string::npos)
182 {
183 fanName = path.substr(fanPos);
Ed Tanous8a57ec02020-10-09 12:46:52 -0700184 auto fanNamePos = fanName.find('_');
Cheng C Yang9b53a622019-08-27 22:13:58 +0800185 if (fanNamePos != std::string::npos)
186 {
187 fanName = fanName.substr(0, fanNamePos);
188 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800189 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800190}
191
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000192PSUSubEvent::~PSUSubEvent()
193{
194 waitTimer.cancel();
195 inputDev.close();
196}
197
Cheng C Yang58b2b532019-05-31 00:19:45 +0800198void PSUSubEvent::setupRead(void)
199{
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000200 if (!readingStateGood(readState))
201 {
202 // Deassert the event
203 updateValue(0);
204 restartRead();
205 return;
206 }
207
Yong Libf8b1da2020-04-15 16:32:50 +0800208 std::shared_ptr<boost::asio::streambuf> buffer =
209 std::make_shared<boost::asio::streambuf>();
210 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800211 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800212 inputDev, *buffer, '\n',
213 [weakRef, buffer](const boost::system::error_code& ec,
214 std::size_t /*bytes_transfered*/) {
215 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
216 if (self)
217 {
218 self->readBuf = buffer;
219 self->handleResponse(ec);
220 }
221 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800222}
223
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000224void PSUSubEvent::restartRead()
Cheng C Yang58b2b532019-05-31 00:19:45 +0800225{
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000226 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
227 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
228 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
229 if (ec == boost::asio::error::operation_aborted)
230 {
231 return;
232 }
233 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
234 if (self)
235 {
236 self->setupRead();
237 }
238 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800239}
240
241void PSUSubEvent::handleResponse(const boost::system::error_code& err)
242{
Yong Libf8b1da2020-04-15 16:32:50 +0800243 if ((err == boost::system::errc::bad_file_descriptor) ||
244 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800245 {
246 return;
247 }
Yong Libf8b1da2020-04-15 16:32:50 +0800248 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800249 if (!err)
250 {
251 std::string response;
252 try
253 {
254 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800255 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800256 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800257
258 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800259 errCount = 0;
260 }
261 catch (const std::invalid_argument&)
262 {
263 errCount++;
264 }
265 }
266 else
267 {
268 errCount++;
269 }
270 if (errCount >= warnAfterErrorCount)
271 {
272 if (errCount == warnAfterErrorCount)
273 {
274 std::cerr << "Failure to read event at " << path << "\n";
275 }
276 updateValue(0);
277 errCount++;
278 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800279 lseek(fd, 0, SEEK_SET);
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000280 restartRead();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800281}
282
283// Any of the sub events of one event is asserted, then the event will be
284// asserted. Only if none of the sub events are asserted, the event will be
285// deasserted.
286void PSUSubEvent::updateValue(const int& newValue)
287{
Josh Lehan833661a2020-03-04 17:35:41 -0800288 // Take no action if value already equal
289 // Same semantics as Sensor::updateValue(const double&)
290 if (newValue == value)
291 {
292 return;
293 }
294
Cheng C Yang58b2b532019-05-31 00:19:45 +0800295 if (newValue == 0)
296 {
Yong Li591b1e42019-12-19 17:46:31 +0800297 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800298 if (!(*asserts).empty())
299 {
Yong Li591b1e42019-12-19 17:46:31 +0800300 auto found = (*asserts).find(path);
301 if (found == (*asserts).end())
302 {
303 return;
304 }
305 (*asserts).erase(path);
306
Cheng C Yang58b2b532019-05-31 00:19:45 +0800307 return;
308 }
309 if (*assertState == true)
310 {
311 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800312 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800313 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800314 {
315 return;
316 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800317 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800318 if (!deassertMessage.empty())
319 {
320 // Fan Failed has two args
Cheng C Yang9b53a622019-08-27 22:13:58 +0800321 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
322 {
Patrick Williams0c42f402021-08-27 16:05:45 -0500323 lg2::info("{EVENT} deassert", "EVENT", eventName,
324 "REDFISH_MESSAGE_ID", deassertMessage,
325 "REDFISH_MESSAGE_ARGS",
326 (psuName + ',' + fanName));
Cheng C Yang9b53a622019-08-27 22:13:58 +0800327 }
328 else
329 {
Patrick Williams0c42f402021-08-27 16:05:45 -0500330 lg2::info("{EVENT} deassert", "EVENT", eventName,
331 "REDFISH_MESSAGE_ID", deassertMessage,
332 "REDFISH_MESSAGE_ARGS", psuName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800333 }
334 }
335
Cheng C Yang58b2b532019-05-31 00:19:45 +0800336 if ((*combineEvent).empty())
337 {
338 eventInterface->set_property("functional", true);
339 }
340 }
341 }
342 else
343 {
Paul Fertser34533542021-07-20 08:29:09 +0000344 std::cerr << "PSUSubEvent asserted by " << path << "\n";
345
Yong Li591b1e42019-12-19 17:46:31 +0800346 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800347 {
348 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800349 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800350 {
Yong Li3046a022020-04-03 13:01:02 +0800351 // For failure and configure error, spec requires a beep
352 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
353 (assertMessage ==
354 "OpenBMC.0.1.PowerSupplyConfigurationError"))
355 {
356 std::cout << " beep for " << assertMessage << "\n";
357 beep(beepPSUFailure);
358 }
359
Cheng C Yang5665db22019-07-12 00:57:30 +0800360 // Fan Failed has two args
Cheng C Yang9b53a622019-08-27 22:13:58 +0800361 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800362 {
Patrick Williams0c42f402021-08-27 16:05:45 -0500363 lg2::warning("{EVENT} assert", "EVENT", eventName,
364 "REDFISH_MESSAGE_ID", assertMessage,
365 "REDFISH_MESSAGE_ARGS",
366 (psuName + ',' + fanName));
Cheng C Yang5665db22019-07-12 00:57:30 +0800367 }
368 else
369 {
Patrick Williams0c42f402021-08-27 16:05:45 -0500370 lg2::warning("{EVENT} assert", "EVENT", eventName,
371 "REDFISH_MESSAGE_ID", assertMessage,
372 "REDFISH_MESSAGE_ARGS", psuName);
Cheng C Yang5665db22019-07-12 00:57:30 +0800373 }
374 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800375 if ((*combineEvent).empty())
376 {
377 eventInterface->set_property("functional", false);
378 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800379 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800380 }
Yong Li591b1e42019-12-19 17:46:31 +0800381 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800382 }
383 value = newValue;
384}
Yong Li3046a022020-04-03 13:01:02 +0800385
386void PSUSubEvent::beep(const uint8_t& beepPriority)
387{
388 systemBus->async_method_call(
389 [](boost::system::error_code ec) {
390 if (ec)
391 {
392 std::cerr << "beep error (ec = " << ec << ")\n";
393 return;
394 }
395 },
396 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
397 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
398}