blob: b5092c05967f669c7bf076e2955ce02e8c50b1be [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,
57 std::placeholders::_1))
Matt Spinlerabf8da32017-04-27 14:08:45 -050058{
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050059 // Start from a known state of functional (even if
60 // _numSensorFailsForNonFunc is 0)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080061 updateInventory(true);
62
Matthew Barth0a9fe162018-01-26 12:53:15 -060063 // Setup tach sensors for monitoring
64 auto& sensors = std::get<sensorListField>(def);
65 for (auto& s : sensors)
66 {
67 try
68 {
Matthew Barth177fe982020-05-26 11:05:19 -050069 _sensors.emplace_back(std::make_shared<TachSensor>(
70 mode, bus, *this, std::get<sensorNameField>(s),
71 std::get<hasTargetField>(s), std::get<funcDelay>(def),
72 std::get<targetInterfaceField>(s), std::get<factorField>(s),
73 std::get<offsetField>(s), std::get<timeoutField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060074
75 _trustManager->registerSensor(_sensors.back());
76 }
77 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080078 {
Matt Spinlerae1f8ef2020-10-14 16:15:51 -050079 // Count the number of failed tach sensors, though if
80 // _numSensorFailsForNonFunc is zero that means the fan should not
81 // be set to nonfunctional.
82 if (_numSensorFailsForNonFunc &&
83 (++_numFailedSensor >= _numSensorFailsForNonFunc))
Jolie Ku5d564a92020-10-23 09:04:28 +080084 {
85 // Mark associated fan as nonfunctional
86 updateInventory(false);
87 }
Jolie Ku4c3c24f2020-09-09 11:01:46 +080088 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060089 }
90
Matt Spinlerb0412d02020-10-12 16:53:52 -050091#ifndef MONITOR_USE_JSON
Matthew Barth0a9fe162018-01-26 12:53:15 -060092 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050093 if (mode != Mode::init)
94 {
Matt Spinlerb0412d02020-10-12 16:53:52 -050095 _monitorReady = true;
96
Matthew Barth177fe982020-05-26 11:05:19 -050097 // The TachSensors will now have already read the input
98 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -050099 tachChanged();
100 }
Matt Spinlerb0412d02020-10-12 16:53:52 -0500101#else
102 // If it used the JSON config, then it also will do all the work
103 // out of fan-monitor-init, after _monitorDelay.
104 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
Matt Spinlerb0412d02020-10-12 16:53:52 -0500105#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -0500106
107 // Get the initial presence state
108 _present = util::SDBusPlus::getProperty<bool>(
109 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
Matt Spinlerb0412d02020-10-12 16:53:52 -0500110}
111
112void Fan::startMonitor()
113{
114 _monitorReady = true;
115
116 tachChanged();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500117}
118
Matt Spinlerebaae612017-04-27 14:21:48 -0500119void Fan::tachChanged()
120{
Matt Spinlerb0412d02020-10-12 16:53:52 -0500121 if (_monitorReady)
Matt Spinlerebaae612017-04-27 14:21:48 -0500122 {
Matt Spinlerb0412d02020-10-12 16:53:52 -0500123 for (auto& s : _sensors)
124 {
125 tachChanged(*s);
126 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500127 }
128}
129
Matt Spinlerebaae612017-04-27 14:21:48 -0500130void Fan::tachChanged(TachSensor& sensor)
131{
Matt Spinlerc39e8592017-09-28 13:13:08 -0500132 if (_trustManager->active())
133 {
134 if (!_trustManager->checkTrust(sensor))
135 {
136 return;
137 }
138 }
139
Matthew Barth177fe982020-05-26 11:05:19 -0500140 // If this sensor is out of range at this moment, start
141 // its timer, at the end of which the inventory
142 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500143
Matthew Barth177fe982020-05-26 11:05:19 -0500144 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500145
146 if (outOfRange(sensor))
147 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600148 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500149 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600150 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600151 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500152 }
153 }
154 else
155 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600156 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500157 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500158 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500159 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600160 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500161 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600162 // Start functional timer if not already running
163 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500164 }
165 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500166}
167
Matthew Barthf552ea52018-01-15 16:22:04 -0600168uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500169{
170 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500171 // The sensor doesn't support a target,
172 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600173 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500174 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500175
Matthew Barthf552ea52018-01-15 16:22:04 -0600176 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500177 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600178 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500179 }
180
181 return target;
182}
183
Matt Spinlerabf8da32017-04-27 14:08:45 -0500184bool Fan::tooManySensorsNonfunctional()
185{
Matthew Barth177fe982020-05-26 11:05:19 -0500186 size_t numFailed =
187 std::count_if(_sensors.begin(), _sensors.end(),
188 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500189
190 return (numFailed >= _numSensorFailsForNonFunc);
191}
192
Matt Spinlerabf8da32017-04-27 14:08:45 -0500193bool Fan::outOfRange(const TachSensor& sensor)
194{
195 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600196 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800197 auto factor = sensor.getFactor();
198 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500199
200 uint64_t min = target * (100 - _deviation) / 100;
201 uint64_t max = target * (100 + _deviation) / 100;
202
Lei YU8e5d1972018-01-26 17:14:00 +0800203 // TODO: openbmc/openbmc#2937 enhance this function
204 // either by making it virtual, or by predefining different
205 // outOfRange ops and selecting by yaml config
206 min = min * factor + offset;
207 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500208 if ((actual < min) || (actual > max))
209 {
210 return true;
211 }
212
213 return false;
214}
215
Matt Spinlera9406a72017-04-27 14:29:24 -0500216void Fan::timerExpired(TachSensor& sensor)
217{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600218 sensor.setFunctional(!sensor.functional());
219
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500220 getLogger().log(
221 fmt::format("Setting tach sensor {} functional state to {}. "
222 "Actual speed: {} Target speed: {}",
223 sensor.name(), sensor.functional(), sensor.getInput(),
224 sensor.getTarget()));
225
226 // A zero value for _numSensorFailsForNonFunc means we aren't dealing
227 // with fan FRU functional status, only sensor functional status.
228 if (_numSensorFailsForNonFunc)
Matthew Barthe11cbc62018-02-20 12:11:07 -0600229 {
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500230 // If the fan was nonfunctional and enough sensors are now OK,
231 // the fan can go back to functional
232 if (!_functional && !tooManySensorsNonfunctional())
233 {
234 getLogger().log(
235 fmt::format("Setting fan {} back to functional", _name));
Matthew Barthe11cbc62018-02-20 12:11:07 -0600236
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500237 updateInventory(true);
238 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500239
Matt Spinlerae1f8ef2020-10-14 16:15:51 -0500240 // If the fan is currently functional, but too many
241 // contained sensors are now nonfunctional, update
242 // the whole fan nonfunctional.
243 if (_functional && tooManySensorsNonfunctional())
244 {
245 getLogger().log(fmt::format("Setting fan {} to nonfunctional "
246 "Sensor: {} "
247 "Actual speed: {} "
248 "Target speed: {}",
249 _name, sensor.name(), sensor.getInput(),
250 sensor.getTarget()));
251 updateInventory(false);
252 }
Matt Spinlerb1e18512017-04-27 14:42:33 -0500253 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500254
255 _system.fanStatusChange(*this);
Matt Spinlera9406a72017-04-27 14:29:24 -0500256}
257
Matt Spinlerb1e18512017-04-27 14:42:33 -0500258void Fan::updateInventory(bool functional)
259{
Matthew Barth177fe982020-05-26 11:05:19 -0500260 auto objectMap =
261 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
262 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600263 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500264 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500265 if (response.is_method_error())
266 {
267 log<level::ERR>("Error in Notify call to update inventory");
268 return;
269 }
270
Matthew Barth177fe982020-05-26 11:05:19 -0500271 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500272 _functional = functional;
273}
274
Matt Spinlerb63aa092020-10-14 09:45:11 -0500275void Fan::presenceChanged(sdbusplus::message::message& msg)
276{
277 std::string interface;
278 std::map<std::string, std::variant<bool>> properties;
279
280 msg.read(interface, properties);
281
282 auto presentProp = properties.find("Present");
283 if (presentProp != properties.end())
284 {
285 _present = std::get<bool>(presentProp->second);
286
287 _system.fanStatusChange(*this);
288 }
289}
Matthew Barth177fe982020-05-26 11:05:19 -0500290} // namespace monitor
291} // namespace fan
292} // namespace phosphor