blob: 9fbe0816572a34c34b9f294dcd8825cf58c3d0a6 [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
22#include <phosphor-logging/log.hpp>
23
24#include <algorithm>
Matt Spinlerabf8da32017-04-27 14:08:45 -050025
26namespace phosphor
27{
28namespace fan
29{
30namespace monitor
31{
32
33using namespace phosphor::logging;
34
Matthew Barth177fe982020-05-26 11:05:19 -050035Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event,
36 std::unique_ptr<trust::Manager>& trust, const FanDefinition& def) :
Matt Spinlerabf8da32017-04-27 14:08:45 -050037 _bus(bus),
38 _name(std::get<fanNameField>(def)),
39 _deviation(std::get<fanDeviationField>(def)),
Matt Spinlerc39e8592017-09-28 13:13:08 -050040 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
41 _trustManager(trust)
Matt Spinlerabf8da32017-04-27 14:08:45 -050042{
Jolie Ku4c3c24f2020-09-09 11:01:46 +080043 // Start from a known state of functional
44 updateInventory(true);
45
Matthew Barth0a9fe162018-01-26 12:53:15 -060046 // Setup tach sensors for monitoring
47 auto& sensors = std::get<sensorListField>(def);
48 for (auto& s : sensors)
49 {
50 try
51 {
Matthew Barth177fe982020-05-26 11:05:19 -050052 _sensors.emplace_back(std::make_shared<TachSensor>(
53 mode, bus, *this, std::get<sensorNameField>(s),
54 std::get<hasTargetField>(s), std::get<funcDelay>(def),
55 std::get<targetInterfaceField>(s), std::get<factorField>(s),
56 std::get<offsetField>(s), std::get<timeoutField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060057
58 _trustManager->registerSensor(_sensors.back());
59 }
60 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080061 {
62 // mark associated fan as nonfunctional
63 updateInventory(false);
64 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060065 }
66
Matthew Barth0a9fe162018-01-26 12:53:15 -060067 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050068 if (mode != Mode::init)
69 {
Matthew Barth177fe982020-05-26 11:05:19 -050070 // The TachSensors will now have already read the input
71 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -050072 tachChanged();
73 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050074}
75
Matt Spinlerebaae612017-04-27 14:21:48 -050076void Fan::tachChanged()
77{
78 for (auto& s : _sensors)
79 {
80 tachChanged(*s);
81 }
82}
83
Matt Spinlerebaae612017-04-27 14:21:48 -050084void Fan::tachChanged(TachSensor& sensor)
85{
Matt Spinlerc39e8592017-09-28 13:13:08 -050086 if (_trustManager->active())
87 {
88 if (!_trustManager->checkTrust(sensor))
89 {
90 return;
91 }
92 }
93
Matthew Barth177fe982020-05-26 11:05:19 -050094 // If this sensor is out of range at this moment, start
95 // its timer, at the end of which the inventory
96 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050097
Matthew Barth177fe982020-05-26 11:05:19 -050098 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050099
100 if (outOfRange(sensor))
101 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600102 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500103 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600104 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600105 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500106 }
107 }
108 else
109 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600110 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500111 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500112 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500113 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600114 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500115 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600116 // Start functional timer if not already running
117 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500118 }
119 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500120}
121
Matthew Barthf552ea52018-01-15 16:22:04 -0600122uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500123{
124 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500125 // The sensor doesn't support a target,
126 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600127 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500128 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500129
Matthew Barthf552ea52018-01-15 16:22:04 -0600130 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500131 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600132 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500133 }
134
135 return target;
136}
137
Matt Spinlerabf8da32017-04-27 14:08:45 -0500138bool Fan::tooManySensorsNonfunctional()
139{
Matthew Barth177fe982020-05-26 11:05:19 -0500140 size_t numFailed =
141 std::count_if(_sensors.begin(), _sensors.end(),
142 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500143
144 return (numFailed >= _numSensorFailsForNonFunc);
145}
146
Matt Spinlerabf8da32017-04-27 14:08:45 -0500147bool Fan::outOfRange(const TachSensor& sensor)
148{
149 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600150 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800151 auto factor = sensor.getFactor();
152 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500153
154 uint64_t min = target * (100 - _deviation) / 100;
155 uint64_t max = target * (100 + _deviation) / 100;
156
Lei YU8e5d1972018-01-26 17:14:00 +0800157 // TODO: openbmc/openbmc#2937 enhance this function
158 // either by making it virtual, or by predefining different
159 // outOfRange ops and selecting by yaml config
160 min = min * factor + offset;
161 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500162 if ((actual < min) || (actual > max))
163 {
164 return true;
165 }
166
167 return false;
168}
169
Matt Spinlera9406a72017-04-27 14:29:24 -0500170void Fan::timerExpired(TachSensor& sensor)
171{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600172 sensor.setFunctional(!sensor.functional());
173
Matthew Barth177fe982020-05-26 11:05:19 -0500174 // If the fan was nonfunctional and enough sensors are now OK,
175 // the fan can go back to functional
Matthew Barthe11cbc62018-02-20 12:11:07 -0600176 if (!_functional && !tooManySensorsNonfunctional())
177 {
178 log<level::INFO>("Setting a fan back to functional",
179 entry("FAN=%s", _name.c_str()));
180
181 updateInventory(true);
182 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500183
Matthew Barth177fe982020-05-26 11:05:19 -0500184 // If the fan is currently functional, but too many
185 // contained sensors are now nonfunctional, update
186 // the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500187 if (_functional && tooManySensorsNonfunctional())
188 {
189 log<level::ERR>("Setting a fan to nonfunctional",
Matthew Barth177fe982020-05-26 11:05:19 -0500190 entry("FAN=%s", _name.c_str()),
191 entry("TACH_SENSOR=%s", sensor.name().c_str()),
192 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
193 entry("TARGET_SPEED=%lld", sensor.getTarget()));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500194
195 updateInventory(false);
196 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500197}
198
Matt Spinlerb1e18512017-04-27 14:42:33 -0500199void Fan::updateInventory(bool functional)
200{
Matthew Barth177fe982020-05-26 11:05:19 -0500201 auto objectMap =
202 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
203 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600204 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500205 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500206 if (response.is_method_error())
207 {
208 log<level::ERR>("Error in Notify call to update inventory");
209 return;
210 }
211
Matthew Barth177fe982020-05-26 11:05:19 -0500212 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500213 _functional = functional;
214}
215
Matthew Barth177fe982020-05-26 11:05:19 -0500216} // namespace monitor
217} // namespace fan
218} // namespace phosphor