blob: 014080db3f70e89a318c51802995906c7c101860 [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 */
16#include <algorithm>
17#include <phosphor-logging/log.hpp>
18#include "fan.hpp"
19#include "types.hpp"
Matt Spinlerb1e18512017-04-27 14:42:33 -050020#include "utility.hpp"
Matthew Barth51dd1852017-11-16 15:21:13 -060021#include "sdbusplus.hpp"
Matt Spinlerabf8da32017-04-27 14:08:45 -050022
23namespace phosphor
24{
25namespace fan
26{
27namespace monitor
28{
29
30using namespace phosphor::logging;
31
Matthew Barth6ad28432017-08-22 11:18:19 -050032Fan::Fan(Mode mode,
33 sdbusplus::bus::bus& bus,
William A. Kennington III1cfc2f12018-10-19 17:29:46 -070034 const sdeventplus::Event& event,
Matt Spinlerc39e8592017-09-28 13:13:08 -050035 std::unique_ptr<trust::Manager>& trust,
Matt Spinlerabf8da32017-04-27 14:08:45 -050036 const FanDefinition& def) :
37 _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 {
49 _sensors.emplace_back(
Matthew Barth32affb92018-02-16 16:11:13 -060050 std::make_shared<TachSensor>(
Matthew Barth0a9fe162018-01-26 12:53:15 -060051 mode,
52 bus,
53 *this,
54 std::get<sensorNameField>(s),
55 std::get<hasTargetField>(s),
Matthew Barth9396bcc2018-02-19 14:13:20 -060056 std::get<funcDelay>(def),
Lei YU80f271b2018-01-31 15:24:46 +080057 std::get<targetInterfaceField>(s),
Lei YU8e5d1972018-01-26 17:14:00 +080058 std::get<factorField>(s),
59 std::get<offsetField>(s),
Matthew Barth0a9fe162018-01-26 12:53:15 -060060 std::get<timeoutField>(def),
William A. Kennington III1cfc2f12018-10-19 17:29:46 -070061 event));
Matthew Barth0a9fe162018-01-26 12:53:15 -060062
63 _trustManager->registerSensor(_sensors.back());
64 }
65 catch (InvalidSensorError& e)
66 {
67
68 }
69 }
70
Matt Spinlerb1e18512017-04-27 14:42:33 -050071 //Start from a known state of functional
72 updateInventory(true);
73
Matthew Barth0a9fe162018-01-26 12:53:15 -060074 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050075 if (mode != Mode::init)
76 {
Matthew Barth6ad28432017-08-22 11:18:19 -050077 //The TachSensors will now have already read the input
78 //and target values, so check them.
79 tachChanged();
80 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050081}
82
83
Matt Spinlerebaae612017-04-27 14:21:48 -050084void Fan::tachChanged()
85{
86 for (auto& s : _sensors)
87 {
88 tachChanged(*s);
89 }
90}
91
92
93void Fan::tachChanged(TachSensor& sensor)
94{
Matt Spinlerc39e8592017-09-28 13:13:08 -050095 if (_trustManager->active())
96 {
97 if (!_trustManager->checkTrust(sensor))
98 {
99 return;
100 }
101 }
102
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500103 //If this sensor is out of range at this moment, start
104 //its timer, at the end of which the inventory
105 //for the fan may get updated to not functional.
106
107 //If this sensor is OK, put everything back into a good state.
108
109 if (outOfRange(sensor))
110 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600111 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500112 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600113 // Start nonfunctional timer if not already running
Matthew Barth3800ae72018-02-19 16:08:04 -0600114 sensor.startTimer(TimerMode::nonfunc);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500115 }
116 }
117 else
118 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600119 if (sensor.functional())
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500120 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500121 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500122 }
Matthew Barthe11cbc62018-02-20 12:11:07 -0600123 else
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500124 {
Matthew Barthe11cbc62018-02-20 12:11:07 -0600125 // Start functional timer if not already running
126 sensor.startTimer(TimerMode::func);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500127 }
128 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500129}
130
131
Matthew Barthf552ea52018-01-15 16:22:04 -0600132uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500133{
134 uint64_t target = 0;
Matthew Barthf552ea52018-01-15 16:22:04 -0600135 //The sensor doesn't support a target,
136 //so get it from another sensor.
137 auto s = std::find_if(_sensors.begin(), _sensors.end(),
138 [](const auto& s)
139 {
140 return s->hasTarget();
141 });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500142
Matthew Barthf552ea52018-01-15 16:22:04 -0600143 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500144 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600145 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500146 }
147
148 return target;
149}
150
151
152bool Fan::tooManySensorsNonfunctional()
153{
154 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
155 [](const auto& s)
156 {
157 return !s->functional();
158 });
159
160 return (numFailed >= _numSensorFailsForNonFunc);
161}
162
163
164bool Fan::outOfRange(const TachSensor& sensor)
165{
166 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600167 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800168 auto factor = sensor.getFactor();
169 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500170
171 uint64_t min = target * (100 - _deviation) / 100;
172 uint64_t max = target * (100 + _deviation) / 100;
173
Lei YU8e5d1972018-01-26 17:14:00 +0800174 // TODO: openbmc/openbmc#2937 enhance this function
175 // either by making it virtual, or by predefining different
176 // outOfRange ops and selecting by yaml config
177 min = min * factor + offset;
178 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500179 if ((actual < min) || (actual > max))
180 {
181 return true;
182 }
183
184 return false;
185}
186
187
Matt Spinlera9406a72017-04-27 14:29:24 -0500188void Fan::timerExpired(TachSensor& sensor)
189{
Matthew Barthe11cbc62018-02-20 12:11:07 -0600190 sensor.setFunctional(!sensor.functional());
191
192 //If the fan was nonfunctional and enough sensors are now OK,
193 //the fan can go back to functional
194 if (!_functional && !tooManySensorsNonfunctional())
195 {
196 log<level::INFO>("Setting a fan back to functional",
197 entry("FAN=%s", _name.c_str()));
198
199 updateInventory(true);
200 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500201
202 //If the fan is currently functional, but too many
203 //contained sensors are now nonfunctional, update
204 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500205 if (_functional && tooManySensorsNonfunctional())
206 {
207 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500208 entry("FAN=%s", _name.c_str()),
209 entry("TACH_SENSOR=%s", sensor.name().c_str()),
210 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
Matthew Barth5008daf2018-01-15 16:38:24 -0600211 entry("TARGET_SPEED=%lld", sensor.getTarget()));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500212
213 updateInventory(false);
214 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500215}
216
Matt Spinlerb1e18512017-04-27 14:42:33 -0500217
218void Fan::updateInventory(bool functional)
219{
Matthew Barth51dd1852017-11-16 15:21:13 -0600220 auto objectMap = util::getObjMap<bool>(
221 _name,
222 util::OPERATIONAL_STATUS_INTF,
223 util::FUNCTIONAL_PROPERTY,
224 functional);
225 auto response = util::SDBusPlus::lookupAndCallMethod(
226 _bus,
227 util::INVENTORY_PATH,
228 util::INVENTORY_INTF,
229 "Notify",
230 objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500231 if (response.is_method_error())
232 {
233 log<level::ERR>("Error in Notify call to update inventory");
234 return;
235 }
236
237 //This will always track the current state of the inventory.
238 _functional = functional;
239}
240
Matt Spinlerabf8da32017-04-27 14:08:45 -0500241}
242}
243}