1d7be555eSGeorge Liu /*
2d7be555eSGeorge Liu // Copyright (c) 2019 Intel Corporation
3d7be555eSGeorge Liu //
4d7be555eSGeorge Liu // Licensed under the Apache License, Version 2.0 (the "License");
5d7be555eSGeorge Liu // you may not use this file except in compliance with the License.
6d7be555eSGeorge Liu // You may obtain a copy of the License at
7d7be555eSGeorge Liu //
8d7be555eSGeorge Liu // http://www.apache.org/licenses/LICENSE-2.0
9d7be555eSGeorge Liu //
10d7be555eSGeorge Liu // Unless required by applicable law or agreed to in writing, software
11d7be555eSGeorge Liu // distributed under the License is distributed on an "AS IS" BASIS,
12d7be555eSGeorge Liu // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7be555eSGeorge Liu // See the License for the specific language governing permissions and
14d7be555eSGeorge Liu // limitations under the License.
15d7be555eSGeorge Liu */
16d7be555eSGeorge Liu
17d7be555eSGeorge Liu #include "PSUEvent.hpp"
18d7be555eSGeorge Liu
19d7be555eSGeorge Liu #include "SensorPaths.hpp"
20d7be555eSGeorge Liu #include "Utils.hpp"
21d7be555eSGeorge Liu
22d7be555eSGeorge Liu #include <boost/asio/buffer.hpp>
23d7be555eSGeorge Liu #include <boost/asio/error.hpp>
24d7be555eSGeorge Liu #include <boost/asio/io_context.hpp>
25d7be555eSGeorge Liu #include <boost/asio/random_access_file.hpp>
26d7be555eSGeorge Liu #include <boost/container/flat_map.hpp>
27d7be555eSGeorge Liu #include <phosphor-logging/lg2.hpp>
28d7be555eSGeorge Liu #include <sdbusplus/asio/connection.hpp>
29d7be555eSGeorge Liu #include <sdbusplus/asio/object_server.hpp>
30d7be555eSGeorge Liu
31d7be555eSGeorge Liu #include <array>
32d7be555eSGeorge Liu #include <chrono>
33d7be555eSGeorge Liu #include <cstddef>
34d7be555eSGeorge Liu #include <memory>
35d7be555eSGeorge Liu #include <set>
36d7be555eSGeorge Liu #include <stdexcept>
37d7be555eSGeorge Liu #include <string>
38d7be555eSGeorge Liu #include <utility>
39d7be555eSGeorge Liu #include <vector>
40d7be555eSGeorge Liu
PSUCombineEvent(sdbusplus::asio::object_server & objectServer,std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & io,const std::string & psuName,const PowerState & powerState,EventPathList & eventPathList,GroupEventPathList & groupEventPathList,const std::string & combineEventName,double pollRate)41d7be555eSGeorge Liu PSUCombineEvent::PSUCombineEvent(
42d7be555eSGeorge Liu sdbusplus::asio::object_server& objectServer,
43d7be555eSGeorge Liu std::shared_ptr<sdbusplus::asio::connection>& conn,
44d7be555eSGeorge Liu boost::asio::io_context& io, const std::string& psuName,
45d7be555eSGeorge Liu const PowerState& powerState, EventPathList& eventPathList,
46d7be555eSGeorge Liu GroupEventPathList& groupEventPathList, const std::string& combineEventName,
47d7be555eSGeorge Liu double pollRate) : objServer(objectServer)
48d7be555eSGeorge Liu {
49d7be555eSGeorge Liu std::string psuNameEscaped = sensor_paths::escapePathForDbus(psuName);
50d7be555eSGeorge Liu eventInterface = objServer.add_interface(
51d7be555eSGeorge Liu "/xyz/openbmc_project/State/Decorator/" + psuNameEscaped + "_" +
52d7be555eSGeorge Liu combineEventName,
53d7be555eSGeorge Liu "xyz.openbmc_project.State.Decorator.OperationalStatus");
54d7be555eSGeorge Liu eventInterface->register_property("functional", true);
55d7be555eSGeorge Liu
56d7be555eSGeorge Liu if (!eventInterface->initialize())
57d7be555eSGeorge Liu {
58*4155a5a8SGeorge Liu lg2::error("error initializing event interface");
59d7be555eSGeorge Liu }
60d7be555eSGeorge Liu
61d7be555eSGeorge Liu std::shared_ptr<std::set<std::string>> combineEvent =
62d7be555eSGeorge Liu std::make_shared<std::set<std::string>>();
63d7be555eSGeorge Liu for (const auto& [eventName, paths] : eventPathList)
64d7be555eSGeorge Liu {
65d7be555eSGeorge Liu std::shared_ptr<std::set<std::string>> assert =
66d7be555eSGeorge Liu std::make_shared<std::set<std::string>>();
67d7be555eSGeorge Liu std::shared_ptr<bool> state = std::make_shared<bool>(false);
68d7be555eSGeorge Liu
69d7be555eSGeorge Liu std::string eventPSUName = eventName + psuName;
70d7be555eSGeorge Liu for (const auto& path : paths)
71d7be555eSGeorge Liu {
72d7be555eSGeorge Liu auto p = std::make_shared<PSUSubEvent>(
73d7be555eSGeorge Liu eventInterface, path, conn, io, powerState, eventName,
74d7be555eSGeorge Liu eventName, assert, combineEvent, state, psuName, pollRate);
75d7be555eSGeorge Liu p->setupRead();
76d7be555eSGeorge Liu
77d7be555eSGeorge Liu events[eventPSUName].emplace_back(p);
78d7be555eSGeorge Liu asserts.emplace_back(assert);
79d7be555eSGeorge Liu states.emplace_back(state);
80d7be555eSGeorge Liu }
81d7be555eSGeorge Liu }
82d7be555eSGeorge Liu
83d7be555eSGeorge Liu for (const auto& [eventName, groupEvents] : groupEventPathList)
84d7be555eSGeorge Liu {
85d7be555eSGeorge Liu for (const auto& [groupEventName, paths] : groupEvents)
86d7be555eSGeorge Liu {
87d7be555eSGeorge Liu std::shared_ptr<std::set<std::string>> assert =
88d7be555eSGeorge Liu std::make_shared<std::set<std::string>>();
89d7be555eSGeorge Liu std::shared_ptr<bool> state = std::make_shared<bool>(false);
90d7be555eSGeorge Liu
91d7be555eSGeorge Liu std::string eventPSUName = groupEventName + psuName;
92d7be555eSGeorge Liu for (const auto& path : paths)
93d7be555eSGeorge Liu {
94d7be555eSGeorge Liu auto p = std::make_shared<PSUSubEvent>(
95d7be555eSGeorge Liu eventInterface, path, conn, io, powerState, groupEventName,
96d7be555eSGeorge Liu eventName, assert, combineEvent, state, psuName, pollRate);
97d7be555eSGeorge Liu p->setupRead();
98d7be555eSGeorge Liu events[eventPSUName].emplace_back(p);
99d7be555eSGeorge Liu
100d7be555eSGeorge Liu asserts.emplace_back(assert);
101d7be555eSGeorge Liu states.emplace_back(state);
102d7be555eSGeorge Liu }
103d7be555eSGeorge Liu }
104d7be555eSGeorge Liu }
105d7be555eSGeorge Liu }
106d7be555eSGeorge Liu
~PSUCombineEvent()107d7be555eSGeorge Liu PSUCombineEvent::~PSUCombineEvent()
108d7be555eSGeorge Liu {
109d7be555eSGeorge Liu // Clear unique_ptr first
110d7be555eSGeorge Liu for (auto& [psuName, subEvents] : events)
111d7be555eSGeorge Liu {
112d7be555eSGeorge Liu for (auto& subEventPtr : subEvents)
113d7be555eSGeorge Liu {
114d7be555eSGeorge Liu subEventPtr.reset();
115d7be555eSGeorge Liu }
116d7be555eSGeorge Liu }
117d7be555eSGeorge Liu events.clear();
118d7be555eSGeorge Liu objServer.remove_interface(eventInterface);
119d7be555eSGeorge Liu }
120d7be555eSGeorge Liu
121d7be555eSGeorge Liu static boost::container::flat_map<std::string,
122d7be555eSGeorge Liu std::pair<std::string, std::string>>
123d7be555eSGeorge Liu logID = {
124d7be555eSGeorge Liu {"PredictiveFailure",
125d7be555eSGeorge Liu {"OpenBMC.0.1.PowerSupplyFailurePredicted",
126d7be555eSGeorge Liu "OpenBMC.0.1.PowerSupplyPredictedFailureRecovered"}},
127d7be555eSGeorge Liu {"Failure",
128d7be555eSGeorge Liu {"OpenBMC.0.1.PowerSupplyFailed", "OpenBMC.0.1.PowerSupplyRecovered"}},
129d7be555eSGeorge Liu {"ACLost",
130d7be555eSGeorge Liu {"OpenBMC.0.1.PowerSupplyPowerLost",
131d7be555eSGeorge Liu "OpenBMC.0.1.PowerSupplyPowerRestored"}},
132d7be555eSGeorge Liu {"FanFault",
133d7be555eSGeorge Liu {"OpenBMC.0.1.PowerSupplyFanFailed",
134d7be555eSGeorge Liu "OpenBMC.0.1.PowerSupplyFanRecovered"}},
135d7be555eSGeorge Liu {"ConfigureError",
136d7be555eSGeorge Liu {"OpenBMC.0.1.PowerSupplyConfigurationError",
137d7be555eSGeorge Liu "OpenBMC.0.1.PowerSupplyConfigurationErrorRecovered"}}};
138d7be555eSGeorge Liu
PSUSubEvent(std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,const std::string & path,std::shared_ptr<sdbusplus::asio::connection> & conn,boost::asio::io_context & io,const PowerState & powerState,const std::string & groupEventName,const std::string & eventName,std::shared_ptr<std::set<std::string>> asserts,std::shared_ptr<std::set<std::string>> combineEvent,std::shared_ptr<bool> state,const std::string & psuName,double pollRate)139d7be555eSGeorge Liu PSUSubEvent::PSUSubEvent(
140d7be555eSGeorge Liu std::shared_ptr<sdbusplus::asio::dbus_interface> eventInterface,
141d7be555eSGeorge Liu const std::string& path, std::shared_ptr<sdbusplus::asio::connection>& conn,
142d7be555eSGeorge Liu boost::asio::io_context& io, const PowerState& powerState,
143d7be555eSGeorge Liu const std::string& groupEventName, const std::string& eventName,
144d7be555eSGeorge Liu std::shared_ptr<std::set<std::string>> asserts,
145d7be555eSGeorge Liu std::shared_ptr<std::set<std::string>> combineEvent,
146d7be555eSGeorge Liu std::shared_ptr<bool> state, const std::string& psuName, double pollRate) :
147d7be555eSGeorge Liu eventInterface(std::move(eventInterface)), asserts(std::move(asserts)),
148d7be555eSGeorge Liu combineEvent(std::move(combineEvent)), assertState(std::move(state)),
149d7be555eSGeorge Liu path(path), eventName(eventName), readState(powerState), waitTimer(io),
150d7be555eSGeorge Liu
151d7be555eSGeorge Liu inputDev(io, path, boost::asio::random_access_file::read_only),
152d7be555eSGeorge Liu psuName(psuName), groupEventName(groupEventName), systemBus(conn)
153d7be555eSGeorge Liu {
154d7be555eSGeorge Liu buffer = std::make_shared<std::array<char, 128>>();
155d7be555eSGeorge Liu if (pollRate > 0.0)
156d7be555eSGeorge Liu {
157d7be555eSGeorge Liu eventPollMs = static_cast<unsigned int>(pollRate * 1000);
158d7be555eSGeorge Liu }
159d7be555eSGeorge Liu
160d7be555eSGeorge Liu auto found = logID.find(eventName);
161d7be555eSGeorge Liu if (found == logID.end())
162d7be555eSGeorge Liu {
163d7be555eSGeorge Liu assertMessage.clear();
164d7be555eSGeorge Liu deassertMessage.clear();
165d7be555eSGeorge Liu }
166d7be555eSGeorge Liu else
167d7be555eSGeorge Liu {
168d7be555eSGeorge Liu assertMessage = found->second.first;
169d7be555eSGeorge Liu deassertMessage = found->second.second;
170d7be555eSGeorge Liu }
171d7be555eSGeorge Liu
172d7be555eSGeorge Liu auto fanPos = path.find("fan");
173d7be555eSGeorge Liu if (fanPos != std::string::npos)
174d7be555eSGeorge Liu {
175d7be555eSGeorge Liu fanName = path.substr(fanPos);
176d7be555eSGeorge Liu auto fanNamePos = fanName.find('_');
177d7be555eSGeorge Liu if (fanNamePos != std::string::npos)
178d7be555eSGeorge Liu {
179d7be555eSGeorge Liu fanName = fanName.substr(0, fanNamePos);
180d7be555eSGeorge Liu }
181d7be555eSGeorge Liu }
182d7be555eSGeorge Liu }
183d7be555eSGeorge Liu
~PSUSubEvent()184d7be555eSGeorge Liu PSUSubEvent::~PSUSubEvent()
185d7be555eSGeorge Liu {
186d7be555eSGeorge Liu waitTimer.cancel();
187d7be555eSGeorge Liu inputDev.close();
188d7be555eSGeorge Liu }
189d7be555eSGeorge Liu
setupRead()190d7be555eSGeorge Liu void PSUSubEvent::setupRead()
191d7be555eSGeorge Liu {
192d7be555eSGeorge Liu if (!readingStateGood(readState))
193d7be555eSGeorge Liu {
194d7be555eSGeorge Liu // Deassert the event
195d7be555eSGeorge Liu updateValue(0);
196d7be555eSGeorge Liu restartRead();
197d7be555eSGeorge Liu return;
198d7be555eSGeorge Liu }
199d7be555eSGeorge Liu if (!buffer)
200d7be555eSGeorge Liu {
201*4155a5a8SGeorge Liu lg2::error("Buffer was invalid?");
202d7be555eSGeorge Liu return;
203d7be555eSGeorge Liu }
204d7be555eSGeorge Liu
205d7be555eSGeorge Liu std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
206d7be555eSGeorge Liu inputDev.async_read_some_at(
207d7be555eSGeorge Liu 0, boost::asio::buffer(buffer->data(), buffer->size() - 1),
208d7be555eSGeorge Liu [weakRef, buffer{buffer}](const boost::system::error_code& ec,
209d7be555eSGeorge Liu std::size_t bytesTransferred) {
210d7be555eSGeorge Liu std::shared_ptr<PSUSubEvent> self = weakRef.lock();
211d7be555eSGeorge Liu if (self)
212d7be555eSGeorge Liu {
213d7be555eSGeorge Liu self->handleResponse(ec, bytesTransferred);
214d7be555eSGeorge Liu }
215d7be555eSGeorge Liu });
216d7be555eSGeorge Liu }
217d7be555eSGeorge Liu
restartRead()218d7be555eSGeorge Liu void PSUSubEvent::restartRead()
219d7be555eSGeorge Liu {
220d7be555eSGeorge Liu std::weak_ptr<PSUSubEvent> weakRef = weak_from_this();
221d7be555eSGeorge Liu waitTimer.expires_after(std::chrono::milliseconds(eventPollMs));
222d7be555eSGeorge Liu waitTimer.async_wait([weakRef](const boost::system::error_code& ec) {
223d7be555eSGeorge Liu if (ec == boost::asio::error::operation_aborted)
224d7be555eSGeorge Liu {
225d7be555eSGeorge Liu return;
226d7be555eSGeorge Liu }
227d7be555eSGeorge Liu std::shared_ptr<PSUSubEvent> self = weakRef.lock();
228d7be555eSGeorge Liu if (self)
229d7be555eSGeorge Liu {
230d7be555eSGeorge Liu self->setupRead();
231d7be555eSGeorge Liu }
232d7be555eSGeorge Liu });
233d7be555eSGeorge Liu }
234d7be555eSGeorge Liu
handleResponse(const boost::system::error_code & err,size_t bytesTransferred)235d7be555eSGeorge Liu void PSUSubEvent::handleResponse(const boost::system::error_code& err,
236d7be555eSGeorge Liu size_t bytesTransferred)
237d7be555eSGeorge Liu {
238d7be555eSGeorge Liu if (err == boost::asio::error::operation_aborted)
239d7be555eSGeorge Liu {
240d7be555eSGeorge Liu return;
241d7be555eSGeorge Liu }
242d7be555eSGeorge Liu
243d7be555eSGeorge Liu if ((err == boost::system::errc::bad_file_descriptor) ||
244d7be555eSGeorge Liu (err == boost::asio::error::misc_errors::not_found))
245d7be555eSGeorge Liu {
246d7be555eSGeorge Liu return;
247d7be555eSGeorge Liu }
248d7be555eSGeorge Liu if (!buffer)
249d7be555eSGeorge Liu {
250*4155a5a8SGeorge Liu lg2::error("Buffer was invalid?");
251d7be555eSGeorge Liu return;
252d7be555eSGeorge Liu }
253d7be555eSGeorge Liu // null terminate the string so we don't walk off the end
254d7be555eSGeorge Liu std::array<char, 128>& bufferRef = *buffer;
255d7be555eSGeorge Liu bufferRef[bytesTransferred] = '\0';
256d7be555eSGeorge Liu
257d7be555eSGeorge Liu if (!err)
258d7be555eSGeorge Liu {
259d7be555eSGeorge Liu try
260d7be555eSGeorge Liu {
261d7be555eSGeorge Liu int nvalue = std::stoi(bufferRef.data());
262d7be555eSGeorge Liu updateValue(nvalue);
263d7be555eSGeorge Liu errCount = 0;
264d7be555eSGeorge Liu }
265d7be555eSGeorge Liu catch (const std::invalid_argument&)
266d7be555eSGeorge Liu {
267d7be555eSGeorge Liu errCount++;
268d7be555eSGeorge Liu }
269d7be555eSGeorge Liu }
270d7be555eSGeorge Liu else
271d7be555eSGeorge Liu {
272d7be555eSGeorge Liu errCount++;
273d7be555eSGeorge Liu }
274d7be555eSGeorge Liu if (errCount >= warnAfterErrorCount)
275d7be555eSGeorge Liu {
276d7be555eSGeorge Liu if (errCount == warnAfterErrorCount)
277d7be555eSGeorge Liu {
278*4155a5a8SGeorge Liu lg2::error("Failure to read event at '{PATH}'", "PATH", path);
279d7be555eSGeorge Liu }
280d7be555eSGeorge Liu updateValue(0);
281d7be555eSGeorge Liu errCount++;
282d7be555eSGeorge Liu }
283d7be555eSGeorge Liu restartRead();
284d7be555eSGeorge Liu }
285d7be555eSGeorge Liu
286d7be555eSGeorge Liu // Any of the sub events of one event is asserted, then the event will be
287d7be555eSGeorge Liu // asserted. Only if none of the sub events are asserted, the event will be
288d7be555eSGeorge Liu // deasserted.
updateValue(const int & newValue)289d7be555eSGeorge Liu void PSUSubEvent::updateValue(const int& newValue)
290d7be555eSGeorge Liu {
291d7be555eSGeorge Liu // Take no action if value already equal
292d7be555eSGeorge Liu // Same semantics as Sensor::updateValue(const double&)
293d7be555eSGeorge Liu if (newValue == value)
294d7be555eSGeorge Liu {
295d7be555eSGeorge Liu return;
296d7be555eSGeorge Liu }
297d7be555eSGeorge Liu
298d7be555eSGeorge Liu if (newValue == 0)
299d7be555eSGeorge Liu {
300d7be555eSGeorge Liu // log deassert only after all asserts are gone
301d7be555eSGeorge Liu if (!(*asserts).empty())
302d7be555eSGeorge Liu {
303d7be555eSGeorge Liu auto found = (*asserts).find(path);
304d7be555eSGeorge Liu if (found == (*asserts).end())
305d7be555eSGeorge Liu {
306d7be555eSGeorge Liu return;
307d7be555eSGeorge Liu }
308d7be555eSGeorge Liu (*asserts).erase(path);
309d7be555eSGeorge Liu
310d7be555eSGeorge Liu return;
311d7be555eSGeorge Liu }
312d7be555eSGeorge Liu if (*assertState)
313d7be555eSGeorge Liu {
314d7be555eSGeorge Liu *assertState = false;
315d7be555eSGeorge Liu auto foundCombine = (*combineEvent).find(groupEventName);
316d7be555eSGeorge Liu if (foundCombine == (*combineEvent).end())
317d7be555eSGeorge Liu {
318d7be555eSGeorge Liu return;
319d7be555eSGeorge Liu }
320d7be555eSGeorge Liu (*combineEvent).erase(groupEventName);
321d7be555eSGeorge Liu if (!deassertMessage.empty())
322d7be555eSGeorge Liu {
323d7be555eSGeorge Liu // Fan Failed has two args
324d7be555eSGeorge Liu if (deassertMessage == "OpenBMC.0.1.PowerSupplyFanRecovered")
325d7be555eSGeorge Liu {
326*4155a5a8SGeorge Liu lg2::info("'{EVENT}' deassert", "EVENT", eventName,
327d7be555eSGeorge Liu "REDFISH_MESSAGE_ID", deassertMessage,
328d7be555eSGeorge Liu "REDFISH_MESSAGE_ARGS",
329d7be555eSGeorge Liu (psuName + ',' + fanName));
330d7be555eSGeorge Liu }
331d7be555eSGeorge Liu else
332d7be555eSGeorge Liu {
333*4155a5a8SGeorge Liu lg2::info("'{EVENT}' deassert", "EVENT", eventName,
334d7be555eSGeorge Liu "REDFISH_MESSAGE_ID", deassertMessage,
335d7be555eSGeorge Liu "REDFISH_MESSAGE_ARGS", psuName);
336d7be555eSGeorge Liu }
337d7be555eSGeorge Liu }
338d7be555eSGeorge Liu
339d7be555eSGeorge Liu if ((*combineEvent).empty())
340d7be555eSGeorge Liu {
341d7be555eSGeorge Liu eventInterface->set_property("functional", true);
342d7be555eSGeorge Liu }
343d7be555eSGeorge Liu }
344d7be555eSGeorge Liu }
345d7be555eSGeorge Liu else
346d7be555eSGeorge Liu {
347*4155a5a8SGeorge Liu lg2::error("PSUSubEvent asserted by '{PATH}'", "PATH", path);
348d7be555eSGeorge Liu
349d7be555eSGeorge Liu if ((!*assertState) && ((*asserts).empty()))
350d7be555eSGeorge Liu {
351d7be555eSGeorge Liu *assertState = true;
352d7be555eSGeorge Liu if (!assertMessage.empty())
353d7be555eSGeorge Liu {
354d7be555eSGeorge Liu // Fan Failed has two args
355d7be555eSGeorge Liu if (assertMessage == "OpenBMC.0.1.PowerSupplyFanFailed")
356d7be555eSGeorge Liu {
357*4155a5a8SGeorge Liu lg2::warning("'{EVENT}' assert", "EVENT", eventName,
358d7be555eSGeorge Liu "REDFISH_MESSAGE_ID", assertMessage,
359d7be555eSGeorge Liu "REDFISH_MESSAGE_ARGS",
360d7be555eSGeorge Liu (psuName + ',' + fanName));
361d7be555eSGeorge Liu }
362d7be555eSGeorge Liu else
363d7be555eSGeorge Liu {
364*4155a5a8SGeorge Liu lg2::warning("'{EVENT}' assert", "EVENT", eventName,
365d7be555eSGeorge Liu "REDFISH_MESSAGE_ID", assertMessage,
366d7be555eSGeorge Liu "REDFISH_MESSAGE_ARGS", psuName);
367d7be555eSGeorge Liu }
368d7be555eSGeorge Liu }
369d7be555eSGeorge Liu if ((*combineEvent).empty())
370d7be555eSGeorge Liu {
371d7be555eSGeorge Liu eventInterface->set_property("functional", false);
372d7be555eSGeorge Liu }
373d7be555eSGeorge Liu (*combineEvent).emplace(groupEventName);
374d7be555eSGeorge Liu }
375d7be555eSGeorge Liu (*asserts).emplace(path);
376d7be555eSGeorge Liu }
377d7be555eSGeorge Liu value = newValue;
378d7be555eSGeorge Liu }
379