blob: 9620c091a14b2da5bc1a42c4197c6a037b65ac35 [file] [log] [blame]
Matthew Barthc95c5272020-06-15 19:51:13 -05001/**
Mike Capps7b34ee02022-05-04 14:16:12 -04002 * Copyright © 2022 IBM Corporation
Matthew Barthc95c5272020-06-15 19:51:13 -05003 *
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 */
Matthew Barthc95c5272020-06-15 19:51:13 -050016#include "system.hpp"
17
Mike Cappsbf8e56f2022-06-29 14:23:07 -040018#include "dbus_paths.hpp"
Matthew Barthc95c5272020-06-15 19:51:13 -050019#include "fan.hpp"
20#include "fan_defs.hpp"
21#include "tach_sensor.hpp"
22#include "trust_manager.hpp"
23#include "types.hpp"
Mike Cappsfdcd5db2021-05-20 12:47:10 -040024#include "utility.hpp"
Matthew Barthc95c5272020-06-15 19:51:13 -050025#ifdef MONITOR_USE_JSON
Mike Cappsb4379a12021-10-11 14:18:06 -040026#include "json_config.hpp"
Matthew Barthc95c5272020-06-15 19:51:13 -050027#include "json_parser.hpp"
28#endif
29
Matt Spinlerc8d3c512021-01-06 14:22:25 -060030#include "config.h"
31
Matt Spinlerbb449c12021-06-14 11:45:28 -060032#include "hwmon_ffdc.hpp"
33
Matthew Barthc95c5272020-06-15 19:51:13 -050034#include <nlohmann/json.hpp>
Anwaar Hadia00f6832025-03-27 15:03:11 +000035#include <phosphor-logging/lg2.hpp>
Matthew Barthc95c5272020-06-15 19:51:13 -050036#include <sdbusplus/bus.hpp>
Patrick Williamscb356d42022-07-22 19:26:53 -050037#include <sdbusplus/bus/match.hpp>
Matthew Barthc95c5272020-06-15 19:51:13 -050038#include <sdeventplus/event.hpp>
Matthew Barthd06905c2020-06-12 08:13:06 -050039#include <sdeventplus/source/signal.hpp>
Matthew Barthc95c5272020-06-15 19:51:13 -050040
41namespace phosphor::fan::monitor
42{
43
44using json = nlohmann::json;
Matt Spinlerf13b42e2020-10-26 15:29:49 -050045using Severity = sdbusplus::xyz::openbmc_project::Logging::server::Entry::Level;
46
Matt Spinler4f472a82022-08-26 13:55:34 -050047const std::string System::dumpFile = "/tmp/fan_monitor_dump.json";
48
Patrick Williamscb356d42022-07-22 19:26:53 -050049System::System(Mode mode, sdbusplus::bus_t& bus,
Matthew Barthc95c5272020-06-15 19:51:13 -050050 const sdeventplus::Event& event) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040051 _mode(mode), _bus(bus), _event(event),
Chau Lyfce14902023-01-13 08:52:33 +000052#ifdef MONITOR_USE_HOST_STATE
53 _powerState(std::make_unique<HostPowerState>(
54#else
Matt Spinlerc8d3c512021-01-06 14:22:25 -060055 _powerState(std::make_unique<PGoodState>(
Chau Lyfce14902023-01-13 08:52:33 +000056#endif
Matt Spinlere892e392020-10-14 13:21:31 -050057 bus, std::bind(std::mem_fn(&System::powerStateChanged), this,
Matt Spinlerc8d3c512021-01-06 14:22:25 -060058 std::placeholders::_1))),
59 _thermalAlert(bus, THERMAL_ALERT_OBJPATH)
Matt Spinler7d135642021-02-04 12:44:17 -060060{}
Matt Spinlere892e392020-10-14 13:21:31 -050061
Matthew Barth823bc492021-06-21 14:19:09 -050062void System::start()
Matt Spinler7d135642021-02-04 12:44:17 -060063{
Mike Cappsb4379a12021-10-11 14:18:06 -040064 namespace match = sdbusplus::bus::match;
65
66 // must be done before service detection
Patrick Williamscb356d42022-07-22 19:26:53 -050067 _inventoryMatch = std::make_unique<sdbusplus::bus::match_t>(
Mike Cappsb4379a12021-10-11 14:18:06 -040068 _bus, match::rules::nameOwnerChanged(util::INVENTORY_SVC),
69 std::bind(&System::inventoryOnlineCb, this, std::placeholders::_1));
70
71 bool invServiceRunning = util::SDBusPlus::callMethodAndRead<bool>(
72 _bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
73 "org.freedesktop.DBus", "NameHasOwner", util::INVENTORY_SVC);
74
75 if (invServiceRunning)
76 {
77 _inventoryMatch.reset();
78
79 if (!_loaded)
80 {
81 load();
82 }
83 }
84}
85
86void System::load()
87{
Matthew Barthc95c5272020-06-15 19:51:13 -050088 json jsonObj = json::object();
89#ifdef MONITOR_USE_JSON
Mike Cappsb4379a12021-10-11 14:18:06 -040090 try
91 {
Mike Capps808d7fe2022-06-13 10:12:16 -040092 jsonObj = getJsonObj();
Matthew Barthc95c5272020-06-15 19:51:13 -050093#endif
Mike Cappsb4379a12021-10-11 14:18:06 -040094 auto trustGrps = getTrustGroups(jsonObj);
95 auto fanDefs = getFanDefinitions(jsonObj);
96 // Retrieve and set trust groups within the trust manager
97 setTrustMgr(getTrustGroups(jsonObj));
98 // Clear/set configured fan definitions
99 _fans.clear();
100 _fanHealth.clear();
101 // Retrieve fan definitions and create fan objects to be monitored
102 setFans(fanDefs);
103 setFaultConfig(jsonObj);
Anwaar Hadia00f6832025-03-27 15:03:11 +0000104 lg2::info("Configuration loaded");
Mike Cappsb4379a12021-10-11 14:18:06 -0400105
106 _loaded = true;
107#ifdef MONITOR_USE_JSON
108 }
109 catch (const phosphor::fan::NoConfigFound&)
110 {}
111#endif
Matt Spinlere892e392020-10-14 13:21:31 -0500112
Matt Spinlere892e392020-10-14 13:21:31 -0500113 if (_powerState->isPowerOn())
114 {
Matt Spinler752f24e2022-07-06 15:57:54 -0500115 // Fans could be missing on startup, so check the power off rules.
116 // Tach sensors default to functional, so they wouldn't cause a power
117 // off here.
Matt Spinlere892e392020-10-14 13:21:31 -0500118 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
119 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400120 rule->check(PowerRuleState::runtime, _fanHealth);
121 });
Matt Spinlere892e392020-10-14 13:21:31 -0500122 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400123
Mike Cappsb4379a12021-10-11 14:18:06 -0400124 subscribeSensorsToServices();
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400125}
126
Mike Capps25f03272021-09-13 13:38:44 -0400127void System::subscribeSensorsToServices()
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400128{
Mike Capps25f03272021-09-13 13:38:44 -0400129 namespace match = sdbusplus::bus::match;
130
Mike Cappsb4379a12021-10-11 14:18:06 -0400131 _sensorMatch.clear();
132
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400133 SensorMapType sensorMap;
134
135 // build a list of all interfaces, always including the value interface
136 // using set automatically guards against duplicates
137 std::set<std::string> unique_interfaces{util::FAN_SENSOR_VALUE_INTF};
138
139 for (const auto& fan : _fans)
140 {
141 for (const auto& sensor : fan->sensors())
142 {
143 unique_interfaces.insert(sensor->getInterface());
144 }
145 }
146 // convert them to vector to pass into getSubTreeRaw
147 std::vector<std::string> interfaces(unique_interfaces.begin(),
148 unique_interfaces.end());
149
Mike Capps25f03272021-09-13 13:38:44 -0400150 try
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400151 {
Mike Capps25f03272021-09-13 13:38:44 -0400152 // get service information for all service names that are
153 // hosting these interfaces
154 auto serviceObjects = util::SDBusPlus::getSubTreeRaw(
155 _bus, FAN_SENSOR_PATH, interfaces, 0);
156
157 for (const auto& fan : _fans)
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400158 {
Mike Capps25f03272021-09-13 13:38:44 -0400159 // For every sensor in each fan
160 for (const auto& sensor : fan->sensors())
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400161 {
Mike Capps25f03272021-09-13 13:38:44 -0400162 const auto itServ = serviceObjects.find(sensor->name());
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400163
Mike Capps25f03272021-09-13 13:38:44 -0400164 if (serviceObjects.end() == itServ || itServ->second.empty())
165 {
166 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500167 std::format("Fan sensor entry {} not found in D-Bus",
Mike Capps25f03272021-09-13 13:38:44 -0400168 sensor->name()),
169 Logger::error);
170 continue;
171 }
172
173 for (const auto& [serviceName, unused] : itServ->second)
174 {
175 // associate service name with sensor
176 sensorMap[serviceName].insert(sensor);
177 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400178 }
179 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400180
Mike Capps25f03272021-09-13 13:38:44 -0400181 // only create 1 match per service
182 for (const auto& [serviceName, unused] : sensorMap)
183 {
184 // map its service name to the sensor
Patrick Williamscb356d42022-07-22 19:26:53 -0500185 _sensorMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
Mike Capps25f03272021-09-13 13:38:44 -0400186 _bus, match::rules::nameOwnerChanged(serviceName),
187 std::bind(&System::tachSignalOffline, this,
188 std::placeholders::_1, sensorMap)));
189 }
190 }
191 catch (const util::DBusError&)
192 {
193 // catch exception from getSubTreeRaw() when fan sensor paths don't
194 // exist yet
195 }
Matthew Barthd06905c2020-06-12 08:13:06 -0500196}
197
Patrick Williamscb356d42022-07-22 19:26:53 -0500198void System::inventoryOnlineCb(sdbusplus::message_t& msg)
Mike Cappsb4379a12021-10-11 14:18:06 -0400199{
200 namespace match = sdbusplus::bus::match;
201
202 std::string iface;
203 msg.read(iface);
204
205 if (util::INVENTORY_INTF != iface)
206 {
207 return;
208 }
209
210 std::string oldName;
211 msg.read(oldName);
212
213 std::string newName;
214 msg.read(newName);
215
216 // newName should never be empty since match was reset on the first
217 // nameOwnerChanged signal received from the service.
218 if (!_loaded && !newName.empty())
219 {
220 load();
221 }
222
223 // cancel any further notifications about the service state
224 _inventoryMatch.reset();
225}
226
Matthew Barthd06905c2020-06-12 08:13:06 -0500227void System::sighupHandler(sdeventplus::source::Signal&,
228 const struct signalfd_siginfo*)
229{
230 try
Matthew Barthc95c5272020-06-15 19:51:13 -0500231 {
Mike Cappsb4379a12021-10-11 14:18:06 -0400232 load();
Matthew Barthd06905c2020-06-12 08:13:06 -0500233 }
Mike Cappsb4379a12021-10-11 14:18:06 -0400234 catch (std::runtime_error& re)
Matthew Barthd06905c2020-06-12 08:13:06 -0500235 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000236 lg2::error(
237 "Error reloading config, no config changes made: {LOAD_ERROR}",
238 "LOAD_ERROR", re);
Matthew Barthc95c5272020-06-15 19:51:13 -0500239 }
240}
241
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500242const std::vector<CreateGroupFunction> System::getTrustGroups(
243 [[maybe_unused]] const json& jsonObj)
Matthew Barthc95c5272020-06-15 19:51:13 -0500244{
245#ifdef MONITOR_USE_JSON
246 return getTrustGrps(jsonObj);
247#else
248 return trustGroups;
249#endif
250}
251
Matthew Barthd06905c2020-06-12 08:13:06 -0500252void System::setTrustMgr(const std::vector<CreateGroupFunction>& groupFuncs)
253{
254 _trust = std::make_unique<trust::Manager>(groupFuncs);
255}
256
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500257const std::vector<FanDefinition> System::getFanDefinitions(
258 [[maybe_unused]] const json& jsonObj)
Matthew Barthc95c5272020-06-15 19:51:13 -0500259{
260#ifdef MONITOR_USE_JSON
261 return getFanDefs(jsonObj);
262#else
263 return fanDefinitions;
264#endif
265}
266
Matthew Barthd06905c2020-06-12 08:13:06 -0500267void System::setFans(const std::vector<FanDefinition>& fanDefs)
268{
269 for (const auto& fanDef : fanDefs)
270 {
271 // Check if a condition exists on the fan
Matt Spinler18fb12b2023-05-09 11:17:42 -0500272 auto condition = fanDef.condition;
Matthew Barthd06905c2020-06-12 08:13:06 -0500273 if (condition)
274 {
275 // Condition exists, skip adding fan if it fails
276 if (!(*condition)(_bus))
277 {
278 continue;
279 }
280 }
281 _fans.emplace_back(
Matt Spinlerb0412d02020-10-12 16:53:52 -0500282 std::make_unique<Fan>(_mode, _bus, _event, _trust, fanDef, *this));
Matt Spinlerb63aa092020-10-14 09:45:11 -0500283
284 updateFanHealth(*(_fans.back()));
Matthew Barthd06905c2020-06-12 08:13:06 -0500285 }
286}
287
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400288// callback indicating a service went [on|off]line.
289// Determine on/offline status, set all sensors for that service
290// to new state
291//
Patrick Williamscb356d42022-07-22 19:26:53 -0500292void System::tachSignalOffline(sdbusplus::message_t& msg,
Patrick Williams61b73292023-05-10 07:50:12 -0500293 const SensorMapType& sensorMap)
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400294{
295 std::string serviceName, oldOwner, newOwner;
296
297 msg.read(serviceName);
298 msg.read(oldOwner);
299 msg.read(newOwner);
300
301 // true if sensor server came back online, false -> went offline
302 bool hasOwner = !newOwner.empty() && oldOwner.empty();
303
304 std::string stateStr(hasOwner ? "online" : "offline");
Patrick Williamsfbf47032023-07-17 12:27:34 -0500305 getLogger().log(std::format("Changing sensors for service {} to {}",
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400306 serviceName, stateStr),
307 Logger::info);
308
309 auto sensorItr(sensorMap.find(serviceName));
310
311 if (sensorItr != sensorMap.end())
312 {
313 // set all sensors' owner state to not-owned
314 for (auto& sensor : sensorItr->second)
315 {
316 sensor->setOwner(hasOwner);
317 sensor->getFan().process(*sensor);
318 }
319 }
320}
321
Matt Spinlerb63aa092020-10-14 09:45:11 -0500322void System::updateFanHealth(const Fan& fan)
323{
324 std::vector<bool> sensorStatus;
325 for (const auto& sensor : fan.sensors())
326 {
327 sensorStatus.push_back(sensor->functional());
328 }
329
Patrick Williamsdfddd642024-08-16 15:21:51 -0400330 _fanHealth[fan.getName()] =
331 std::make_tuple(fan.present(), std::move(sensorStatus));
Matt Spinlerb63aa092020-10-14 09:45:11 -0500332}
333
Matt Spinler4283c5d2021-03-01 15:56:00 -0600334void System::fanStatusChange(const Fan& fan, bool skipRulesCheck)
Matt Spinlerb63aa092020-10-14 09:45:11 -0500335{
336 updateFanHealth(fan);
Matt Spinlere892e392020-10-14 13:21:31 -0500337
Matt Spinler4283c5d2021-03-01 15:56:00 -0600338 if (_powerState->isPowerOn() && !skipRulesCheck)
Matt Spinlere892e392020-10-14 13:21:31 -0500339 {
340 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
341 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400342 rule->check(PowerRuleState::runtime, _fanHealth);
343 });
Matt Spinlere892e392020-10-14 13:21:31 -0500344 }
345}
346
Mike Capps808d7fe2022-06-13 10:12:16 -0400347void System::setFaultConfig([[maybe_unused]] const json& jsonObj)
Matt Spinlere892e392020-10-14 13:21:31 -0500348{
349#ifdef MONITOR_USE_JSON
350 std::shared_ptr<PowerInterfaceBase> powerInterface =
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600351 std::make_shared<PowerInterface>(_thermalAlert);
Matt Spinlere892e392020-10-14 13:21:31 -0500352
Matt Spinlerac1efc12020-10-27 10:20:11 -0500353 PowerOffAction::PrePowerOffFunc func =
354 std::bind(std::mem_fn(&System::logShutdownError), this);
355
356 _powerOffRules = getPowerOffRules(jsonObj, powerInterface, func);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500357
358 _numNonfuncSensorsBeforeError = getNumNonfuncRotorsBeforeError(jsonObj);
Matt Spinlere892e392020-10-14 13:21:31 -0500359#endif
360}
361
362void System::powerStateChanged(bool powerStateOn)
363{
Matt Spinler7d135642021-02-04 12:44:17 -0600364 std::for_each(_fans.begin(), _fans.end(), [powerStateOn](auto& fan) {
365 fan->powerStateChanged(powerStateOn);
366 });
367
Matt Spinlere892e392020-10-14 13:21:31 -0500368 if (powerStateOn)
369 {
Mike Cappsb4379a12021-10-11 14:18:06 -0400370 if (!_loaded)
Matt Spinler7d135642021-02-04 12:44:17 -0600371 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000372 lg2::error("No conf file found at power on");
Matthew Barthba53d3e2021-02-24 07:48:37 -0600373 throw std::runtime_error("No conf file found at power on");
Matt Spinler7d135642021-02-04 12:44:17 -0600374 }
375
Matt Spinlerbb449c12021-06-14 11:45:28 -0600376 // If no fan has its sensors on D-Bus, then there is a problem
377 // with the fan controller. Log an error and shut down.
378 if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400379 return fan->numSensorsOnDBusAtPowerOn() == 0;
380 }))
Matt Spinlerbb449c12021-06-14 11:45:28 -0600381 {
Chau Ly751c8be2023-01-13 08:21:03 +0000382#if DELAY_HOST_CONTROL > 0
383 sleep(DELAY_HOST_CONTROL);
384 std::for_each(_fans.begin(), _fans.end(),
385 [powerStateOn](auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400386 fan->powerStateChanged(powerStateOn);
387 });
Chau Ly751c8be2023-01-13 08:21:03 +0000388 if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400389 return fan->numSensorsOnDBusAtPowerOn() == 0;
390 }))
Chau Ly751c8be2023-01-13 08:21:03 +0000391 {
392 handleOfflineFanController();
393 return;
394 }
395#else
Matt Spinlerbb449c12021-06-14 11:45:28 -0600396 handleOfflineFanController();
397 return;
Chau Ly751c8be2023-01-13 08:21:03 +0000398#endif
Matt Spinlerbb449c12021-06-14 11:45:28 -0600399 }
400
Mike Capps25f03272021-09-13 13:38:44 -0400401 if (_sensorMatch.empty())
402 {
403 subscribeSensorsToServices();
404 }
405
Matt Spinlere892e392020-10-14 13:21:31 -0500406 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
407 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400408 rule->check(PowerRuleState::atPgood, _fanHealth);
409 });
Matt Spinlere892e392020-10-14 13:21:31 -0500410 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
411 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400412 rule->check(PowerRuleState::runtime, _fanHealth);
413 });
Matt Spinlere892e392020-10-14 13:21:31 -0500414 }
415 else
416 {
Matt Spinlerc8d3c512021-01-06 14:22:25 -0600417 _thermalAlert.enabled(false);
418
Matt Spinlere892e392020-10-14 13:21:31 -0500419 // Cancel any in-progress power off actions
420 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
421 [this](auto& rule) { rule->cancel(); });
422 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500423}
424
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500425void System::sensorErrorTimerExpired(const Fan& fan, const TachSensor& sensor)
426{
427 std::string fanPath{util::INVENTORY_PATH + fan.getName()};
428
429 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500430 std::format("Creating event log for faulted fan {} sensor {}", fanPath,
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500431 sensor.name()),
432 Logger::error);
433
434 // In order to know if the event log should have a severity of error or
435 // informational, count the number of existing nonfunctional sensors and
436 // compare it to _numNonfuncSensorsBeforeError.
437 size_t nonfuncSensors = 0;
438 for (const auto& fan : _fans)
439 {
440 for (const auto& s : fan->sensors())
441 {
442 // Don't count nonfunctional sensors that still have their
443 // error timer running as nonfunctional since they haven't
444 // had event logs created for those errors yet.
445 if (!s->functional() && !s->errorTimerRunning())
446 {
447 nonfuncSensors++;
448 }
449 }
450 }
451
452 Severity severity = Severity::Error;
453 if (nonfuncSensors < _numNonfuncSensorsBeforeError)
454 {
455 severity = Severity::Informational;
456 }
457
458 auto error =
459 std::make_unique<FanError>("xyz.openbmc_project.Fan.Error.Fault",
460 fanPath, sensor.name(), severity);
461
462 auto sensorData = captureSensorData();
463 error->commit(sensorData);
464
Matt Spinlerac1efc12020-10-27 10:20:11 -0500465 // Save the error so it can be committed again on a power off.
466 _lastError = std::move(error);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500467}
468
Matt Spinler27f6b682020-10-27 08:43:37 -0500469void System::fanMissingErrorTimerExpired(const Fan& fan)
470{
471 std::string fanPath{util::INVENTORY_PATH + fan.getName()};
472
473 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500474 std::format("Creating event log for missing fan {}", fanPath),
Matt Spinler27f6b682020-10-27 08:43:37 -0500475 Logger::error);
476
477 auto error = std::make_unique<FanError>(
478 "xyz.openbmc_project.Fan.Error.Missing", fanPath, "", Severity::Error);
479
480 auto sensorData = captureSensorData();
481 error->commit(sensorData);
482
Matt Spinlerac1efc12020-10-27 10:20:11 -0500483 // Save the error so it can be committed again on a power off.
484 _lastError = std::move(error);
485}
486
487void System::logShutdownError()
488{
489 if (_lastError)
490 {
491 getLogger().log("Re-committing previous fan error before power off");
492
493 // Still use the latest sensor data
494 auto sensorData = captureSensorData();
Matt Spinlerf435eb12021-05-11 14:44:25 -0500495 _lastError->commit(sensorData, true);
Matt Spinlerac1efc12020-10-27 10:20:11 -0500496 }
Matt Spinler27f6b682020-10-27 08:43:37 -0500497}
498
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500499json System::captureSensorData()
500{
501 json data;
502
503 for (const auto& fan : _fans)
504 {
505 for (const auto& sensor : fan->sensors())
506 {
507 json values;
508 values["present"] = fan->present();
509 values["functional"] = sensor->functional();
Matt Spinlerd16d4642022-08-26 13:32:07 -0500510 values["in_range"] = !fan->outOfRange(*sensor);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500511 values["tach"] = sensor->getInput();
Mike Capps7b34ee02022-05-04 14:16:12 -0400512
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500513 if (sensor->hasTarget())
514 {
515 values["target"] = sensor->getTarget();
516 }
517
Mike Capps7b34ee02022-05-04 14:16:12 -0400518 // convert between string/json to remove newlines
519 values["prev_tachs"] = json(sensor->getPrevTach()).dump();
520
521 if (sensor->hasTarget())
522 {
523 values["prev_targets"] = json(sensor->getPrevTarget()).dump();
524 }
525
Matt Spinler87f9adc2022-08-11 13:17:09 -0500526 if (sensor->getMethod() == MethodMode::count)
527 {
528 values["ticks"] = sensor->getCounter();
529 }
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500530 data["sensors"][sensor->name()] = values;
531 }
532 }
533
534 return data;
535}
536
Matt Spinlerbb449c12021-06-14 11:45:28 -0600537void System::handleOfflineFanController()
538{
539 getLogger().log("The fan controller appears to be offline. Shutting down.",
540 Logger::error);
541
542 auto ffdc = collectHwmonFFDC();
543
544 FanError error{"xyz.openbmc_project.Fan.Error.FanControllerOffline",
545 Severity::Critical};
546 error.commit(ffdc, true);
547
548 PowerInterface::executeHardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400549
550 createBmcDump();
551}
552
553/**
554 * @brief Create a BMC Dump
555 */
556void System::createBmcDump() const
557{
558 try
559 {
560 util::SDBusPlus::callMethod(
561 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
562 "xyz.openbmc_project.Dump.Create", "CreateDump",
563 std::vector<
564 std::pair<std::string, std::variant<std::string, uint64_t>>>());
565 }
Mike Capps477b13b2022-07-11 10:45:46 -0400566 catch (const std::exception& e)
567 {
568 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500569 std::format("Caught exception while creating BMC dump: {}",
Mike Capps477b13b2022-07-11 10:45:46 -0400570 e.what()),
571 Logger::error);
572 }
Matt Spinlerbb449c12021-06-14 11:45:28 -0600573}
574
Matt Spinler4f472a82022-08-26 13:55:34 -0500575void System::dumpDebugData(sdeventplus::source::Signal&,
576 const struct signalfd_siginfo*)
577{
578 json output;
579
580 if (_loaded)
581 {
582 output["logs"] = getLogger().getLogs();
583 output["sensors"] = captureSensorData();
584 }
585 else
586 {
587 output["error"] = "Fan monitor not loaded yet. Try again later.";
588 }
589
590 std::ofstream file{System::dumpFile};
591 if (!file)
592 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000593 lg2::error("Could not open file for fan monitor dump");
Matt Spinler4f472a82022-08-26 13:55:34 -0500594 }
595 else
596 {
597 file << std::setw(4) << output;
598 }
599}
600
Matthew Barthc95c5272020-06-15 19:51:13 -0500601} // namespace phosphor::fan::monitor