blob: 5919d925303577394142c2ba8214c7d3a07d69a5 [file] [log] [blame]
Matt Spinlerabf8da32017-04-27 14:08:45 -05001/**
2 * Copyright © 2017 IBM 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 */
Matt Spinlerabf8da32017-04-27 14:08:45 -050016#include "fan.hpp"
Matthew Barth177fe982020-05-26 11:05:19 -050017
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050018#include "logging.hpp"
Matthew Barth177fe982020-05-26 11:05:19 -050019#include "sdbusplus.hpp"
Matt Spinlerb0412d02020-10-12 16:53:52 -050020#include "system.hpp"
Matt Spinlerabf8da32017-04-27 14:08:45 -050021#include "types.hpp"
Matt Spinlerb1e18512017-04-27 14:42:33 -050022#include "utility.hpp"
Matthew Barth177fe982020-05-26 11:05:19 -050023
Jay Meyera7aed012020-10-06 14:32:22 -050024#include <fmt/format.h>
25
Matthew Barth177fe982020-05-26 11:05:19 -050026#include <phosphor-logging/log.hpp>
27
28#include <algorithm>
Matt Spinlerabf8da32017-04-27 14:08:45 -050029
30namespace phosphor
31{
32namespace fan
33{
34namespace monitor
35{
36
37using namespace phosphor::logging;
Matt Spinlerb0412d02020-10-12 16:53:52 -050038using namespace sdbusplus::bus::match;
Matt Spinlerabf8da32017-04-27 14:08:45 -050039
Matthew Barth177fe982020-05-26 11:05:19 -050040Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event,
Matt Spinlerb0412d02020-10-12 16:53:52 -050041 std::unique_ptr<trust::Manager>& trust, const FanDefinition& def,
42 System& system) :
Matt Spinlerabf8da32017-04-27 14:08:45 -050043 _bus(bus),
44 _name(std::get<fanNameField>(def)),
45 _deviation(std::get<fanDeviationField>(def)),
Matt Spinlerc39e8592017-09-28 13:13:08 -050046 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
Matt Spinlerb0412d02020-10-12 16:53:52 -050047 _trustManager(trust),
48#ifdef MONITOR_USE_JSON
49 _monitorDelay(std::get<monitorStartDelayField>(def)),
50 _monitorTimer(event, std::bind(std::mem_fn(&Fan::startMonitor), this)),
51#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -050052 _system(system),
53 _presenceMatch(bus,
54 rules::propertiesChanged(util::INVENTORY_PATH + _name,
55 util::INV_ITEM_IFACE),
56 std::bind(std::mem_fn(&Fan::presenceChanged), this,
Matt Spinler27f6b682020-10-27 08:43:37 -050057 std::placeholders::_1)),
Matt Spinler7d135642021-02-04 12:44:17 -060058 _presenceIfaceAddedMatch(
59 bus,
60 rules::interfacesAdded() +
61 rules::argNpath(0, util::INVENTORY_PATH + _name),
62 std::bind(std::mem_fn(&Fan::presenceIfaceAdded), this,
63 std::placeholders::_1)),
Matt Spinler27f6b682020-10-27 08:43:37 -050064 _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def))
Matt Spinlerabf8da32017-04-27 14:08:45 -050065{
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050066 // Start from a known state of functional (even if
67 // _numSensorFailsForNonFunc is 0)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080068 updateInventory(true);
69
Matthew Barth0a9fe162018-01-26 12:53:15 -060070 // Setup tach sensors for monitoring
71 auto& sensors = std::get<sensorListField>(def);
72 for (auto& s : sensors)
73 {
74 try
75 {
Matthew Barth177fe982020-05-26 11:05:19 -050076 _sensors.emplace_back(std::make_shared<TachSensor>(
77 mode, bus, *this, std::get<sensorNameField>(s),
78 std::get<hasTargetField>(s), std::get<funcDelay>(def),
79 std::get<targetInterfaceField>(s), std::get<factorField>(s),
Jolie Ku69f2f482020-10-21 09:59:43 +080080 std::get<offsetField>(s), std::get<methodField>(def),
81 std::get<thresholdField>(s), std::get<timeoutField>(def),
Matt Spinlerf13b42e2020-10-26 15:29:49 -050082 std::get<nonfuncRotorErrDelayField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060083
84 _trustManager->registerSensor(_sensors.back());
85 }
86 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080087 {
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050088 // Count the number of failed tach sensors, though if
89 // _numSensorFailsForNonFunc is zero that means the fan should not
90 // be set to nonfunctional.
91 if (_numSensorFailsForNonFunc &&
92 (++_numFailedSensor >= _numSensorFailsForNonFunc))
Jolie Ku5d564a92020-10-23 09:04:28 +080093 {
94 // Mark associated fan as nonfunctional
95 updateInventory(false);
96 }
Jolie Ku4c3c24f2020-09-09 11:01:46 +080097 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060098 }
99
Matt Spinlerb0412d02020-10-12 16:53:52 -0500100#ifndef MONITOR_USE_JSON
Matthew Barth0a9fe162018-01-26 12:53:15 -0600101 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -0500102 if (mode != Mode::init)
103 {
Matt Spinlerb0412d02020-10-12 16:53:52 -0500104 _monitorReady = true;
105
Matthew Barth177fe982020-05-26 11:05:19 -0500106 // The TachSensors will now have already read the input
107 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -0500108 tachChanged();
109 }
Matt Spinlerb0412d02020-10-12 16:53:52 -0500110#else
Matt Spinler7d135642021-02-04 12:44:17 -0600111 if (_system.isPowerOn())
112 {
113 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
114 }
Matt Spinlerb0412d02020-10-12 16:53:52 -0500115#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -0500116
Matt Spinler27f6b682020-10-27 08:43:37 -0500117 if (_fanMissingErrorDelay)
118 {
119 _fanMissingErrorTimer = std::make_unique<
120 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
121 event, std::bind(&System::fanMissingErrorTimerExpired, &system,
122 std::ref(*this)));
Matt Spinler7d135642021-02-04 12:44:17 -0600123 }
Matt Spinler27f6b682020-10-27 08:43:37 -0500124
Matt Spinler7d135642021-02-04 12:44:17 -0600125 try
126 {
127 _present = util::SDBusPlus::getProperty<bool>(
128 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
129
130 if (!_present)
Matt Spinler27f6b682020-10-27 08:43:37 -0500131 {
Matt Spinlerac372972021-01-25 15:11:22 -0600132 getLogger().log(
133 fmt::format("On startup, fan {} is missing", _name));
Matt Spinler7d135642021-02-04 12:44:17 -0600134 if (_system.isPowerOn() && _fanMissingErrorTimer)
135 {
136 _fanMissingErrorTimer->restartOnce(
137 std::chrono::seconds{*_fanMissingErrorDelay});
138 }
139 }
140 }
141 catch (const util::DBusServiceError& e)
142 {
143 // This could happen on the first BMC boot if the presence
144 // detect app hasn't started yet and there isn't an inventory
145 // cache yet.
146 }
147}
148
149void Fan::presenceIfaceAdded(sdbusplus::message::message& msg)
150{
151 sdbusplus::message::object_path path;
152 std::map<std::string, std::map<std::string, std::variant<bool>>> interfaces;
153
154 msg.read(path, interfaces);
155
156 auto properties = interfaces.find(util::INV_ITEM_IFACE);
157 if (properties == interfaces.end())
158 {
159 return;
160 }
161
162 auto property = properties->second.find("Present");
163 if (property == properties->second.end())
164 {
165 return;
166 }
167
168 _present = std::get<bool>(property->second);
169
170 if (!_present)
171 {
172 getLogger().log(fmt::format(
173 "New fan {} interface added and fan is not present", _name));
174 if (_system.isPowerOn() && _fanMissingErrorTimer)
175 {
Matt Spinler27f6b682020-10-27 08:43:37 -0500176 _fanMissingErrorTimer->restartOnce(
177 std::chrono::seconds{*_fanMissingErrorDelay});
178 }
179 }
Matt Spinler7d135642021-02-04 12:44:17 -0600180
181 _system.fanStatusChange(*this);
Matt Spinlerb0412d02020-10-12 16:53:52 -0500182}
183
184void Fan::startMonitor()
185{
186 _monitorReady = true;
187
188 tachChanged();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500189}
190
Matt Spinlerebaae612017-04-27 14:21:48 -0500191void Fan::tachChanged()
192{
Matt Spinlerb0412d02020-10-12 16:53:52 -0500193 if (_monitorReady)
Matt Spinlerebaae612017-04-27 14:21:48 -0500194 {
Matt Spinlerb0412d02020-10-12 16:53:52 -0500195 for (auto& s : _sensors)
196 {
197 tachChanged(*s);
198 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500199 }
200}
201
Matt Spinlerebaae612017-04-27 14:21:48 -0500202void Fan::tachChanged(TachSensor& sensor)
203{
Matt Spinler7d135642021-02-04 12:44:17 -0600204 if (!_system.isPowerOn() || !_monitorReady)
205 {
206 return;
207 }
208
Matt Spinlerc39e8592017-09-28 13:13:08 -0500209 if (_trustManager->active())
210 {
211 if (!_trustManager->checkTrust(sensor))
212 {
213 return;
214 }
215 }
216
Matthew Barthfcb0dbc2021-02-10 14:23:39 -0600217 process(sensor);
218}
219
220void Fan::process(TachSensor& sensor)
221{
Matthew Barth177fe982020-05-26 11:05:19 -0500222 // If this sensor is out of range at this moment, start
223 // its timer, at the end of which the inventory
224 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500225
Matthew Barth177fe982020-05-26 11:05:19 -0500226 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500227
228 if (outOfRange(sensor))
229 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600230 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500231 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800232 switch (sensor.getMethod())
233 {
234 case MethodMode::timebased:
235 // Start nonfunctional timer if not already running
236 sensor.startTimer(TimerMode::nonfunc);
237 break;
238 case MethodMode::count:
239 sensor.setCounter(true);
240 if (sensor.getCounter() >= sensor.getThreshold())
241 {
242 updateState(sensor);
243 }
244 break;
245 }
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500246 }
247 }
248 else
249 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800250 switch (sensor.getMethod())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500251 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800252 case MethodMode::timebased:
253 if (sensor.functional())
254 {
Matthew Barth11b5d8f2021-01-28 14:04:09 -0600255 if (sensor.timerRunning())
256 {
257 sensor.stopTimer();
258 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800259 }
260 else
261 {
262 // Start functional timer if not already running
263 sensor.startTimer(TimerMode::func);
264 }
265 break;
266 case MethodMode::count:
267 sensor.setCounter(false);
268 if (!sensor.functional() && sensor.getCounter() == 0)
269 {
270 updateState(sensor);
271 }
272 break;
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500273 }
274 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500275}
276
Matthew Barthf552ea52018-01-15 16:22:04 -0600277uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500278{
279 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500280 // The sensor doesn't support a target,
281 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600282 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500283 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500284
Matthew Barthf552ea52018-01-15 16:22:04 -0600285 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500286 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600287 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500288 }
289
290 return target;
291}
292
Matthew Barth7c23a042021-01-26 16:21:45 -0600293size_t Fan::countNonFunctionalSensors()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500294{
Matthew Barth7c23a042021-01-26 16:21:45 -0600295 return std::count_if(_sensors.begin(), _sensors.end(),
296 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500297}
298
Matt Spinlerabf8da32017-04-27 14:08:45 -0500299bool Fan::outOfRange(const TachSensor& sensor)
300{
301 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth7c23a042021-01-26 16:21:45 -0600302 auto range = sensor.getRange(_deviation);
Matt Spinlerabf8da32017-04-27 14:08:45 -0500303
Matthew Barth7c23a042021-01-26 16:21:45 -0600304 if ((actual < range.first) || (actual > range.second))
Matt Spinlerabf8da32017-04-27 14:08:45 -0500305 {
306 return true;
307 }
308
309 return false;
310}
311
Jolie Ku69f2f482020-10-21 09:59:43 +0800312void Fan::updateState(TachSensor& sensor)
Matt Spinlera9406a72017-04-27 14:29:24 -0500313{
Matthew Barth7c23a042021-01-26 16:21:45 -0600314 auto range = sensor.getRange(_deviation);
Matt Spinler7d135642021-02-04 12:44:17 -0600315
316 if (!_system.isPowerOn())
317 {
318 return;
319 }
320
Matthew Barthe11cbc62018-02-20 12:11:07 -0600321 sensor.setFunctional(!sensor.functional());
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500322 getLogger().log(
323 fmt::format("Setting tach sensor {} functional state to {}. "
Matthew Barth7c23a042021-01-26 16:21:45 -0600324 "[target = {}, input = {}, allowed range = ({} - {})]",
325 sensor.name(), sensor.functional(), sensor.getTarget(),
326 sensor.getInput(), range.first, range.second));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500327
328 // A zero value for _numSensorFailsForNonFunc means we aren't dealing
329 // with fan FRU functional status, only sensor functional status.
330 if (_numSensorFailsForNonFunc)
Matthew Barthe11cbc62018-02-20 12:11:07 -0600331 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600332 auto numNonFuncSensors = countNonFunctionalSensors();
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500333 // If the fan was nonfunctional and enough sensors are now OK,
Matthew Barth7c23a042021-01-26 16:21:45 -0600334 // the fan can be set to functional
335 if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc))
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500336 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600337 getLogger().log(fmt::format("Setting fan {} to functional, number "
338 "of nonfunctional sensors = {}",
339 _name, numNonFuncSensors));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500340 updateInventory(true);
341 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500342
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500343 // If the fan is currently functional, but too many
344 // contained sensors are now nonfunctional, update
Matthew Barth7c23a042021-01-26 16:21:45 -0600345 // the fan to nonfunctional.
346 if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc))
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500347 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600348 getLogger().log(fmt::format("Setting fan {} to nonfunctional, "
349 "number of nonfunctional sensors = {}",
350 _name, numNonFuncSensors));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500351 updateInventory(false);
352 }
Matt Spinlerb1e18512017-04-27 14:42:33 -0500353 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500354
355 _system.fanStatusChange(*this);
Matt Spinlera9406a72017-04-27 14:29:24 -0500356}
357
Matt Spinlerb1e18512017-04-27 14:42:33 -0500358void Fan::updateInventory(bool functional)
359{
Matthew Barth177fe982020-05-26 11:05:19 -0500360 auto objectMap =
361 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
362 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600363 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500364 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500365 if (response.is_method_error())
366 {
367 log<level::ERR>("Error in Notify call to update inventory");
368 return;
369 }
370
Matthew Barth177fe982020-05-26 11:05:19 -0500371 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500372 _functional = functional;
373}
374
Matt Spinlerb63aa092020-10-14 09:45:11 -0500375void Fan::presenceChanged(sdbusplus::message::message& msg)
376{
377 std::string interface;
378 std::map<std::string, std::variant<bool>> properties;
379
380 msg.read(interface, properties);
381
382 auto presentProp = properties.find("Present");
383 if (presentProp != properties.end())
384 {
385 _present = std::get<bool>(presentProp->second);
386
Matt Spinler27f6b682020-10-27 08:43:37 -0500387 getLogger().log(
Matt Spinlerac372972021-01-25 15:11:22 -0600388 fmt::format("Fan {} presence state change to {}", _name, _present));
Matt Spinler27f6b682020-10-27 08:43:37 -0500389
Matt Spinlerb63aa092020-10-14 09:45:11 -0500390 _system.fanStatusChange(*this);
Matt Spinler27f6b682020-10-27 08:43:37 -0500391
392 if (_fanMissingErrorDelay)
393 {
Matt Spinler7d135642021-02-04 12:44:17 -0600394 if (!_present && _system.isPowerOn())
Matt Spinler27f6b682020-10-27 08:43:37 -0500395 {
396 _fanMissingErrorTimer->restartOnce(
397 std::chrono::seconds{*_fanMissingErrorDelay});
398 }
Matt Spinler7d135642021-02-04 12:44:17 -0600399 else if (_present && _fanMissingErrorTimer->isEnabled())
Matt Spinler27f6b682020-10-27 08:43:37 -0500400 {
401 _fanMissingErrorTimer->setEnabled(false);
402 }
403 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500404 }
405}
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500406
407void Fan::sensorErrorTimerExpired(const TachSensor& sensor)
408{
Matt Spinler7d135642021-02-04 12:44:17 -0600409 if (_present && _system.isPowerOn())
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500410 {
411 _system.sensorErrorTimerExpired(*this, sensor);
412 }
413}
414
Matt Spinler7d135642021-02-04 12:44:17 -0600415void Fan::powerStateChanged(bool powerStateOn)
416{
417#ifdef MONITOR_USE_JSON
418 if (powerStateOn)
419 {
420 // set all fans back to functional to start with
421 std::for_each(_sensors.begin(), _sensors.end(),
422 [](auto& sensor) { sensor->setFunctional(true); });
423
424 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
425
426 if (!_present)
427 {
428 getLogger().log(
429 fmt::format("At power on, fan {} is missing", _name));
430
431 if (_fanMissingErrorTimer)
432 {
433 _fanMissingErrorTimer->restartOnce(
434 std::chrono::seconds{*_fanMissingErrorDelay});
435 }
436 }
437 }
438 else
439 {
440 _monitorReady = false;
441
442 if (_monitorTimer.isEnabled())
443 {
444 _monitorTimer.setEnabled(false);
445 }
446
447 if (_fanMissingErrorTimer && _fanMissingErrorTimer->isEnabled())
448 {
449 _fanMissingErrorTimer->setEnabled(false);
450 }
451
452 std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) {
453 if (sensor->timerRunning())
454 {
455 sensor->stopTimer();
456 }
457 });
458 }
459#endif
460}
461
Matthew Barth177fe982020-05-26 11:05:19 -0500462} // namespace monitor
463} // namespace fan
464} // namespace phosphor