blob: a2e0386a0b06a18457b1ce4b630ba78c64ce59c5 [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 Spinlerabf8da32017-04-27 14:08:45 -050042 const FanDefinition& def) :
43 _bus(bus),
44 _name(std::get<fanNameField>(def)),
45 _deviation(std::get<fanDeviationField>(def)),
46 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def))
47{
Matt Spinlerb1e18512017-04-27 14:42:33 -050048 //Start from a known state of functional
49 updateInventory(true);
50
Matthew Barth6ad28432017-08-22 11:18:19 -050051 // Setup tach sensors for monitoring when in monitor mode
52 if (mode != Mode::init)
53 {
54 auto& sensors = std::get<sensorListField>(def);
55 for (auto& s : sensors)
56 {
57 try
58 {
59 _sensors.emplace_back(
60 std::make_unique<TachSensor>(
61 bus,
62 *this,
63 std::get<sensorNameField>(s),
64 std::get<hasTargetField>(s),
65 std::get<timeoutField>(def),
66 events));
67 }
68 catch (InvalidSensorError& e)
69 {
70
71 }
72 }
73
74 //The TachSensors will now have already read the input
75 //and target values, so check them.
76 tachChanged();
77 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050078}
79
80
Matt Spinlerebaae612017-04-27 14:21:48 -050081void Fan::tachChanged()
82{
83 for (auto& s : _sensors)
84 {
85 tachChanged(*s);
86 }
87}
88
89
90void Fan::tachChanged(TachSensor& sensor)
91{
Matt Spinler6fa181c2017-09-27 16:24:45 -050092 auto running = sensor.timerRunning();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050093
94 //If this sensor is out of range at this moment, start
95 //its timer, at the end of which the inventory
96 //for the fan may get updated to not functional.
97
98 //If this sensor is OK, put everything back into a good state.
99
100 if (outOfRange(sensor))
101 {
102 if (sensor.functional() && !running)
103 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500104 sensor.startTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500105 }
106 }
107 else
108 {
109 if (!sensor.functional())
110 {
111 sensor.setFunctional(true);
112 }
113
114 if (running)
115 {
Matt Spinler6fa181c2017-09-27 16:24:45 -0500116 sensor.stopTimer();
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500117 }
118
119 //If the fan was nonfunctional and enough sensors are now OK,
120 //the fan can go back to functional
121 if (!_functional && !tooManySensorsNonfunctional())
122 {
123 log<level::INFO>("Setting a fan back to functional",
124 entry("FAN=%s", _name.c_str()));
125
Matt Spinlerb1e18512017-04-27 14:42:33 -0500126 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500127 }
128 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500129}
130
131
Matt Spinlerabf8da32017-04-27 14:08:45 -0500132uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
133{
134 uint64_t target = 0;
135
136 if (sensor.hasTarget())
137 {
138 target = sensor.getTarget();
139 }
140 else
141 {
142 //The sensor doesn't support a target,
143 //so get it from another sensor.
144 auto s = std::find_if(_sensors.begin(), _sensors.end(),
145 [](const auto& s)
146 {
147 return s->hasTarget();
148 });
149
150 if (s != _sensors.end())
151 {
152 target = (*s)->getTarget();
153 }
154 }
155
156 return target;
157}
158
159
160bool Fan::tooManySensorsNonfunctional()
161{
162 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
163 [](const auto& s)
164 {
165 return !s->functional();
166 });
167
168 return (numFailed >= _numSensorFailsForNonFunc);
169}
170
171
172bool Fan::outOfRange(const TachSensor& sensor)
173{
174 auto actual = static_cast<uint64_t>(sensor.getInput());
175 auto target = getTargetSpeed(sensor);
176
177 uint64_t min = target * (100 - _deviation) / 100;
178 uint64_t max = target * (100 + _deviation) / 100;
179
180 if ((actual < min) || (actual > max))
181 {
182 return true;
183 }
184
185 return false;
186}
187
188
Matt Spinlera9406a72017-04-27 14:29:24 -0500189void Fan::timerExpired(TachSensor& sensor)
190{
191 sensor.setFunctional(false);
192
193 //If the fan is currently functional, but too many
194 //contained sensors are now nonfunctional, update
195 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500196
197 if (_functional && tooManySensorsNonfunctional())
198 {
199 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500200 entry("FAN=%s", _name.c_str()),
201 entry("TACH_SENSOR=%s", sensor.name().c_str()),
202 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
203 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor)));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500204
205 updateInventory(false);
206 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500207}
208
Matt Spinlerb1e18512017-04-27 14:42:33 -0500209
210void Fan::updateInventory(bool functional)
211{
212 ObjectMap objectMap = getObjectMap(functional);
213 std::string service;
214
Dinesh Chinari618027a2017-06-26 23:26:50 -0500215 service = phosphor::fan::util::getInvService(_bus);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500216
217 auto msg = _bus.new_method_call(service.c_str(),
218 INVENTORY_PATH,
219 INVENTORY_INTF,
220 "Notify");
221
222 msg.append(std::move(objectMap));
223 auto response = _bus.call(msg);
224 if (response.is_method_error())
225 {
226 log<level::ERR>("Error in Notify call to update inventory");
227 return;
228 }
229
230 //This will always track the current state of the inventory.
231 _functional = functional;
232}
233
234
235Fan::ObjectMap Fan::getObjectMap(bool functional)
236{
237 ObjectMap objectMap;
238 InterfaceMap interfaceMap;
239 PropertyMap propertyMap;
240
241 propertyMap.emplace(FUNCTIONAL_PROPERTY, functional);
242 interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap));
243 objectMap.emplace(_name, std::move(interfaceMap));
244
245 return objectMap;
246}
247
248
Matt Spinlerabf8da32017-04-27 14:08:45 -0500249}
250}
251}