blob: a6af74ac076ccec9ae0d9c4315c260f7b3617aac [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,
Matt Spinlere824f982017-05-11 10:07:55 -050034 phosphor::fan::event::EventPtr& events,
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{
Matt Spinlerb1e18512017-04-27 14:42:33 -050043 //Start from a known state of functional
44 updateInventory(true);
45
Matthew Barth6ad28432017-08-22 11:18:19 -050046 // Setup tach sensors for monitoring when in monitor mode
47 if (mode != Mode::init)
48 {
49 auto& sensors = std::get<sensorListField>(def);
50 for (auto& s : sensors)
51 {
52 try
53 {
54 _sensors.emplace_back(
55 std::make_unique<TachSensor>(
56 bus,
57 *this,
58 std::get<sensorNameField>(s),
59 std::get<hasTargetField>(s),
60 std::get<timeoutField>(def),
61 events));
Matt Spinlerc39e8592017-09-28 13:13:08 -050062
63 _trustManager->registerSensor(_sensors.back());
Matthew Barth6ad28432017-08-22 11:18:19 -050064 }
65 catch (InvalidSensorError& e)
66 {
67
68 }
69 }
70
71 //The TachSensors will now have already read the input
72 //and target values, so check them.
73 tachChanged();
74 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050075}
76
77
Matt Spinlerebaae612017-04-27 14:21:48 -050078void Fan::tachChanged()
79{
80 for (auto& s : _sensors)
81 {
82 tachChanged(*s);
83 }
84}
85
86
87void Fan::tachChanged(TachSensor& sensor)
88{
Matt Spinlerc39e8592017-09-28 13:13:08 -050089 if (_trustManager->active())
90 {
91 if (!_trustManager->checkTrust(sensor))
92 {
93 return;
94 }
95 }
96
Matt Spinler6fa181c2017-09-27 16:24:45 -050097 auto running = sensor.timerRunning();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050098
99 //If this sensor is out of range at this moment, start
100 //its timer, at the end of which the inventory
101 //for the fan may get updated to not functional.
102
103 //If this sensor is OK, put everything back into a good state.
104
105 if (outOfRange(sensor))
106 {
107 if (sensor.functional() && !running)
108 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500109 sensor.startTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500110 }
111 }
112 else
113 {
114 if (!sensor.functional())
115 {
116 sensor.setFunctional(true);
117 }
118
119 if (running)
120 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500121 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500122 }
123
124 //If the fan was nonfunctional and enough sensors are now OK,
125 //the fan can go back to functional
126 if (!_functional && !tooManySensorsNonfunctional())
127 {
128 log<level::INFO>("Setting a fan back to functional",
129 entry("FAN=%s", _name.c_str()));
130
Matt Spinlerb1e18512017-04-27 14:42:33 -0500131 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500132 }
133 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500134}
135
136
Matt Spinlerabf8da32017-04-27 14:08:45 -0500137uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
138{
139 uint64_t target = 0;
140
141 if (sensor.hasTarget())
142 {
143 target = sensor.getTarget();
144 }
145 else
146 {
147 //The sensor doesn't support a target,
148 //so get it from another sensor.
149 auto s = std::find_if(_sensors.begin(), _sensors.end(),
150 [](const auto& s)
151 {
152 return s->hasTarget();
153 });
154
155 if (s != _sensors.end())
156 {
157 target = (*s)->getTarget();
158 }
159 }
160
161 return target;
162}
163
164
165bool Fan::tooManySensorsNonfunctional()
166{
167 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
168 [](const auto& s)
169 {
170 return !s->functional();
171 });
172
173 return (numFailed >= _numSensorFailsForNonFunc);
174}
175
176
177bool Fan::outOfRange(const TachSensor& sensor)
178{
179 auto actual = static_cast<uint64_t>(sensor.getInput());
180 auto target = getTargetSpeed(sensor);
181
182 uint64_t min = target * (100 - _deviation) / 100;
183 uint64_t max = target * (100 + _deviation) / 100;
184
185 if ((actual < min) || (actual > max))
186 {
187 return true;
188 }
189
190 return false;
191}
192
193
Matt Spinlera9406a72017-04-27 14:29:24 -0500194void Fan::timerExpired(TachSensor& sensor)
195{
196 sensor.setFunctional(false);
197
198 //If the fan is currently functional, but too many
199 //contained sensors are now nonfunctional, update
200 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500201
202 if (_functional && tooManySensorsNonfunctional())
203 {
204 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500205 entry("FAN=%s", _name.c_str()),
206 entry("TACH_SENSOR=%s", sensor.name().c_str()),
207 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
208 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor)));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500209
210 updateInventory(false);
211 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500212}
213
Matt Spinlerb1e18512017-04-27 14:42:33 -0500214
215void Fan::updateInventory(bool functional)
216{
Matthew Barth51dd1852017-11-16 15:21:13 -0600217 auto objectMap = util::getObjMap<bool>(
218 _name,
219 util::OPERATIONAL_STATUS_INTF,
220 util::FUNCTIONAL_PROPERTY,
221 functional);
222 auto response = util::SDBusPlus::lookupAndCallMethod(
223 _bus,
224 util::INVENTORY_PATH,
225 util::INVENTORY_INTF,
226 "Notify",
227 objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500228 if (response.is_method_error())
229 {
230 log<level::ERR>("Error in Notify call to update inventory");
231 return;
232 }
233
234 //This will always track the current state of the inventory.
235 _functional = functional;
236}
237
Matt Spinlerabf8da32017-04-27 14:08:45 -0500238}
239}
240}