blob: ff074c34f99a9645d6769c1c1bf9b688e151a6a4 [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
Matthew Barthd06905c2020-06-12 08:13:06 -050047using namespace phosphor::logging;
Matthew Barthc95c5272020-06-15 19:51:13 -050048
Matt Spinler4f472a82022-08-26 13:55:34 -050049const std::string System::dumpFile = "/tmp/fan_monitor_dump.json";
50
Patrick Williamscb356d42022-07-22 19:26:53 -050051System::System(Mode mode, sdbusplus::bus_t& bus,
Matthew Barthc95c5272020-06-15 19:51:13 -050052 const sdeventplus::Event& event) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040053 _mode(mode), _bus(bus), _event(event),
Chau Lyfce14902023-01-13 08:52:33 +000054#ifdef MONITOR_USE_HOST_STATE
55 _powerState(std::make_unique<HostPowerState>(
56#else
Matt Spinlerc8d3c512021-01-06 14:22:25 -060057 _powerState(std::make_unique<PGoodState>(
Chau Lyfce14902023-01-13 08:52:33 +000058#endif
Matt Spinlere892e392020-10-14 13:21:31 -050059 bus, std::bind(std::mem_fn(&System::powerStateChanged), this,
Matt Spinlerc8d3c512021-01-06 14:22:25 -060060 std::placeholders::_1))),
61 _thermalAlert(bus, THERMAL_ALERT_OBJPATH)
Matt Spinler7d135642021-02-04 12:44:17 -060062{}
Matt Spinlere892e392020-10-14 13:21:31 -050063
Matthew Barth823bc492021-06-21 14:19:09 -050064void System::start()
Matt Spinler7d135642021-02-04 12:44:17 -060065{
Mike Cappsb4379a12021-10-11 14:18:06 -040066 namespace match = sdbusplus::bus::match;
67
68 // must be done before service detection
Patrick Williamscb356d42022-07-22 19:26:53 -050069 _inventoryMatch = std::make_unique<sdbusplus::bus::match_t>(
Mike Cappsb4379a12021-10-11 14:18:06 -040070 _bus, match::rules::nameOwnerChanged(util::INVENTORY_SVC),
71 std::bind(&System::inventoryOnlineCb, this, std::placeholders::_1));
72
73 bool invServiceRunning = util::SDBusPlus::callMethodAndRead<bool>(
74 _bus, "org.freedesktop.DBus", "/org/freedesktop/DBus",
75 "org.freedesktop.DBus", "NameHasOwner", util::INVENTORY_SVC);
76
77 if (invServiceRunning)
78 {
79 _inventoryMatch.reset();
80
81 if (!_loaded)
82 {
83 load();
84 }
85 }
86}
87
88void System::load()
89{
Matthew Barthc95c5272020-06-15 19:51:13 -050090 json jsonObj = json::object();
91#ifdef MONITOR_USE_JSON
Mike Cappsb4379a12021-10-11 14:18:06 -040092 try
93 {
Mike Capps808d7fe2022-06-13 10:12:16 -040094 jsonObj = getJsonObj();
Matthew Barthc95c5272020-06-15 19:51:13 -050095#endif
Mike Cappsb4379a12021-10-11 14:18:06 -040096 auto trustGrps = getTrustGroups(jsonObj);
97 auto fanDefs = getFanDefinitions(jsonObj);
98 // Retrieve and set trust groups within the trust manager
99 setTrustMgr(getTrustGroups(jsonObj));
100 // Clear/set configured fan definitions
101 _fans.clear();
102 _fanHealth.clear();
103 // Retrieve fan definitions and create fan objects to be monitored
104 setFans(fanDefs);
105 setFaultConfig(jsonObj);
Anwaar Hadia00f6832025-03-27 15:03:11 +0000106 lg2::info("Configuration loaded");
Mike Cappsb4379a12021-10-11 14:18:06 -0400107
108 _loaded = true;
109#ifdef MONITOR_USE_JSON
110 }
111 catch (const phosphor::fan::NoConfigFound&)
112 {}
113#endif
Matt Spinlere892e392020-10-14 13:21:31 -0500114
Matt Spinlere892e392020-10-14 13:21:31 -0500115 if (_powerState->isPowerOn())
116 {
Matt Spinler752f24e2022-07-06 15:57:54 -0500117 // Fans could be missing on startup, so check the power off rules.
118 // Tach sensors default to functional, so they wouldn't cause a power
119 // off here.
Matt Spinlere892e392020-10-14 13:21:31 -0500120 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
121 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400122 rule->check(PowerRuleState::runtime, _fanHealth);
123 });
Matt Spinlere892e392020-10-14 13:21:31 -0500124 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400125
Mike Cappsb4379a12021-10-11 14:18:06 -0400126 subscribeSensorsToServices();
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400127}
128
Mike Capps25f03272021-09-13 13:38:44 -0400129void System::subscribeSensorsToServices()
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400130{
Mike Capps25f03272021-09-13 13:38:44 -0400131 namespace match = sdbusplus::bus::match;
132
Mike Cappsb4379a12021-10-11 14:18:06 -0400133 _sensorMatch.clear();
134
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400135 SensorMapType sensorMap;
136
137 // build a list of all interfaces, always including the value interface
138 // using set automatically guards against duplicates
139 std::set<std::string> unique_interfaces{util::FAN_SENSOR_VALUE_INTF};
140
141 for (const auto& fan : _fans)
142 {
143 for (const auto& sensor : fan->sensors())
144 {
145 unique_interfaces.insert(sensor->getInterface());
146 }
147 }
148 // convert them to vector to pass into getSubTreeRaw
149 std::vector<std::string> interfaces(unique_interfaces.begin(),
150 unique_interfaces.end());
151
Mike Capps25f03272021-09-13 13:38:44 -0400152 try
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400153 {
Mike Capps25f03272021-09-13 13:38:44 -0400154 // get service information for all service names that are
155 // hosting these interfaces
156 auto serviceObjects = util::SDBusPlus::getSubTreeRaw(
157 _bus, FAN_SENSOR_PATH, interfaces, 0);
158
159 for (const auto& fan : _fans)
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400160 {
Mike Capps25f03272021-09-13 13:38:44 -0400161 // For every sensor in each fan
162 for (const auto& sensor : fan->sensors())
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400163 {
Mike Capps25f03272021-09-13 13:38:44 -0400164 const auto itServ = serviceObjects.find(sensor->name());
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400165
Mike Capps25f03272021-09-13 13:38:44 -0400166 if (serviceObjects.end() == itServ || itServ->second.empty())
167 {
168 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500169 std::format("Fan sensor entry {} not found in D-Bus",
Mike Capps25f03272021-09-13 13:38:44 -0400170 sensor->name()),
171 Logger::error);
172 continue;
173 }
174
175 for (const auto& [serviceName, unused] : itServ->second)
176 {
177 // associate service name with sensor
178 sensorMap[serviceName].insert(sensor);
179 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400180 }
181 }
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400182
Mike Capps25f03272021-09-13 13:38:44 -0400183 // only create 1 match per service
184 for (const auto& [serviceName, unused] : sensorMap)
185 {
186 // map its service name to the sensor
Patrick Williamscb356d42022-07-22 19:26:53 -0500187 _sensorMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
Mike Capps25f03272021-09-13 13:38:44 -0400188 _bus, match::rules::nameOwnerChanged(serviceName),
189 std::bind(&System::tachSignalOffline, this,
190 std::placeholders::_1, sensorMap)));
191 }
192 }
193 catch (const util::DBusError&)
194 {
195 // catch exception from getSubTreeRaw() when fan sensor paths don't
196 // exist yet
197 }
Matthew Barthd06905c2020-06-12 08:13:06 -0500198}
199
Patrick Williamscb356d42022-07-22 19:26:53 -0500200void System::inventoryOnlineCb(sdbusplus::message_t& msg)
Mike Cappsb4379a12021-10-11 14:18:06 -0400201{
202 namespace match = sdbusplus::bus::match;
203
204 std::string iface;
205 msg.read(iface);
206
207 if (util::INVENTORY_INTF != iface)
208 {
209 return;
210 }
211
212 std::string oldName;
213 msg.read(oldName);
214
215 std::string newName;
216 msg.read(newName);
217
218 // newName should never be empty since match was reset on the first
219 // nameOwnerChanged signal received from the service.
220 if (!_loaded && !newName.empty())
221 {
222 load();
223 }
224
225 // cancel any further notifications about the service state
226 _inventoryMatch.reset();
227}
228
Matthew Barthd06905c2020-06-12 08:13:06 -0500229void System::sighupHandler(sdeventplus::source::Signal&,
230 const struct signalfd_siginfo*)
231{
232 try
Matthew Barthc95c5272020-06-15 19:51:13 -0500233 {
Mike Cappsb4379a12021-10-11 14:18:06 -0400234 load();
Matthew Barthd06905c2020-06-12 08:13:06 -0500235 }
Mike Cappsb4379a12021-10-11 14:18:06 -0400236 catch (std::runtime_error& re)
Matthew Barthd06905c2020-06-12 08:13:06 -0500237 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000238 lg2::error(
239 "Error reloading config, no config changes made: {LOAD_ERROR}",
240 "LOAD_ERROR", re);
Matthew Barthc95c5272020-06-15 19:51:13 -0500241 }
242}
243
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500244const std::vector<CreateGroupFunction> System::getTrustGroups(
245 [[maybe_unused]] const json& jsonObj)
Matthew Barthc95c5272020-06-15 19:51:13 -0500246{
247#ifdef MONITOR_USE_JSON
248 return getTrustGrps(jsonObj);
249#else
250 return trustGroups;
251#endif
252}
253
Matthew Barthd06905c2020-06-12 08:13:06 -0500254void System::setTrustMgr(const std::vector<CreateGroupFunction>& groupFuncs)
255{
256 _trust = std::make_unique<trust::Manager>(groupFuncs);
257}
258
Patrick Williams4fa67aa2025-02-03 14:28:47 -0500259const std::vector<FanDefinition> System::getFanDefinitions(
260 [[maybe_unused]] const json& jsonObj)
Matthew Barthc95c5272020-06-15 19:51:13 -0500261{
262#ifdef MONITOR_USE_JSON
263 return getFanDefs(jsonObj);
264#else
265 return fanDefinitions;
266#endif
267}
268
Matthew Barthd06905c2020-06-12 08:13:06 -0500269void System::setFans(const std::vector<FanDefinition>& fanDefs)
270{
271 for (const auto& fanDef : fanDefs)
272 {
273 // Check if a condition exists on the fan
Matt Spinler18fb12b2023-05-09 11:17:42 -0500274 auto condition = fanDef.condition;
Matthew Barthd06905c2020-06-12 08:13:06 -0500275 if (condition)
276 {
277 // Condition exists, skip adding fan if it fails
278 if (!(*condition)(_bus))
279 {
280 continue;
281 }
282 }
283 _fans.emplace_back(
Matt Spinlerb0412d02020-10-12 16:53:52 -0500284 std::make_unique<Fan>(_mode, _bus, _event, _trust, fanDef, *this));
Matt Spinlerb63aa092020-10-14 09:45:11 -0500285
286 updateFanHealth(*(_fans.back()));
Matthew Barthd06905c2020-06-12 08:13:06 -0500287 }
288}
289
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400290// callback indicating a service went [on|off]line.
291// Determine on/offline status, set all sensors for that service
292// to new state
293//
Patrick Williamscb356d42022-07-22 19:26:53 -0500294void System::tachSignalOffline(sdbusplus::message_t& msg,
Patrick Williams61b73292023-05-10 07:50:12 -0500295 const SensorMapType& sensorMap)
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400296{
297 std::string serviceName, oldOwner, newOwner;
298
299 msg.read(serviceName);
300 msg.read(oldOwner);
301 msg.read(newOwner);
302
303 // true if sensor server came back online, false -> went offline
304 bool hasOwner = !newOwner.empty() && oldOwner.empty();
305
306 std::string stateStr(hasOwner ? "online" : "offline");
Patrick Williamsfbf47032023-07-17 12:27:34 -0500307 getLogger().log(std::format("Changing sensors for service {} to {}",
Mike Cappsfdcd5db2021-05-20 12:47:10 -0400308 serviceName, stateStr),
309 Logger::info);
310
311 auto sensorItr(sensorMap.find(serviceName));
312
313 if (sensorItr != sensorMap.end())
314 {
315 // set all sensors' owner state to not-owned
316 for (auto& sensor : sensorItr->second)
317 {
318 sensor->setOwner(hasOwner);
319 sensor->getFan().process(*sensor);
320 }
321 }
322}
323
Matt Spinlerb63aa092020-10-14 09:45:11 -0500324void System::updateFanHealth(const Fan& fan)
325{
326 std::vector<bool> sensorStatus;
327 for (const auto& sensor : fan.sensors())
328 {
329 sensorStatus.push_back(sensor->functional());
330 }
331
Patrick Williamsdfddd642024-08-16 15:21:51 -0400332 _fanHealth[fan.getName()] =
333 std::make_tuple(fan.present(), std::move(sensorStatus));
Matt Spinlerb63aa092020-10-14 09:45:11 -0500334}
335
Matt Spinler4283c5d2021-03-01 15:56:00 -0600336void System::fanStatusChange(const Fan& fan, bool skipRulesCheck)
Matt Spinlerb63aa092020-10-14 09:45:11 -0500337{
338 updateFanHealth(fan);
Matt Spinlere892e392020-10-14 13:21:31 -0500339
Matt Spinler4283c5d2021-03-01 15:56:00 -0600340 if (_powerState->isPowerOn() && !skipRulesCheck)
Matt Spinlere892e392020-10-14 13:21:31 -0500341 {
342 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
343 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400344 rule->check(PowerRuleState::runtime, _fanHealth);
345 });
Matt Spinlere892e392020-10-14 13:21:31 -0500346 }
347}
348
Mike Capps808d7fe2022-06-13 10:12:16 -0400349void System::setFaultConfig([[maybe_unused]] const json& jsonObj)
Matt Spinlere892e392020-10-14 13:21:31 -0500350{
351#ifdef MONITOR_USE_JSON
352 std::shared_ptr<PowerInterfaceBase> powerInterface =
Matt Spinlerba3ee9a2021-01-06 14:45:50 -0600353 std::make_shared<PowerInterface>(_thermalAlert);
Matt Spinlere892e392020-10-14 13:21:31 -0500354
Matt Spinlerac1efc12020-10-27 10:20:11 -0500355 PowerOffAction::PrePowerOffFunc func =
356 std::bind(std::mem_fn(&System::logShutdownError), this);
357
358 _powerOffRules = getPowerOffRules(jsonObj, powerInterface, func);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500359
360 _numNonfuncSensorsBeforeError = getNumNonfuncRotorsBeforeError(jsonObj);
Matt Spinlere892e392020-10-14 13:21:31 -0500361#endif
362}
363
364void System::powerStateChanged(bool powerStateOn)
365{
Matt Spinler7d135642021-02-04 12:44:17 -0600366 std::for_each(_fans.begin(), _fans.end(), [powerStateOn](auto& fan) {
367 fan->powerStateChanged(powerStateOn);
368 });
369
Matt Spinlere892e392020-10-14 13:21:31 -0500370 if (powerStateOn)
371 {
Mike Cappsb4379a12021-10-11 14:18:06 -0400372 if (!_loaded)
Matt Spinler7d135642021-02-04 12:44:17 -0600373 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000374 lg2::error("No conf file found at power on");
Matthew Barthba53d3e2021-02-24 07:48:37 -0600375 throw std::runtime_error("No conf file found at power on");
Matt Spinler7d135642021-02-04 12:44:17 -0600376 }
377
Matt Spinlerbb449c12021-06-14 11:45:28 -0600378 // If no fan has its sensors on D-Bus, then there is a problem
379 // with the fan controller. Log an error and shut down.
380 if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400381 return fan->numSensorsOnDBusAtPowerOn() == 0;
382 }))
Matt Spinlerbb449c12021-06-14 11:45:28 -0600383 {
Chau Ly751c8be2023-01-13 08:21:03 +0000384#if DELAY_HOST_CONTROL > 0
385 sleep(DELAY_HOST_CONTROL);
386 std::for_each(_fans.begin(), _fans.end(),
387 [powerStateOn](auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400388 fan->powerStateChanged(powerStateOn);
389 });
Chau Ly751c8be2023-01-13 08:21:03 +0000390 if (std::all_of(_fans.begin(), _fans.end(), [](const auto& fan) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400391 return fan->numSensorsOnDBusAtPowerOn() == 0;
392 }))
Chau Ly751c8be2023-01-13 08:21:03 +0000393 {
394 handleOfflineFanController();
395 return;
396 }
397#else
Matt Spinlerbb449c12021-06-14 11:45:28 -0600398 handleOfflineFanController();
399 return;
Chau Ly751c8be2023-01-13 08:21:03 +0000400#endif
Matt Spinlerbb449c12021-06-14 11:45:28 -0600401 }
402
Mike Capps25f03272021-09-13 13:38:44 -0400403 if (_sensorMatch.empty())
404 {
405 subscribeSensorsToServices();
406 }
407
Matt Spinlere892e392020-10-14 13:21:31 -0500408 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
409 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400410 rule->check(PowerRuleState::atPgood, _fanHealth);
411 });
Matt Spinlere892e392020-10-14 13:21:31 -0500412 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
413 [this](auto& rule) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400414 rule->check(PowerRuleState::runtime, _fanHealth);
415 });
Matt Spinlere892e392020-10-14 13:21:31 -0500416 }
417 else
418 {
Matt Spinlerc8d3c512021-01-06 14:22:25 -0600419 _thermalAlert.enabled(false);
420
Matt Spinlere892e392020-10-14 13:21:31 -0500421 // Cancel any in-progress power off actions
422 std::for_each(_powerOffRules.begin(), _powerOffRules.end(),
423 [this](auto& rule) { rule->cancel(); });
424 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500425}
426
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500427void System::sensorErrorTimerExpired(const Fan& fan, const TachSensor& sensor)
428{
429 std::string fanPath{util::INVENTORY_PATH + fan.getName()};
430
431 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500432 std::format("Creating event log for faulted fan {} sensor {}", fanPath,
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500433 sensor.name()),
434 Logger::error);
435
436 // In order to know if the event log should have a severity of error or
437 // informational, count the number of existing nonfunctional sensors and
438 // compare it to _numNonfuncSensorsBeforeError.
439 size_t nonfuncSensors = 0;
440 for (const auto& fan : _fans)
441 {
442 for (const auto& s : fan->sensors())
443 {
444 // Don't count nonfunctional sensors that still have their
445 // error timer running as nonfunctional since they haven't
446 // had event logs created for those errors yet.
447 if (!s->functional() && !s->errorTimerRunning())
448 {
449 nonfuncSensors++;
450 }
451 }
452 }
453
454 Severity severity = Severity::Error;
455 if (nonfuncSensors < _numNonfuncSensorsBeforeError)
456 {
457 severity = Severity::Informational;
458 }
459
460 auto error =
461 std::make_unique<FanError>("xyz.openbmc_project.Fan.Error.Fault",
462 fanPath, sensor.name(), severity);
463
464 auto sensorData = captureSensorData();
465 error->commit(sensorData);
466
Matt Spinlerac1efc12020-10-27 10:20:11 -0500467 // Save the error so it can be committed again on a power off.
468 _lastError = std::move(error);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500469}
470
Matt Spinler27f6b682020-10-27 08:43:37 -0500471void System::fanMissingErrorTimerExpired(const Fan& fan)
472{
473 std::string fanPath{util::INVENTORY_PATH + fan.getName()};
474
475 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500476 std::format("Creating event log for missing fan {}", fanPath),
Matt Spinler27f6b682020-10-27 08:43:37 -0500477 Logger::error);
478
479 auto error = std::make_unique<FanError>(
480 "xyz.openbmc_project.Fan.Error.Missing", fanPath, "", Severity::Error);
481
482 auto sensorData = captureSensorData();
483 error->commit(sensorData);
484
Matt Spinlerac1efc12020-10-27 10:20:11 -0500485 // Save the error so it can be committed again on a power off.
486 _lastError = std::move(error);
487}
488
489void System::logShutdownError()
490{
491 if (_lastError)
492 {
493 getLogger().log("Re-committing previous fan error before power off");
494
495 // Still use the latest sensor data
496 auto sensorData = captureSensorData();
Matt Spinlerf435eb12021-05-11 14:44:25 -0500497 _lastError->commit(sensorData, true);
Matt Spinlerac1efc12020-10-27 10:20:11 -0500498 }
Matt Spinler27f6b682020-10-27 08:43:37 -0500499}
500
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500501json System::captureSensorData()
502{
503 json data;
504
505 for (const auto& fan : _fans)
506 {
507 for (const auto& sensor : fan->sensors())
508 {
509 json values;
510 values["present"] = fan->present();
511 values["functional"] = sensor->functional();
Matt Spinlerd16d4642022-08-26 13:32:07 -0500512 values["in_range"] = !fan->outOfRange(*sensor);
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500513 values["tach"] = sensor->getInput();
Mike Capps7b34ee02022-05-04 14:16:12 -0400514
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500515 if (sensor->hasTarget())
516 {
517 values["target"] = sensor->getTarget();
518 }
519
Mike Capps7b34ee02022-05-04 14:16:12 -0400520 // convert between string/json to remove newlines
521 values["prev_tachs"] = json(sensor->getPrevTach()).dump();
522
523 if (sensor->hasTarget())
524 {
525 values["prev_targets"] = json(sensor->getPrevTarget()).dump();
526 }
527
Matt Spinler87f9adc2022-08-11 13:17:09 -0500528 if (sensor->getMethod() == MethodMode::count)
529 {
530 values["ticks"] = sensor->getCounter();
531 }
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500532 data["sensors"][sensor->name()] = values;
533 }
534 }
535
536 return data;
537}
538
Matt Spinlerbb449c12021-06-14 11:45:28 -0600539void System::handleOfflineFanController()
540{
541 getLogger().log("The fan controller appears to be offline. Shutting down.",
542 Logger::error);
543
544 auto ffdc = collectHwmonFFDC();
545
546 FanError error{"xyz.openbmc_project.Fan.Error.FanControllerOffline",
547 Severity::Critical};
548 error.commit(ffdc, true);
549
550 PowerInterface::executeHardPowerOff();
Mike Capps683a96c2022-04-27 16:46:06 -0400551
552 createBmcDump();
553}
554
555/**
556 * @brief Create a BMC Dump
557 */
558void System::createBmcDump() const
559{
560 try
561 {
562 util::SDBusPlus::callMethod(
563 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump/bmc",
564 "xyz.openbmc_project.Dump.Create", "CreateDump",
565 std::vector<
566 std::pair<std::string, std::variant<std::string, uint64_t>>>());
567 }
Mike Capps477b13b2022-07-11 10:45:46 -0400568 catch (const std::exception& e)
569 {
570 getLogger().log(
Patrick Williamsfbf47032023-07-17 12:27:34 -0500571 std::format("Caught exception while creating BMC dump: {}",
Mike Capps477b13b2022-07-11 10:45:46 -0400572 e.what()),
573 Logger::error);
574 }
Matt Spinlerbb449c12021-06-14 11:45:28 -0600575}
576
Matt Spinler4f472a82022-08-26 13:55:34 -0500577void System::dumpDebugData(sdeventplus::source::Signal&,
578 const struct signalfd_siginfo*)
579{
580 json output;
581
582 if (_loaded)
583 {
584 output["logs"] = getLogger().getLogs();
585 output["sensors"] = captureSensorData();
586 }
587 else
588 {
589 output["error"] = "Fan monitor not loaded yet. Try again later.";
590 }
591
592 std::ofstream file{System::dumpFile};
593 if (!file)
594 {
Anwaar Hadia00f6832025-03-27 15:03:11 +0000595 lg2::error("Could not open file for fan monitor dump");
Matt Spinler4f472a82022-08-26 13:55:34 -0500596 }
597 else
598 {
599 file << std::setw(4) << output;
600 }
601}
602
Matthew Barthc95c5272020-06-15 19:51:13 -0500603} // namespace phosphor::fan::monitor