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