blob: 3b12905e253ee5db9ec7e2540a3b9ac1105a8b7f [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
18#include "sdbusplus.hpp"
Matt Spinlerabf8da32017-04-27 14:08:45 -050019#include "types.hpp"
Matt Spinlerb1e18512017-04-27 14:42:33 -050020#include "utility.hpp"
Matthew Barth177fe982020-05-26 11:05:19 -050021
Jay Meyera7aed012020-10-06 14:32:22 -050022#include <fmt/format.h>
23
Matthew Barth177fe982020-05-26 11:05:19 -050024#include <phosphor-logging/log.hpp>
25
26#include <algorithm>
Matt Spinlerabf8da32017-04-27 14:08:45 -050027
28namespace phosphor
29{
30namespace fan
31{
32namespace monitor
33{
34
35using namespace phosphor::logging;
36
Matthew Barth177fe982020-05-26 11:05:19 -050037Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event,
38 std::unique_ptr<trust::Manager>& trust, const FanDefinition& def) :
Matt Spinlerabf8da32017-04-27 14:08:45 -050039 _bus(bus),
40 _name(std::get<fanNameField>(def)),
41 _deviation(std::get<fanDeviationField>(def)),
Matt Spinlerc39e8592017-09-28 13:13:08 -050042 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
43 _trustManager(trust)
Matt Spinlerabf8da32017-04-27 14:08:45 -050044{
Jolie Ku4c3c24f2020-09-09 11:01:46 +080045 // Start from a known state of functional
46 updateInventory(true);
47
Matthew Barth0a9fe162018-01-26 12:53:15 -060048 // Setup tach sensors for monitoring
49 auto& sensors = std::get<sensorListField>(def);
50 for (auto& s : sensors)
51 {
52 try
53 {
Matthew Barth177fe982020-05-26 11:05:19 -050054 _sensors.emplace_back(std::make_shared<TachSensor>(
55 mode, bus, *this, std::get<sensorNameField>(s),
56 std::get<hasTargetField>(s), std::get<funcDelay>(def),
57 std::get<targetInterfaceField>(s), std::get<factorField>(s),
58 std::get<offsetField>(s), std::get<timeoutField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060059
60 _trustManager->registerSensor(_sensors.back());
61 }
62 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080063 {
Jolie Ku5d564a92020-10-23 09:04:28 +080064 // Count the number of failed tach sensors
65 if (++_numFailedSensor >= _numSensorFailsForNonFunc)
66 {
67 // Mark associated fan as nonfunctional
68 updateInventory(false);
69 }
Jolie Ku4c3c24f2020-09-09 11:01:46 +080070 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060071 }
72
Matthew Barth0a9fe162018-01-26 12:53:15 -060073 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050074 if (mode != Mode::init)
75 {
Matthew Barth177fe982020-05-26 11:05:19 -050076 // The TachSensors will now have already read the input
77 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -050078 tachChanged();
79 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050080}
81
Matt Spinlerebaae612017-04-27 14:21:48 -050082void Fan::tachChanged()
83{
84 for (auto& s : _sensors)
85 {
86 tachChanged(*s);
87 }
88}
89
Matt Spinlerebaae612017-04-27 14:21:48 -050090void Fan::tachChanged(TachSensor& sensor)
91{
Matt Spinlerc39e8592017-09-28 13:13:08 -050092 if (_trustManager->active())
93 {
94 if (!_trustManager->checkTrust(sensor))
95 {
96 return;
97 }
98 }
99
Matthew Barth177fe982020-05-26 11:05:19 -0500100 // If this sensor is out of range at this moment, start
101 // its timer, at the end of which the inventory
102 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500103
Matthew Barth177fe982020-05-26 11:05:19 -0500104 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500105
106 if (outOfRange(sensor))
107 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600108 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500109 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600110 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600111 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500112 }
113 }
114 else
115 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600116 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500117 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500118 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500119 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600120 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500121 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600122 // Start functional timer if not already running
123 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500124 }
125 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500126}
127
Matthew Barthf552ea52018-01-15 16:22:04 -0600128uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500129{
130 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500131 // The sensor doesn't support a target,
132 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600133 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500134 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500135
Matthew Barthf552ea52018-01-15 16:22:04 -0600136 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500137 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600138 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500139 }
140
141 return target;
142}
143
Matt Spinlerabf8da32017-04-27 14:08:45 -0500144bool Fan::tooManySensorsNonfunctional()
145{
Matthew Barth177fe982020-05-26 11:05:19 -0500146 size_t numFailed =
147 std::count_if(_sensors.begin(), _sensors.end(),
148 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500149
150 return (numFailed >= _numSensorFailsForNonFunc);
151}
152
Matt Spinlerabf8da32017-04-27 14:08:45 -0500153bool Fan::outOfRange(const TachSensor& sensor)
154{
155 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600156 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800157 auto factor = sensor.getFactor();
158 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500159
160 uint64_t min = target * (100 - _deviation) / 100;
161 uint64_t max = target * (100 + _deviation) / 100;
162
Lei YU8e5d1972018-01-26 17:14:00 +0800163 // TODO: openbmc/openbmc#2937 enhance this function
164 // either by making it virtual, or by predefining different
165 // outOfRange ops and selecting by yaml config
166 min = min * factor + offset;
167 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500168 if ((actual < min) || (actual > max))
169 {
170 return true;
171 }
172
173 return false;
174}
175
Matt Spinlera9406a72017-04-27 14:29:24 -0500176void Fan::timerExpired(TachSensor& sensor)
177{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600178 sensor.setFunctional(!sensor.functional());
179
Matthew Barth177fe982020-05-26 11:05:19 -0500180 // If the fan was nonfunctional and enough sensors are now OK,
181 // the fan can go back to functional
Matthew Barthe11cbc62018-02-20 12:11:07 -0600182 if (!_functional && !tooManySensorsNonfunctional())
183 {
Jay Meyera7aed012020-10-06 14:32:22 -0500184 log<level::INFO>(
185 fmt::format("Setting fan {} back to functional", _name).c_str());
Matthew Barthe11cbc62018-02-20 12:11:07 -0600186
187 updateInventory(true);
188 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500189
Matthew Barth177fe982020-05-26 11:05:19 -0500190 // If the fan is currently functional, but too many
191 // contained sensors are now nonfunctional, update
192 // the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500193 if (_functional && tooManySensorsNonfunctional())
194 {
Jay Meyera7aed012020-10-06 14:32:22 -0500195 log<level::ERR>(fmt::format("Setting fan {} to nonfunctional "
196 "Sensor: {} "
197 "Actual speed: {} "
198 "Target speed: {}",
199 _name, sensor.name(), sensor.getInput(),
200 sensor.getTarget())
201 .c_str());
Matt Spinlerb1e18512017-04-27 14:42:33 -0500202
203 updateInventory(false);
204 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500205}
206
Matt Spinlerb1e18512017-04-27 14:42:33 -0500207void Fan::updateInventory(bool functional)
208{
Matthew Barth177fe982020-05-26 11:05:19 -0500209 auto objectMap =
210 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
211 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600212 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500213 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500214 if (response.is_method_error())
215 {
216 log<level::ERR>("Error in Notify call to update inventory");
217 return;
218 }
219
Matthew Barth177fe982020-05-26 11:05:19 -0500220 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500221 _functional = functional;
222}
223
Matthew Barth177fe982020-05-26 11:05:19 -0500224} // namespace monitor
225} // namespace fan
226} // namespace phosphor