blob: bbf82027b11b8cee77fd4af7683ee1da4c3dcc2b [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{
Matthew Barth0a9fe162018-01-26 12:53:15 -060043 // Setup tach sensors for monitoring
44 auto& sensors = std::get<sensorListField>(def);
45 for (auto& s : sensors)
46 {
47 try
48 {
Matthew Barth177fe982020-05-26 11:05:19 -050049 _sensors.emplace_back(std::make_shared<TachSensor>(
50 mode, bus, *this, std::get<sensorNameField>(s),
51 std::get<hasTargetField>(s), std::get<funcDelay>(def),
52 std::get<targetInterfaceField>(s), std::get<factorField>(s),
53 std::get<offsetField>(s), std::get<timeoutField>(def), event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060054
55 _trustManager->registerSensor(_sensors.back());
56 }
57 catch (InvalidSensorError& e)
Matthew Barth177fe982020-05-26 11:05:19 -050058 {}
Matthew Barth0a9fe162018-01-26 12:53:15 -060059 }
60
Matthew Barth177fe982020-05-26 11:05:19 -050061 // Start from a known state of functional
Matt Spinlerb1e18512017-04-27 14:42:33 -050062 updateInventory(true);
63
Matthew Barth0a9fe162018-01-26 12:53:15 -060064 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050065 if (mode != Mode::init)
66 {
Matthew Barth177fe982020-05-26 11:05:19 -050067 // The TachSensors will now have already read the input
68 // and target values, so check them.
Matthew Barth6ad28432017-08-22 11:18:19 -050069 tachChanged();
70 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050071}
72
Matt Spinlerebaae612017-04-27 14:21:48 -050073void Fan::tachChanged()
74{
75 for (auto& s : _sensors)
76 {
77 tachChanged(*s);
78 }
79}
80
Matt Spinlerebaae612017-04-27 14:21:48 -050081void Fan::tachChanged(TachSensor& sensor)
82{
Matt Spinlerc39e8592017-09-28 13:13:08 -050083 if (_trustManager->active())
84 {
85 if (!_trustManager->checkTrust(sensor))
86 {
87 return;
88 }
89 }
90
Matthew Barth177fe982020-05-26 11:05:19 -050091 // If this sensor is out of range at this moment, start
92 // its timer, at the end of which the inventory
93 // for the fan may get updated to not functional.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050094
Matthew Barth177fe982020-05-26 11:05:19 -050095 // If this sensor is OK, put everything back into a good state.
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050096
97 if (outOfRange(sensor))
98 {
Matthew Barthe11cbc62018-02-20 12:11:07 -060099 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500100 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600101 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600102 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500103 }
104 }
105 else
106 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600107 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500108 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500109 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500110 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600111 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500112 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600113 // Start functional timer if not already running
114 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500115 }
116 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500117}
118
Matthew Barthf552ea52018-01-15 16:22:04 -0600119uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500120{
121 uint64_t target = 0;
Matthew Barth177fe982020-05-26 11:05:19 -0500122 // The sensor doesn't support a target,
123 // so get it from another sensor.
Matthew Barthf552ea52018-01-15 16:22:04 -0600124 auto s = std::find_if(_sensors.begin(), _sensors.end(),
Matthew Barth177fe982020-05-26 11:05:19 -0500125 [](const auto& s) { return s->hasTarget(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500126
Matthew Barthf552ea52018-01-15 16:22:04 -0600127 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500128 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600129 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500130 }
131
132 return target;
133}
134
Matt Spinlerabf8da32017-04-27 14:08:45 -0500135bool Fan::tooManySensorsNonfunctional()
136{
Matthew Barth177fe982020-05-26 11:05:19 -0500137 size_t numFailed =
138 std::count_if(_sensors.begin(), _sensors.end(),
139 [](const auto& s) { return !s->functional(); });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500140
141 return (numFailed >= _numSensorFailsForNonFunc);
142}
143
Matt Spinlerabf8da32017-04-27 14:08:45 -0500144bool Fan::outOfRange(const TachSensor& sensor)
145{
146 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600147 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800148 auto factor = sensor.getFactor();
149 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500150
151 uint64_t min = target * (100 - _deviation) / 100;
152 uint64_t max = target * (100 + _deviation) / 100;
153
Lei YU8e5d1972018-01-26 17:14:00 +0800154 // TODO: openbmc/openbmc#2937 enhance this function
155 // either by making it virtual, or by predefining different
156 // outOfRange ops and selecting by yaml config
157 min = min * factor + offset;
158 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500159 if ((actual < min) || (actual > max))
160 {
161 return true;
162 }
163
164 return false;
165}
166
Matt Spinlera9406a72017-04-27 14:29:24 -0500167void Fan::timerExpired(TachSensor& sensor)
168{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600169 sensor.setFunctional(!sensor.functional());
170
Matthew Barth177fe982020-05-26 11:05:19 -0500171 // If the fan was nonfunctional and enough sensors are now OK,
172 // the fan can go back to functional
Matthew Barthe11cbc62018-02-20 12:11:07 -0600173 if (!_functional && !tooManySensorsNonfunctional())
174 {
175 log<level::INFO>("Setting a fan back to functional",
176 entry("FAN=%s", _name.c_str()));
177
178 updateInventory(true);
179 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500180
Matthew Barth177fe982020-05-26 11:05:19 -0500181 // If the fan is currently functional, but too many
182 // contained sensors are now nonfunctional, update
183 // the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500184 if (_functional && tooManySensorsNonfunctional())
185 {
186 log<level::ERR>("Setting a fan to nonfunctional",
Matthew Barth177fe982020-05-26 11:05:19 -0500187 entry("FAN=%s", _name.c_str()),
188 entry("TACH_SENSOR=%s", sensor.name().c_str()),
189 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
190 entry("TARGET_SPEED=%lld", sensor.getTarget()));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500191
192 updateInventory(false);
193 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500194}
195
Matt Spinlerb1e18512017-04-27 14:42:33 -0500196void Fan::updateInventory(bool functional)
197{
Matthew Barth177fe982020-05-26 11:05:19 -0500198 auto objectMap =
199 util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
200 util::FUNCTIONAL_PROPERTY, functional);
Matthew Barth51dd1852017-11-16 15:21:13 -0600201 auto response = util::SDBusPlus::lookupAndCallMethod(
Matthew Barth177fe982020-05-26 11:05:19 -0500202 _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500203 if (response.is_method_error())
204 {
205 log<level::ERR>("Error in Notify call to update inventory");
206 return;
207 }
208
Matthew Barth177fe982020-05-26 11:05:19 -0500209 // This will always track the current state of the inventory.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500210 _functional = functional;
211}
212
Matthew Barth177fe982020-05-26 11:05:19 -0500213} // namespace monitor
214} // namespace fan
215} // namespace phosphor