blob: 637a3ca8911f04f10f1cfdd570fff2d960d91d0a [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{
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(
50 std::make_unique<TachSensor>(
51 mode,
52 bus,
53 *this,
54 std::get<sensorNameField>(s),
55 std::get<hasTargetField>(s),
Lei YU80f271b2018-01-31 15:24:46 +080056 std::get<targetInterfaceField>(s),
Lei YU8e5d1972018-01-26 17:14:00 +080057 std::get<factorField>(s),
58 std::get<offsetField>(s),
Matthew Barth0a9fe162018-01-26 12:53:15 -060059 std::get<timeoutField>(def),
60 events));
61
62 _trustManager->registerSensor(_sensors.back());
63 }
64 catch (InvalidSensorError& e)
65 {
66
67 }
68 }
69
Matt Spinlerb1e18512017-04-27 14:42:33 -050070 //Start from a known state of functional
71 updateInventory(true);
72
Matthew Barth0a9fe162018-01-26 12:53:15 -060073 // Check current tach state when entering monitor mode
Matthew Barth6ad28432017-08-22 11:18:19 -050074 if (mode != Mode::init)
75 {
Matthew Barth6ad28432017-08-22 11:18:19 -050076 //The TachSensors will now have already read the input
77 //and target values, so check them.
78 tachChanged();
79 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050080}
81
82
Matt Spinlerebaae612017-04-27 14:21:48 -050083void Fan::tachChanged()
84{
85 for (auto& s : _sensors)
86 {
87 tachChanged(*s);
88 }
89}
90
91
92void Fan::tachChanged(TachSensor& sensor)
93{
Matt Spinlerc39e8592017-09-28 13:13:08 -050094 if (_trustManager->active())
95 {
96 if (!_trustManager->checkTrust(sensor))
97 {
98 return;
99 }
100 }
101
Matt Spinler6fa181c2017-09-27 16:24:45 -0500102 auto running = sensor.timerRunning();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500103
104 //If this sensor is out of range at this moment, start
105 //its timer, at the end of which the inventory
106 //for the fan may get updated to not functional.
107
108 //If this sensor is OK, put everything back into a good state.
109
110 if (outOfRange(sensor))
111 {
112 if (sensor.functional() && !running)
113 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500114 sensor.startTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500115 }
116 }
117 else
118 {
119 if (!sensor.functional())
120 {
121 sensor.setFunctional(true);
122 }
123
124 if (running)
125 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500126 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500127 }
128
129 //If the fan was nonfunctional and enough sensors are now OK,
130 //the fan can go back to functional
131 if (!_functional && !tooManySensorsNonfunctional())
132 {
133 log<level::INFO>("Setting a fan back to functional",
134 entry("FAN=%s", _name.c_str()));
135
Matt Spinlerb1e18512017-04-27 14:42:33 -0500136 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500137 }
138 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500139}
140
141
Matthew Barthf552ea52018-01-15 16:22:04 -0600142uint64_t Fan::findTargetSpeed()
Matt Spinlerabf8da32017-04-27 14:08:45 -0500143{
144 uint64_t target = 0;
Matthew Barthf552ea52018-01-15 16:22:04 -0600145 //The sensor doesn't support a target,
146 //so get it from another sensor.
147 auto s = std::find_if(_sensors.begin(), _sensors.end(),
148 [](const auto& s)
149 {
150 return s->hasTarget();
151 });
Matt Spinlerabf8da32017-04-27 14:08:45 -0500152
Matthew Barthf552ea52018-01-15 16:22:04 -0600153 if (s != _sensors.end())
Matt Spinlerabf8da32017-04-27 14:08:45 -0500154 {
Matthew Barthf552ea52018-01-15 16:22:04 -0600155 target = (*s)->getTarget();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500156 }
157
158 return target;
159}
160
161
162bool Fan::tooManySensorsNonfunctional()
163{
164 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
165 [](const auto& s)
166 {
167 return !s->functional();
168 });
169
170 return (numFailed >= _numSensorFailsForNonFunc);
171}
172
173
174bool Fan::outOfRange(const TachSensor& sensor)
175{
176 auto actual = static_cast<uint64_t>(sensor.getInput());
Matthew Barth5008daf2018-01-15 16:38:24 -0600177 auto target = sensor.getTarget();
Lei YU8e5d1972018-01-26 17:14:00 +0800178 auto factor = sensor.getFactor();
179 auto offset = sensor.getOffset();
Matt Spinlerabf8da32017-04-27 14:08:45 -0500180
181 uint64_t min = target * (100 - _deviation) / 100;
182 uint64_t max = target * (100 + _deviation) / 100;
183
Lei YU8e5d1972018-01-26 17:14:00 +0800184 // TODO: openbmc/openbmc#2937 enhance this function
185 // either by making it virtual, or by predefining different
186 // outOfRange ops and selecting by yaml config
187 min = min * factor + offset;
188 max = max * factor + offset;
Matt Spinlerabf8da32017-04-27 14:08:45 -0500189 if ((actual < min) || (actual > max))
190 {
191 return true;
192 }
193
194 return false;
195}
196
197
Matt Spinlera9406a72017-04-27 14:29:24 -0500198void Fan::timerExpired(TachSensor& sensor)
199{
200 sensor.setFunctional(false);
201
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
206 if (_functional && tooManySensorsNonfunctional())
207 {
208 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500209 entry("FAN=%s", _name.c_str()),
210 entry("TACH_SENSOR=%s", sensor.name().c_str()),
211 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
Matthew Barth5008daf2018-01-15 16:38:24 -0600212 entry("TARGET_SPEED=%lld", sensor.getTarget()));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500213
214 updateInventory(false);
215 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500216}
217
Matt Spinlerb1e18512017-04-27 14:42:33 -0500218
219void Fan::updateInventory(bool functional)
220{
Matthew Barth51dd1852017-11-16 15:21:13 -0600221 auto objectMap = util::getObjMap<bool>(
222 _name,
223 util::OPERATIONAL_STATUS_INTF,
224 util::FUNCTIONAL_PROPERTY,
225 functional);
226 auto response = util::SDBusPlus::lookupAndCallMethod(
227 _bus,
228 util::INVENTORY_PATH,
229 util::INVENTORY_INTF,
230 "Notify",
231 objectMap);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500232 if (response.is_method_error())
233 {
234 log<level::ERR>("Error in Notify call to update inventory");
235 return;
236 }
237
238 //This will always track the current state of the inventory.
239 _functional = functional;
240}
241
Matt Spinlerabf8da32017-04-27 14:08:45 -0500242}
243}
244}