blob: 23021d992fe1b56b31a937b03ab16f405ba826ce [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
Cheng C Yang5665db22019-07-12 00:57:30 +080017#include <systemd/sd-journal.h>
18
Ed Tanous8a57ec02020-10-09 12:46:52 -070019#include <PSUEvent.hpp>
Ed Tanous6cb732a2021-02-18 15:33:51 -080020#include <SensorPaths.hpp>
James Feist8086aba2020-08-25 16:00:59 -070021#include <boost/asio/io_service.hpp>
22#include <boost/asio/read_until.hpp>
Patrick Venture96e97db2019-10-31 13:44:38 -070023#include <boost/container/flat_map.hpp>
Cheng C Yang58b2b532019-05-31 00:19:45 +080024#include <sdbusplus/asio/connection.hpp>
25#include <sdbusplus/asio/object_server.hpp>
James Feist38fb5982020-05-28 10:09:54 -070026
27#include <iostream>
28#include <memory>
Patrick Venture96e97db2019-10-31 13:44:38 -070029#include <stdexcept>
30#include <string>
31#include <utility>
32#include <variant>
33#include <vector>
Cheng C Yang58b2b532019-05-31 00:19:45 +080034
35PSUCombineEvent::PSUCombineEvent(
Yong Li3046a022020-04-03 13:01:02 +080036 sdbusplus::asio::object_server& objectServer,
37 std::shared_ptr<sdbusplus::asio::connection>& conn,
38 boost::asio::io_service& io, const std::string& psuName,
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000039 const PowerState& powerState,
Cheng C Yang58b2b532019-05-31 00:19:45 +080040 boost::container::flat_map<std::string, std::vector<std::string>>&
41 eventPathList,
Cheng C Yang202a1ff2020-01-09 09:34:22 +080042 boost::container::flat_map<
43 std::string,
44 boost::container::flat_map<std::string, std::vector<std::string>>>&
45 groupEventPathList,
Lei YU7170a232021-02-04 16:19:27 +080046 const std::string& combineEventName, double pollRate) :
Cheng C Yang58b2b532019-05-31 00:19:45 +080047 objServer(objectServer)
48{
Ed Tanous6cb732a2021-02-18 15:33:51 -080049 std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName);
Cheng C Yang58b2b532019-05-31 00:19:45 +080050 eventInterface = objServer.add_interface(
Ed Tanous6cb732a2021-02-18 15:33:51 -080051 "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" +
Cheng C Yang58b2b532019-05-31 00:19:45 +080052 combineEventName,
53 "xyz.openbmc_project.State.Decorator.OperationalStatus");
James Feistb6c0b912019-07-09 12:21:44 -070054 eventInterface->register_property("functional", true);
Cheng C Yang58b2b532019-05-31 00:19:45 +080055
56 if (!eventInterface->initialize())
57 {
58 std::cerr << "error initializing event interface\n";
59 }
60
61 std::shared_ptr<std::set<std::string>> combineEvent =
62 std::make_shared<std::set<std::string>>();
63 for (const auto& pathList : eventPathList)
64 {
Yong Li591b1e42019-12-19 17:46:31 +080065 std::shared_ptr<std::set<std::string>> assert =
66 std::make_shared<std::set<std::string>>();
Cheng C Yang202a1ff2020-01-09 09:34:22 +080067 std::shared_ptr<bool> state = std::make_shared<bool>(false);
Yong Li591b1e42019-12-19 17:46:31 +080068
Cheng C Yang58b2b532019-05-31 00:19:45 +080069 const std::string& eventName = pathList.first;
70 std::string eventPSUName = eventName + psuName;
71 for (const auto& path : pathList.second)
72 {
Yong Libf8b1da2020-04-15 16:32:50 +080073 auto p = std::make_shared<PSUSubEvent>(
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000074 eventInterface, path, conn, io, powerState, eventName,
75 eventName, assert, combineEvent, state, psuName, pollRate);
Yong Libf8b1da2020-04-15 16:32:50 +080076 p->setupRead();
77
78 events[eventPSUName].emplace_back(p);
Cheng C Yang58b2b532019-05-31 00:19:45 +080079 asserts.emplace_back(assert);
80 states.emplace_back(state);
81 }
82 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +080083
84 for (const auto& groupPathList : groupEventPathList)
85 {
86 for (const auto& pathList : groupPathList.second)
87 {
88 std::shared_ptr<std::set<std::string>> assert =
89 std::make_shared<std::set<std::string>>();
90 std::shared_ptr<bool> state = std::make_shared<bool>(false);
91
92 const std::string& groupEventName = pathList.first;
93 std::string eventPSUName = groupEventName + psuName;
94 for (const auto& path : pathList.second)
95 {
Yong Libf8b1da2020-04-15 16:32:50 +080096 auto p = std::make_shared<PSUSubEvent>(
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +000097 eventInterface, path, conn, io, powerState, groupEventName,
Lei YU7170a232021-02-04 16:19:27 +080098 groupPathList.first, assert, combineEvent, state, psuName,
99 pollRate);
Yong Libf8b1da2020-04-15 16:32:50 +0800100 p->setupRead();
101 events[eventPSUName].emplace_back(p);
102
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800103 asserts.emplace_back(assert);
104 states.emplace_back(state);
105 }
106 }
107 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800108}
109
110PSUCombineEvent::~PSUCombineEvent()
111{
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800112 // Clear unique_ptr first
113 for (auto& event : events)
114 {
115 for (auto& subEventPtr : event.second)
116 {
117 subEventPtr.reset();
118 }
119 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800120 events.clear();
121 objServer.remove_interface(eventInterface);
122}
123
Cheng C Yang9b53a622019-08-27 22:13:58 +0800124static boost::container::flat_map<std::string,
125 std::pair<std::string, std::string>>
126 logID = {
127 {"PredictiveFailure",
128 {"OpenBMC.0.1.PowerSupplyFailurePredicted",
129 "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
130 {"Failure",
131 {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
132 {"ACLost",
Cheng C Yangbb732ef2019-09-18 16:25:58 +0800133 {"OpenBMC.0.1.PowerSupplyPowerLost",
134 "OpenBMC.0.1.PowerSupplyPowerRestored"}},
Cheng C Yang9b53a622019-08-27 22:13:58 +0800135 {"FanFault",
136 {"OpenBMC.0.1.PowerSupplyFanFailed",
137 "OpenBMC.0.1.PowerSupplyFanRecovered"}},
jayaprakash Mutyalad12f23e2019-12-03 23:03:52 +0000138 {"ConfigureError",
139 {"OpenBMC.0.1.PowerSupplyConfigurationError",
140 "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
Cheng C Yang5665db22019-07-12 00:57:30 +0800141
Cheng C Yang58b2b532019-05-31 00:19:45 +0800142PSUSubEvent::PSUSubEvent(
143 std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
Yong Li3046a022020-04-03 13:01:02 +0800144 const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000145 boost::asio::io_service& io, const PowerState& powerState,
146 const std::string& groupEventName, const std::string& eventName,
Cheng C Yang58b2b532019-05-31 00:19:45 +0800147 std::shared_ptr<std::set<std::string>> asserts,
148 std::shared_ptr<std::set<std::string>> combineEvent,
Lei YU7170a232021-02-04 16:19:27 +0800149 std::shared_ptr<bool> state, const std::string& psuName, double pollRate) :
Yong Libf8b1da2020-04-15 16:32:50 +0800150 std::enable_shared_from_this<PSUSubEvent>(),
Ed Tanous8a57ec02020-10-09 12:46:52 -0700151 eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
152 combineEvent(std::move(combineEvent)), assertState(std::move(state)),
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000153 errCount(0), path(path), eventName(eventName), readState(powerState),
154 waitTimer(io), inputDev(io), psuName(psuName),
155 groupEventName(groupEventName), systemBus(conn)
Cheng C Yang58b2b532019-05-31 00:19:45 +0800156{
Lei YU7170a232021-02-04 16:19:27 +0800157 if (pollRate > 0.0)
158 {
159 eventPollMs = static_cast<unsigned int>(pollRate * 1000);
160 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800161 fd = open(path.c_str(), O_RDONLY);
162 if (fd < 0)
163 {
164 std::cerr << "PSU sub event failed to open file\n";
165 return;
166 }
167 inputDev.assign(fd);
168
Cheng C Yang5665db22019-07-12 00:57:30 +0800169 auto found = logID.find(eventName);
170 if (found == logID.end())
171 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800172 assertMessage.clear();
173 deassertMessage.clear();
Cheng C Yang5665db22019-07-12 00:57:30 +0800174 }
175 else
176 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800177 assertMessage = found->second.first;
178 deassertMessage = found->second.second;
Cheng C Yang5665db22019-07-12 00:57:30 +0800179 }
180
181 auto fanPos = path.find("fan");
182 if (fanPos != std::string::npos)
183 {
184 fanName = path.substr(fanPos);
Ed Tanous8a57ec02020-10-09 12:46:52 -0700185 auto fanNamePos = fanName.find('_');
Cheng C Yang9b53a622019-08-27 22:13:58 +0800186 if (fanNamePos != std::string::npos)
187 {
188 fanName = fanName.substr(0, fanNamePos);
189 }
Cheng C Yang5665db22019-07-12 00:57:30 +0800190 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800191}
192
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000193PSUSubEvent::~PSUSubEvent()
194{
195 waitTimer.cancel();
196 inputDev.close();
197}
198
Cheng C Yang58b2b532019-05-31 00:19:45 +0800199void PSUSubEvent::setupRead(void)
200{
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000201 if (!readingStateGood(readState))
202 {
203 // Deassert the event
204 updateValue(0);
205 restartRead();
206 return;
207 }
208
Yong Libf8b1da2020-04-15 16:32:50 +0800209 std::shared_ptr<boost::asio::streambuf> buffer =
210 std::make_shared<boost::asio::streambuf>();
211 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800212 boost::asio::async_read_until(
Yong Libf8b1da2020-04-15 16:32:50 +0800213 inputDev, *buffer, '\n',
214 [weakRef, buffer](const boost::system::error_code& ec,
215 std::size_t /*bytes_transfered*/) {
216 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
217 if (self)
218 {
219 self->readBuf = buffer;
220 self->handleResponse(ec);
221 }
222 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800223}
224
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000225void PSUSubEvent::restartRead()
Cheng C Yang58b2b532019-05-31 00:19:45 +0800226{
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000227 std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
228 waitTimer.expires_from_now(boost::posix_time::milliseconds(eventPollMs));
229 waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
230 if (ec == boost::asio::error::operation_aborted)
231 {
232 return;
233 }
234 std::shared_ptr<PSUSubEvent> self = weakRef.lock();
235 if (self)
236 {
237 self->setupRead();
238 }
239 });
Cheng C Yang58b2b532019-05-31 00:19:45 +0800240}
241
242void PSUSubEvent::handleResponse(const boost::system::error_code& err)
243{
Yong Libf8b1da2020-04-15 16:32:50 +0800244 if ((err == boost::system::errc::bad_file_descriptor) ||
245 (err == boost::asio::error::misc_errors::not_found))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800246 {
247 return;
248 }
Yong Libf8b1da2020-04-15 16:32:50 +0800249 std::istream responseStream(readBuf.get());
Cheng C Yang58b2b532019-05-31 00:19:45 +0800250 if (!err)
251 {
252 std::string response;
253 try
254 {
255 std::getline(responseStream, response);
Josh Lehan833661a2020-03-04 17:35:41 -0800256 int nvalue = std::stoi(response);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800257 responseStream.clear();
Josh Lehan833661a2020-03-04 17:35:41 -0800258
259 updateValue(nvalue);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800260 errCount = 0;
261 }
262 catch (const std::invalid_argument&)
263 {
264 errCount++;
265 }
266 }
267 else
268 {
269 errCount++;
270 }
271 if (errCount >= warnAfterErrorCount)
272 {
273 if (errCount == warnAfterErrorCount)
274 {
275 std::cerr << "Failure to read event at " << path << "\n";
276 }
277 updateValue(0);
278 errCount++;
279 }
Cheng C Yanga97f1342020-02-11 15:10:41 +0800280 lseek(fd, 0, SEEK_SET);
Konstantin Aladyshevc7a1ae62021-04-30 08:50:43 +0000281 restartRead();
Cheng C Yang58b2b532019-05-31 00:19:45 +0800282}
283
284// Any of the sub events of one event is asserted, then the event will be
285// asserted. Only if none of the sub events are asserted, the event will be
286// deasserted.
287void PSUSubEvent::updateValue(const int& newValue)
288{
Josh Lehan833661a2020-03-04 17:35:41 -0800289 // Take no action if value already equal
290 // Same semantics as Sensor::updateValue(const double&)
291 if (newValue == value)
292 {
293 return;
294 }
295
Cheng C Yang58b2b532019-05-31 00:19:45 +0800296 if (newValue == 0)
297 {
Yong Li591b1e42019-12-19 17:46:31 +0800298 // log deassert only after all asserts are gone
Cheng C Yang58b2b532019-05-31 00:19:45 +0800299 if (!(*asserts).empty())
300 {
Yong Li591b1e42019-12-19 17:46:31 +0800301 auto found = (*asserts).find(path);
302 if (found == (*asserts).end())
303 {
304 return;
305 }
306 (*asserts).erase(path);
307
Cheng C Yang58b2b532019-05-31 00:19:45 +0800308 return;
309 }
310 if (*assertState == true)
311 {
312 *assertState = false;
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800313 auto foundCombine = (*combineEvent).find(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800314 if (foundCombine == (*combineEvent).end())
Cheng C Yang58b2b532019-05-31 00:19:45 +0800315 {
316 return;
317 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800318 (*combineEvent).erase(groupEventName);
Cheng C Yang9b53a622019-08-27 22:13:58 +0800319 if (!deassertMessage.empty())
320 {
321 // Fan Failed has two args
322 std::string sendMessage = eventName + " deassert";
323 if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
324 {
325 sd_journal_send(
326 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800327 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800328 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
329 psuName.c_str(), fanName.c_str(), NULL);
330 }
331 else
332 {
333 sd_journal_send(
334 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800335 LOG_INFO, "REDFISH_MESSAGE_ID=%s",
Cheng C Yang9b53a622019-08-27 22:13:58 +0800336 deassertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
337 psuName.c_str(), NULL);
338 }
339 }
340
Cheng C Yang58b2b532019-05-31 00:19:45 +0800341 if ((*combineEvent).empty())
342 {
343 eventInterface->set_property("functional", true);
344 }
345 }
346 }
347 else
348 {
Paul Fertser34533542021-07-20 08:29:09 +0000349 std::cerr << "PSUSubEvent asserted by " << path << "\n";
350
Yong Li591b1e42019-12-19 17:46:31 +0800351 if ((*assertState == false) && ((*asserts).empty()))
Cheng C Yang58b2b532019-05-31 00:19:45 +0800352 {
353 *assertState = true;
Cheng C Yang9b53a622019-08-27 22:13:58 +0800354 if (!assertMessage.empty())
Cheng C Yang5665db22019-07-12 00:57:30 +0800355 {
Yong Li3046a022020-04-03 13:01:02 +0800356 // For failure and configure error, spec requires a beep
357 if ((assertMessage == "OpenBMC.0.1.PowerSupplyFailed") ||
358 (assertMessage ==
359 "OpenBMC.0.1.PowerSupplyConfigurationError"))
360 {
361 std::cout << " beep for " << assertMessage << "\n";
362 beep(beepPSUFailure);
363 }
364
Cheng C Yang5665db22019-07-12 00:57:30 +0800365 // Fan Failed has two args
366 std::string sendMessage = eventName + " assert";
Cheng C Yang9b53a622019-08-27 22:13:58 +0800367 if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
Cheng C Yang5665db22019-07-12 00:57:30 +0800368 {
Cheng C Yang9b53a622019-08-27 22:13:58 +0800369 sd_journal_send(
370 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800371 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
372 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s,%s",
373 psuName.c_str(), fanName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800374 }
375 else
376 {
377 sd_journal_send(
378 "MESSAGE=%s", sendMessage.c_str(), "PRIORITY=%i",
Yong Li591b1e42019-12-19 17:46:31 +0800379 LOG_WARNING, "REDFISH_MESSAGE_ID=%s",
380 assertMessage.c_str(), "REDFISH_MESSAGE_ARGS=%s",
381 psuName.c_str(), NULL);
Cheng C Yang5665db22019-07-12 00:57:30 +0800382 }
383 }
Cheng C Yang58b2b532019-05-31 00:19:45 +0800384 if ((*combineEvent).empty())
385 {
386 eventInterface->set_property("functional", false);
387 }
Cheng C Yang202a1ff2020-01-09 09:34:22 +0800388 (*combineEvent).emplace(groupEventName);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800389 }
Yong Li591b1e42019-12-19 17:46:31 +0800390 (*asserts).emplace(path);
Cheng C Yang58b2b532019-05-31 00:19:45 +0800391 }
392 value = newValue;
393}
Yong Li3046a022020-04-03 13:01:02 +0800394
395void PSUSubEvent::beep(const uint8_t& beepPriority)
396{
397 systemBus->async_method_call(
398 [](boost::system::error_code ec) {
399 if (ec)
400 {
401 std::cerr << "beep error (ec = " << ec << ")\n";
402 return;
403 }
404 },
405 "xyz.openbmc_project.BeepCode", "/xyz/openbmc_project/BeepCode",
406 "xyz.openbmc_project.BeepCode", "Beep", uint8_t(beepPriority));
407}