blob: 054c67d7865ba2381a6eb025c56346fc482ffad5 [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;
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050030using TimerType = phosphor::fan::util::Timer::TimerType;
Matt Spinlerabf8da32017-04-27 14:08:45 -050031
Matt Spinlerb1e18512017-04-27 14:42:33 -050032constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
33constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
34
35constexpr auto FUNCTIONAL_PROPERTY = "Functional";
36constexpr auto OPERATIONAL_STATUS_INTF =
37 "xyz.openbmc_project.State.Decorator.OperationalStatus";
38
39
Matthew Barth6ad28432017-08-22 11:18:19 -050040Fan::Fan(Mode mode,
41 sdbusplus::bus::bus& bus,
Matt Spinlere824f982017-05-11 10:07:55 -050042 phosphor::fan::event::EventPtr& events,
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)),
47 _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def))
48{
Matt Spinlerb1e18512017-04-27 14:42:33 -050049 //Start from a known state of functional
50 updateInventory(true);
51
Matthew Barth6ad28432017-08-22 11:18:19 -050052 // Setup tach sensors for monitoring when in monitor mode
53 if (mode != Mode::init)
54 {
55 auto& sensors = std::get<sensorListField>(def);
56 for (auto& s : sensors)
57 {
58 try
59 {
60 _sensors.emplace_back(
61 std::make_unique<TachSensor>(
62 bus,
63 *this,
64 std::get<sensorNameField>(s),
65 std::get<hasTargetField>(s),
66 std::get<timeoutField>(def),
67 events));
68 }
69 catch (InvalidSensorError& e)
70 {
71
72 }
73 }
74
75 //The TachSensors will now have already read the input
76 //and target values, so check them.
77 tachChanged();
78 }
Matt Spinlerabf8da32017-04-27 14:08:45 -050079}
80
81
Matt Spinlerebaae612017-04-27 14:21:48 -050082void Fan::tachChanged()
83{
84 for (auto& s : _sensors)
85 {
86 tachChanged(*s);
87 }
88}
89
90
91void Fan::tachChanged(TachSensor& sensor)
92{
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050093 auto& timer = sensor.getTimer();
94 auto running = timer.running();
95
96 //If this sensor is out of range at this moment, start
97 //its timer, at the end of which the inventory
98 //for the fan may get updated to not functional.
99
100 //If this sensor is OK, put everything back into a good state.
101
102 if (outOfRange(sensor))
103 {
104 if (sensor.functional() && !running)
105 {
106 timer.start(sensor.getTimeout(), TimerType::oneshot);
107 }
108 }
109 else
110 {
111 if (!sensor.functional())
112 {
113 sensor.setFunctional(true);
114 }
115
116 if (running)
117 {
118 timer.stop();
119 }
120
121 //If the fan was nonfunctional and enough sensors are now OK,
122 //the fan can go back to functional
123 if (!_functional && !tooManySensorsNonfunctional())
124 {
125 log<level::INFO>("Setting a fan back to functional",
126 entry("FAN=%s", _name.c_str()));
127
Matt Spinlerb1e18512017-04-27 14:42:33 -0500128 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500129 }
130 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500131}
132
133
Matt Spinlerabf8da32017-04-27 14:08:45 -0500134uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
135{
136 uint64_t target = 0;
137
138 if (sensor.hasTarget())
139 {
140 target = sensor.getTarget();
141 }
142 else
143 {
144 //The sensor doesn't support a target,
145 //so get it from another sensor.
146 auto s = std::find_if(_sensors.begin(), _sensors.end(),
147 [](const auto& s)
148 {
149 return s->hasTarget();
150 });
151
152 if (s != _sensors.end())
153 {
154 target = (*s)->getTarget();
155 }
156 }
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());
177 auto target = getTargetSpeed(sensor);
178
179 uint64_t min = target * (100 - _deviation) / 100;
180 uint64_t max = target * (100 + _deviation) / 100;
181
182 if ((actual < min) || (actual > max))
183 {
184 return true;
185 }
186
187 return false;
188}
189
190
Matt Spinlera9406a72017-04-27 14:29:24 -0500191void Fan::timerExpired(TachSensor& sensor)
192{
193 sensor.setFunctional(false);
194
195 //If the fan is currently functional, but too many
196 //contained sensors are now nonfunctional, update
197 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500198
199 if (_functional && tooManySensorsNonfunctional())
200 {
201 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500202 entry("FAN=%s", _name.c_str()),
203 entry("TACH_SENSOR=%s", sensor.name().c_str()),
204 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
205 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor)));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500206
207 updateInventory(false);
208 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500209}
210
Matt Spinlerb1e18512017-04-27 14:42:33 -0500211
212void Fan::updateInventory(bool functional)
213{
214 ObjectMap objectMap = getObjectMap(functional);
215 std::string service;
216
Dinesh Chinari618027a2017-06-26 23:26:50 -0500217 service = phosphor::fan::util::getInvService(_bus);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500218
219 auto msg = _bus.new_method_call(service.c_str(),
220 INVENTORY_PATH,
221 INVENTORY_INTF,
222 "Notify");
223
224 msg.append(std::move(objectMap));
225 auto response = _bus.call(msg);
226 if (response.is_method_error())
227 {
228 log<level::ERR>("Error in Notify call to update inventory");
229 return;
230 }
231
232 //This will always track the current state of the inventory.
233 _functional = functional;
234}
235
236
237Fan::ObjectMap Fan::getObjectMap(bool functional)
238{
239 ObjectMap objectMap;
240 InterfaceMap interfaceMap;
241 PropertyMap propertyMap;
242
243 propertyMap.emplace(FUNCTIONAL_PROPERTY, functional);
244 interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap));
245 objectMap.emplace(_name, std::move(interfaceMap));
246
247 return objectMap;
248}
249
250
Matt Spinlerabf8da32017-04-27 14:08:45 -0500251}
252}
253}