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