blob: 6abd2da72f8fc9c2a198d626ef4da18e122698db [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 Spinlerb0412d02020-10-12 16:53:52 -050019#include "system.hpp"
Matt Spinlerabf8da32017-04-27 14:08:45 -050020#include "types.hpp"
Matt Spinlerb1e18512017-04-27 14:42:33 -050021#include "utility.hpp"
Matthew Barth177fe982020-05-26 11:05:19 -050022
Jay Meyera7aed012020-10-06 14:32:22 -050023#include <fmt/format.h>
24
Matthew Barth177fe982020-05-26 11:05:19 -050025#include <phosphor-logging/log.hpp>
26
27#include <algorithm>
Matt Spinlerabf8da32017-04-27 14:08:45 -050028
29namespace phosphor
30{
31namespace fan
32{
33namespace monitor
34{
35
36using namespace phosphor::logging;
Matt Spinlerb0412d02020-10-12 16:53:52 -050037using namespace sdbusplus::bus::match;
Matt Spinlerabf8da32017-04-27 14:08:45 -050038
Matthew Barth177fe982020-05-26 11:05:19 -050039Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event,
Matt Spinlerb0412d02020-10-12 16:53:52 -050040 std::unique_ptr<trust::Manager>& trust, const FanDefinition& def,
41 System& system) :
Matt Spinlerabf8da32017-04-27 14:08:45 -050042 _bus(bus),
43 _name(std::get<fanNameField>(def)),
44 _deviation(std::get<fanDeviationField>(def)),
Matt Spinlerc39e8592017-09-28 13:13:08 -050045 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
Matt Spinlerb0412d02020-10-12 16:53:52 -050046 _trustManager(trust),
47#ifdef MONITOR_USE_JSON
48 _monitorDelay(std::get<monitorStartDelayField>(def)),
49 _monitorTimer(event, std::bind(std::mem_fn(&Fan::startMonitor), this)),
50#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -050051 _system(system),
52 _presenceMatch(bus,
53 rules::propertiesChanged(util::INVENTORY_PATH + _name,
54 util::INV_ITEM_IFACE),
55 std::bind(std::mem_fn(&Fan::presenceChanged), this,
56 std::placeholders::_1))
Matt Spinlerabf8da32017-04-27 14:08:45 -050057{
Jolie Ku4c3c24f2020-09-09 11:01:46 +080058 // Start from a known state of functional
59 updateInventory(true);
60
Matthew Barth0a9fe162018-01-26 12:53:15 -060061 // Setup tach sensors for monitoring
62 auto& sensors = std::get<sensorListField>(def);
63 for (auto& s : sensors)
64 {
65 try
66 {
Matthew Barth177fe982020-05-26 11:05:19 -050067 _sensors.emplace_back(std::make_shared<TachSensor>(
68 mode, bus, *this, std::get<sensorNameField>(s),
69 std::get<hasTargetField>(s), std::get<funcDelay>(def),
70 std::get<targetInterfaceField>(s), std::get<factorField>(s),
71 std::get<offsetField>(s), std::get<timeoutField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060072
73 _trustManager->registerSensor(_sensors.back());
74 }
75 catch (InvalidSensorError& e)
Jolie Ku4c3c24f2020-09-09 11:01:46 +080076 {
Jolie Ku5d564a92020-10-23 09:04:28 +080077 // Count the number of failed tach sensors
78 if (++_numFailedSensor >= _numSensorFailsForNonFunc)
79 {
80 // Mark associated fan as nonfunctional
81 updateInventory(false);
82 }
Jolie Ku4c3c24f2020-09-09 11:01:46 +080083 }
Matthew Barth0a9fe162018-01-26 12:53:15 -060084 }
85
Matt Spinlerb0412d02020-10-12 16:53:52 -050086#ifndef MONITOR_USE_JSON
Matthew Barth0a9fe162018-01-26 12:53:15 -060087 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050088 if (mode != Mode::init)
89 {
Matt Spinlerb0412d02020-10-12 16:53:52 -050090 _monitorReady = true;
91
Matthew Barth177fe982020-05-26 11:05:19 -050092 // The TachSensors will now have already read the input
93 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -050094 tachChanged();
95 }
Matt Spinlerb0412d02020-10-12 16:53:52 -050096#else
97 // If it used the JSON config, then it also will do all the work
98 // out of fan-monitor-init, after _monitorDelay.
99 _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
Matt Spinlerb0412d02020-10-12 16:53:52 -0500100#endif
Matt Spinlerb63aa092020-10-14 09:45:11 -0500101
102 // Get the initial presence state
103 _present = util::SDBusPlus::getProperty<bool>(
104 util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
Matt Spinlerb0412d02020-10-12 16:53:52 -0500105}
106
107void Fan::startMonitor()
108{
109 _monitorReady = true;
110
111 tachChanged();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500112}
113
Matt Spinlerebaae612017-04-27 14:21:48 -0500114void Fan::tachChanged()
115{
Matt Spinlerb0412d02020-10-12 16:53:52 -0500116 if (_monitorReady)
Matt Spinlerebaae612017-04-27 14:21:48 -0500117 {
Matt Spinlerb0412d02020-10-12 16:53:52 -0500118 for (auto& s : _sensors)
119 {
120 tachChanged(*s);
121 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500122 }
123}
124
Matt Spinlerebaae612017-04-27 14:21:48 -0500125void Fan::tachChanged(TachSensor& sensor)
126{
Matt Spinlerc39e8592017-09-28 13:13:08 -0500127 if (_trustManager->active())
128 {
129 if (!_trustManager->checkTrust(sensor))
130 {
131 return;
132 }
133 }
134
Matthew Barth177fe982020-05-26 11:05:19 -0500135 // If this sensor is out of range at this moment, start
136 // its timer, at the end of which the inventory
137 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500138
Matthew Barth177fe982020-05-26 11:05:19 -0500139 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500140
141 if (outOfRange(sensor))
142 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600143 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500144 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600145 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600146 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500147 }
148 }
149 else
150 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600151 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500152 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500153 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500154 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600155 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500156 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600157 // Start functional timer if not already running
158 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500159 }
160 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500161}
162
Matthew Barthf552ea52018-01-15 16:22:04 -0600163uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500164{
165 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500166 // The sensor doesn't support a target,
167 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600168 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500169 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500170
Matthew Barthf552ea52018-01-15 16:22:04 -0600171 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500172 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600173 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500174 }
175
176 return target;
177}
178
Matt Spinlerabf8da32017-04-27 14:08:45 -0500179bool Fan::tooManySensorsNonfunctional()
180{
Matthew Barth177fe982020-05-26 11:05:19 -0500181 size_t numFailed =
182 std::count_if(_sensors.begin(), _sensors.end(),
183 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500184
185 return (numFailed >= _numSensorFailsForNonFunc);
186}
187
Matt Spinlerabf8da32017-04-27 14:08:45 -0500188bool Fan::outOfRange(const TachSensor& sensor)
189{
190 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600191 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800192 auto factor = sensor.getFactor();
193 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500194
195 uint64_t min = target * (100 - _deviation) / 100;
196 uint64_t max = target * (100 + _deviation) / 100;
197
Lei YU8e5d1972018-01-26 17:14:00 +0800198 // TODO: openbmc/openbmc#2937 enhance this function
199 // either by making it virtual, or by predefining different
200 // outOfRange ops and selecting by yaml config
201 min = min * factor + offset;
202 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500203 if ((actual < min) || (actual > max))
204 {
205 return true;
206 }
207
208 return false;
209}
210
Matt Spinlera9406a72017-04-27 14:29:24 -0500211void Fan::timerExpired(TachSensor& sensor)
212{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600213 sensor.setFunctional(!sensor.functional());
214
Matthew Barth177fe982020-05-26 11:05:19 -0500215 // If the fan was nonfunctional and enough sensors are now OK,
216 // the fan can go back to functional
Matthew Barthe11cbc62018-02-20 12:11:07 -0600217 if (!_functional && !tooManySensorsNonfunctional())
218 {
Jay Meyera7aed012020-10-06 14:32:22 -0500219 log<level::INFO>(
220 fmt::format("Setting fan {} back to functional", _name).c_str());
Matthew Barthe11cbc62018-02-20 12:11:07 -0600221
222 updateInventory(true);
223 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500224
Matthew Barth177fe982020-05-26 11:05:19 -0500225 // If the fan is currently functional, but too many
226 // contained sensors are now nonfunctional, update
227 // the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500228 if (_functional && tooManySensorsNonfunctional())
229 {
Jay Meyera7aed012020-10-06 14:32:22 -0500230 log<level::ERR>(fmt::format("Setting fan {} to nonfunctional "
231 "Sensor: {} "
232 "Actual speed: {} "
233 "Target speed: {}",
234 _name, sensor.name(), sensor.getInput(),
235 sensor.getTarget())
236 .c_str());
Matt Spinlerb1e18512017-04-27 14:42:33 -0500237
238 updateInventory(false);
239 }
Matt Spinlerb63aa092020-10-14 09:45:11 -0500240
241 _system.fanStatusChange(*this);
Matt Spinlera9406a72017-04-27 14:29:24 -0500242}
243
Matt Spinlerb1e18512017-04-27 14:42:33 -0500244void Fan::updateInventory(bool functional)
245{
Matthew Barth177fe982020-05-26 11:05:19 -0500246 auto objectMap =
247 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
248 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600249 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500250 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500251 if (response.is_method_error())
252 {
253 log<level::ERR>("Error in Notify call to update inventory");
254 return;
255 }
256
Matthew Barth177fe982020-05-26 11:05:19 -0500257 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500258 _functional = functional;
259}
260
Matt Spinlerb63aa092020-10-14 09:45:11 -0500261void Fan::presenceChanged(sdbusplus::message::message& msg)
262{
263 std::string interface;
264 std::map<std::string, std::variant<bool>> properties;
265
266 msg.read(interface, properties);
267
268 auto presentProp = properties.find("Present");
269 if (presentProp != properties.end())
270 {
271 _present = std::get<bool>(presentProp->second);
272
273 _system.fanStatusChange(*this);
274 }
275}
Matthew Barth177fe982020-05-26 11:05:19 -0500276} // namespace monitor
277} // namespace fan
278} // namespace phosphor