blob: 18e9d059b880ca0aa3d0760af9a9a16736709edb [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)),
58 _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def))
Matt Spinlerabf8da32017-04-27 14:08:45 -050059{
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050060 // Start from a known state of functional (even if
61 // _numSensorFailsForNonFunc is 0)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080062 updateInventory(true);
63
Matthew Barth0a9fe162018-01-26 12:53:15 -060064 // Setup tach sensors for monitoring
65 auto& sensors = std::get<sensorListField>(def);
66 for (auto& s : sensors)
67 {
68 try
69 {
Matthew Barth177fe982020-05-26 11:05:19 -050070 _sensors.emplace_back(std::make_shared<TachSensor>(
71 mode, bus, *this, std::get<sensorNameField>(s),
72 std::get<hasTargetField>(s), std::get<funcDelay>(def),
73 std::get<targetInterfaceField>(s), std::get<factorField>(s),
Jolie Ku69f2f482020-10-21 09:59:43 +080074 std::get<offsetField>(s), std::get<methodField>(def),
75 std::get<thresholdField>(s), std::get<timeoutField>(def),
Matt Spinlerf13b42e2020-10-26 15:29:49 -050076 std::get<nonfuncRotorErrDelayField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060077
78 _trustManager->registerSensor(_sensors.back());
79 }
80 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080081 {
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050082 // Count the number of failed tach sensors, though if
83 // _numSensorFailsForNonFunc is zero that means the fan should not
84 // be set to nonfunctional.
85 if (_numSensorFailsForNonFunc &&
86 (++_numFailedSensor >= _numSensorFailsForNonFunc))
Jolie Ku5d564a92020-10-23 09:04:28 +080087 {
88 // Mark associated fan as nonfunctional
89 updateInventory(false);
90 }
Jolie Ku4c3c24f2020-09-09 11:01:46 +080091 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060092 }
93
Matt Spinlerb0412d02020-10-12 16:53:52 -050094#ifndef MONITOR_USE_JSON
Matthew Barth0a9fe162018-01-26 12:53:15 -060095 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050096 if (mode != Mode::init)
97 {
Matt Spinlerb0412d02020-10-12 16:53:52 -050098 _monitorReady = true;
99
Matthew Barth177fe982020-05-26 11:05:19 -0500100 // The TachSensors will now have already read the input
101 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -0500102 tachChanged();
103 }
Matt Spinlerb0412d02020-10-12 16:53:52 -0500104#else
105 // If it used the JSON config, then it also will do all the work
106 // out of fan-monitor-init, after _monitorDelay.
107 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
Matt Spinlerb0412d02020-10-12 16:53:52 -0500108#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -0500109
110 // Get the initial presence state
Matt Spinler06480142021-01-20 13:45:31 -0600111 bool available = true;
112
113 try
114 {
115 _present = util::SDBusPlus::getProperty<bool>(
116 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
117 }
118 catch (const util::DBusServiceError& e)
119 {
120 // This could be the initial boot and phosphor-fan-presence hasn't
121 // written to the inventory yet.
122 available = false;
123 }
Matt Spinler27f6b682020-10-27 08:43:37 -0500124
125 if (_fanMissingErrorDelay)
126 {
127 _fanMissingErrorTimer = std::make_unique<
128 sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
129 event, std::bind(&System::fanMissingErrorTimerExpired, &system,
130 std::ref(*this)));
131
Matt Spinler06480142021-01-20 13:45:31 -0600132 if (!_present && available)
Matt Spinler27f6b682020-10-27 08:43:37 -0500133 {
Matt Spinlerac372972021-01-25 15:11:22 -0600134 getLogger().log(
135 fmt::format("On startup, fan {} is missing", _name));
Matt Spinler27f6b682020-10-27 08:43:37 -0500136 _fanMissingErrorTimer->restartOnce(
137 std::chrono::seconds{*_fanMissingErrorDelay});
138 }
139 }
Matt Spinlerb0412d02020-10-12 16:53:52 -0500140}
141
142void Fan::startMonitor()
143{
144 _monitorReady = true;
145
146 tachChanged();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500147}
148
Matt Spinlerebaae612017-04-27 14:21:48 -0500149void Fan::tachChanged()
150{
Matt Spinlerb0412d02020-10-12 16:53:52 -0500151 if (_monitorReady)
Matt Spinlerebaae612017-04-27 14:21:48 -0500152 {
Matt Spinlerb0412d02020-10-12 16:53:52 -0500153 for (auto& s : _sensors)
154 {
155 tachChanged(*s);
156 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500157 }
158}
159
Matt Spinlerebaae612017-04-27 14:21:48 -0500160void Fan::tachChanged(TachSensor& sensor)
161{
Matt Spinlerc39e8592017-09-28 13:13:08 -0500162 if (_trustManager->active())
163 {
164 if (!_trustManager->checkTrust(sensor))
165 {
166 return;
167 }
168 }
169
Matthew Barthfcb0dbc2021-02-10 14:23:39 -0600170 process(sensor);
171}
172
173void Fan::process(TachSensor& sensor)
174{
Matthew Barth177fe982020-05-26 11:05:19 -0500175 // If this sensor is out of range at this moment, start
176 // its timer, at the end of which the inventory
177 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500178
Matthew Barth177fe982020-05-26 11:05:19 -0500179 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500180
181 if (outOfRange(sensor))
182 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600183 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500184 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800185 switch (sensor.getMethod())
186 {
187 case MethodMode::timebased:
188 // Start nonfunctional timer if not already running
189 sensor.startTimer(TimerMode::nonfunc);
190 break;
191 case MethodMode::count:
192 sensor.setCounter(true);
193 if (sensor.getCounter() >= sensor.getThreshold())
194 {
195 updateState(sensor);
196 }
197 break;
198 }
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500199 }
200 }
201 else
202 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800203 switch (sensor.getMethod())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500204 {
Jolie Ku69f2f482020-10-21 09:59:43 +0800205 case MethodMode::timebased:
206 if (sensor.functional())
207 {
Matthew Barth11b5d8f2021-01-28 14:04:09 -0600208 if (sensor.timerRunning())
209 {
210 sensor.stopTimer();
211 }
Jolie Ku69f2f482020-10-21 09:59:43 +0800212 }
213 else
214 {
215 // Start functional timer if not already running
216 sensor.startTimer(TimerMode::func);
217 }
218 break;
219 case MethodMode::count:
220 sensor.setCounter(false);
221 if (!sensor.functional() && sensor.getCounter() == 0)
222 {
223 updateState(sensor);
224 }
225 break;
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500226 }
227 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500228}
229
Matthew Barthf552ea52018-01-15 16:22:04 -0600230uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500231{
232 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500233 // The sensor doesn't support a target,
234 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600235 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500236 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500237
Matthew Barthf552ea52018-01-15 16:22:04 -0600238 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500239 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600240 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500241 }
242
243 return target;
244}
245
Matthew Barth7c23a042021-01-26 16:21:45 -0600246size_t Fan::countNonFunctionalSensors()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500247{
Matthew Barth7c23a042021-01-26 16:21:45 -0600248 return std::count_if(_sensors.begin(), _sensors.end(),
249 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500250}
251
Matt Spinlerabf8da32017-04-27 14:08:45 -0500252bool Fan::outOfRange(const TachSensor& sensor)
253{
254 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth7c23a042021-01-26 16:21:45 -0600255 auto range = sensor.getRange(_deviation);
Matt Spinlerabf8da32017-04-27 14:08:45 -0500256
Matthew Barth7c23a042021-01-26 16:21:45 -0600257 if ((actual < range.first) || (actual > range.second))
Matt Spinlerabf8da32017-04-27 14:08:45 -0500258 {
259 return true;
260 }
261
262 return false;
263}
264
Jolie Ku69f2f482020-10-21 09:59:43 +0800265void Fan::updateState(TachSensor& sensor)
Matt Spinlera9406a72017-04-27 14:29:24 -0500266{
Matthew Barth7c23a042021-01-26 16:21:45 -0600267 auto range = sensor.getRange(_deviation);
Matthew Barthe11cbc62018-02-20 12:11:07 -0600268 sensor.setFunctional(!sensor.functional());
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500269 getLogger().log(
270 fmt::format("Setting tach sensor {} functional state to {}. "
Matthew Barth7c23a042021-01-26 16:21:45 -0600271 "[target = {}, input = {}, allowed range = ({} - {})]",
272 sensor.name(), sensor.functional(), sensor.getTarget(),
273 sensor.getInput(), range.first, range.second));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500274
275 // A zero value for _numSensorFailsForNonFunc means we aren't dealing
276 // with fan FRU functional status, only sensor functional status.
277 if (_numSensorFailsForNonFunc)
Matthew Barthe11cbc62018-02-20 12:11:07 -0600278 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600279 auto numNonFuncSensors = countNonFunctionalSensors();
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500280 // If the fan was nonfunctional and enough sensors are now OK,
Matthew Barth7c23a042021-01-26 16:21:45 -0600281 // the fan can be set to functional
282 if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc))
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500283 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600284 getLogger().log(fmt::format("Setting fan {} to functional, number "
285 "of nonfunctional sensors = {}",
286 _name, numNonFuncSensors));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500287 updateInventory(true);
288 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500289
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500290 // If the fan is currently functional, but too many
291 // contained sensors are now nonfunctional, update
Matthew Barth7c23a042021-01-26 16:21:45 -0600292 // the fan to nonfunctional.
293 if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc))
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500294 {
Matthew Barth7c23a042021-01-26 16:21:45 -0600295 getLogger().log(fmt::format("Setting fan {} to nonfunctional, "
296 "number of nonfunctional sensors = {}",
297 _name, numNonFuncSensors));
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500298 updateInventory(false);
299 }
Matt Spinlerb1e18512017-04-27 14:42:33 -0500300 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500301
302 _system.fanStatusChange(*this);
Matt Spinlera9406a72017-04-27 14:29:24 -0500303}
304
Matt Spinlerb1e18512017-04-27 14:42:33 -0500305void Fan::updateInventory(bool functional)
306{
Matthew Barth177fe982020-05-26 11:05:19 -0500307 auto objectMap =
308 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
309 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600310 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500311 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500312 if (response.is_method_error())
313 {
314 log<level::ERR>("Error in Notify call to update inventory");
315 return;
316 }
317
Matthew Barth177fe982020-05-26 11:05:19 -0500318 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500319 _functional = functional;
320}
321
Matt Spinlerb63aa092020-10-14 09:45:11 -0500322void Fan::presenceChanged(sdbusplus::message::message& msg)
323{
324 std::string interface;
325 std::map<std::string, std::variant<bool>> properties;
326
327 msg.read(interface, properties);
328
329 auto presentProp = properties.find("Present");
330 if (presentProp != properties.end())
331 {
332 _present = std::get<bool>(presentProp->second);
333
Matt Spinler27f6b682020-10-27 08:43:37 -0500334 getLogger().log(
Matt Spinlerac372972021-01-25 15:11:22 -0600335 fmt::format("Fan {} presence state change to {}", _name, _present));
Matt Spinler27f6b682020-10-27 08:43:37 -0500336
Matt Spinlerb63aa092020-10-14 09:45:11 -0500337 _system.fanStatusChange(*this);
Matt Spinler27f6b682020-10-27 08:43:37 -0500338
339 if (_fanMissingErrorDelay)
340 {
341 if (!_present)
342 {
343 _fanMissingErrorTimer->restartOnce(
344 std::chrono::seconds{*_fanMissingErrorDelay});
345 }
346 else if (_fanMissingErrorTimer->isEnabled())
347 {
348 _fanMissingErrorTimer->setEnabled(false);
349 }
350 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500351 }
352}
Matt Spinlerf13b42e2020-10-26 15:29:49 -0500353
354void Fan::sensorErrorTimerExpired(const TachSensor& sensor)
355{
356 if (_present)
357 {
358 _system.sensorErrorTimerExpired(*this, sensor);
359 }
360}
361
Matthew Barth177fe982020-05-26 11:05:19 -0500362} // namespace monitor
363} // namespace fan
364} // namespace phosphor