blob: ba0744a7d9f6348436b473acdc40c4512df43491 [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"
Matt Spinlerabf8da32017-04-27 14:08:45 -050021
22namespace phosphor
23{
24namespace fan
25{
26namespace monitor
27{
28
29using namespace phosphor::logging;
30
Matt Spinlerb1e18512017-04-27 14:42:33 -050031constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
32constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
33
34constexpr auto FUNCTIONAL_PROPERTY = "Functional";
35constexpr auto OPERATIONAL_STATUS_INTF =
36 "xyz.openbmc_project.State.Decorator.OperationalStatus";
37
38
Matthew Barth6ad28432017-08-22 11:18:19 -050039Fan::Fan(Mode mode,
40 sdbusplus::bus::bus& bus,
Matt Spinlere824f982017-05-11 10:07:55 -050041 phosphor::fan::event::EventPtr& events,
Matt Spinlerc39e8592017-09-28 13:13:08 -050042 std::unique_ptr<trust::Manager>& trust,
Matt Spinlerabf8da32017-04-27 14:08:45 -050043 const FanDefinition& def) :
44 _bus(bus),
45 _name(std::get<fanNameField>(def)),
46 _deviation(std::get<fanDeviationField>(def)),
Matt Spinlerc39e8592017-09-28 13:13:08 -050047 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
48 _trustManager(trust)
Matt Spinlerabf8da32017-04-27 14:08:45 -050049{
Matt Spinlerb1e18512017-04-27 14:42:33 -050050 //Start from a known state of functional
51 updateInventory(true);
52
Matthew Barth6ad28432017-08-22 11:18:19 -050053 // Setup tach sensors for monitoring when in monitor mode
54 if (mode != Mode::init)
55 {
56 auto& sensors = std::get<sensorListField>(def);
57 for (auto& s : sensors)
58 {
59 try
60 {
61 _sensors.emplace_back(
62 std::make_unique<TachSensor>(
63 bus,
64 *this,
65 std::get<sensorNameField>(s),
66 std::get<hasTargetField>(s),
67 std::get<timeoutField>(def),
68 events));
Matt Spinlerc39e8592017-09-28 13:13:08 -050069
70 _trustManager->registerSensor(_sensors.back());
Matthew Barth6ad28432017-08-22 11:18:19 -050071 }
72 catch (InvalidSensorError& e)
73 {
74
75 }
76 }
77
78 //The TachSensors will now have already read the input
79 //and target values, so check them.
80 tachChanged();
81 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050082}
83
84
Matt Spinlerebaae612017-04-27 14:21:48 -050085void Fan::tachChanged()
86{
87 for (auto& s : _sensors)
88 {
89 tachChanged(*s);
90 }
91}
92
93
94void Fan::tachChanged(TachSensor& sensor)
95{
Matt Spinlerc39e8592017-09-28 13:13:08 -050096 if (_trustManager->active())
97 {
98 if (!_trustManager->checkTrust(sensor))
99 {
100 return;
101 }
102 }
103
Matt Spinler6fa181c2017-09-27 16:24:45 -0500104 auto running = sensor.timerRunning();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500105
106 //If this sensor is out of range at this moment, start
107 //its timer, at the end of which the inventory
108 //for the fan may get updated to not functional.
109
110 //If this sensor is OK, put everything back into a good state.
111
112 if (outOfRange(sensor))
113 {
114 if (sensor.functional() && !running)
115 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500116 sensor.startTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500117 }
118 }
119 else
120 {
121 if (!sensor.functional())
122 {
123 sensor.setFunctional(true);
124 }
125
126 if (running)
127 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500128 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500129 }
130
131 //If the fan was nonfunctional and enough sensors are now OK,
132 //the fan can go back to functional
133 if (!_functional && !tooManySensorsNonfunctional())
134 {
135 log<level::INFO>("Setting a fan back to functional",
136 entry("FAN=%s", _name.c_str()));
137
Matt Spinlerb1e18512017-04-27 14:42:33 -0500138 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500139 }
140 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500141}
142
143
Matt Spinlerabf8da32017-04-27 14:08:45 -0500144uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
145{
146 uint64_t target = 0;
147
148 if (sensor.hasTarget())
149 {
150 target = sensor.getTarget();
151 }
152 else
153 {
154 //The sensor doesn't support a target,
155 //so get it from another sensor.
156 auto s = std::find_if(_sensors.begin(), _sensors.end(),
157 [](const auto& s)
158 {
159 return s->hasTarget();
160 });
161
162 if (s != _sensors.end())
163 {
164 target = (*s)->getTarget();
165 }
166 }
167
168 return target;
169}
170
171
172bool Fan::tooManySensorsNonfunctional()
173{
174 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
175 [](const auto& s)
176 {
177 return !s->functional();
178 });
179
180 return (numFailed >= _numSensorFailsForNonFunc);
181}
182
183
184bool Fan::outOfRange(const TachSensor& sensor)
185{
186 auto actual = static_cast<uint64_t>(sensor.getInput());
187 auto target = getTargetSpeed(sensor);
188
189 uint64_t min = target * (100 - _deviation) / 100;
190 uint64_t max = target * (100 + _deviation) / 100;
191
192 if ((actual < min) || (actual > max))
193 {
194 return true;
195 }
196
197 return false;
198}
199
200
Matt Spinlera9406a72017-04-27 14:29:24 -0500201void Fan::timerExpired(TachSensor& sensor)
202{
203 sensor.setFunctional(false);
204
205 //If the fan is currently functional, but too many
206 //contained sensors are now nonfunctional, update
207 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500208
209 if (_functional && tooManySensorsNonfunctional())
210 {
211 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500212 entry("FAN=%s", _name.c_str()),
213 entry("TACH_SENSOR=%s", sensor.name().c_str()),
214 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
215 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor)));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500216
217 updateInventory(false);
218 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500219}
220
Matt Spinlerb1e18512017-04-27 14:42:33 -0500221
222void Fan::updateInventory(bool functional)
223{
224 ObjectMap objectMap = getObjectMap(functional);
225 std::string service;
226
Dinesh Chinari618027a2017-06-26 23:26:50 -0500227 service = phosphor::fan::util::getInvService(_bus);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500228
229 auto msg = _bus.new_method_call(service.c_str(),
230 INVENTORY_PATH,
231 INVENTORY_INTF,
232 "Notify");
233
234 msg.append(std::move(objectMap));
235 auto response = _bus.call(msg);
236 if (response.is_method_error())
237 {
238 log<level::ERR>("Error in Notify call to update inventory");
239 return;
240 }
241
242 //This will always track the current state of the inventory.
243 _functional = functional;
244}
245
246
247Fan::ObjectMap Fan::getObjectMap(bool functional)
248{
249 ObjectMap objectMap;
250 InterfaceMap interfaceMap;
251 PropertyMap propertyMap;
252
253 propertyMap.emplace(FUNCTIONAL_PROPERTY, functional);
254 interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap));
255 objectMap.emplace(_name, std::move(interfaceMap));
256
257 return objectMap;
258}
259
260
Matt Spinlerabf8da32017-04-27 14:08:45 -0500261}
262}
263}