blob: f3937c253477fb0e817585e78bc310a391c86782 [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
Matt Spinlerabf8da32017-04-27 14:08:45 -050040Fan::Fan(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{
48 auto& sensors = std::get<sensorListField>(def);
49
50 for (auto& s : sensors)
51 {
52 _sensors.emplace_back(
53 std::make_unique<TachSensor>(bus,
54 *this,
55 std::get<sensorNameField>(s),
56 std::get<hasTargetField>(s),
Matt Spinlera9406a72017-04-27 14:29:24 -050057 std::get<timeoutField>(def),
58 events));
Matt Spinlerabf8da32017-04-27 14:08:45 -050059 }
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050060
Matt Spinlerb1e18512017-04-27 14:42:33 -050061 //Start from a known state of functional
62 updateInventory(true);
63
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050064 //The TachSensors will now have already read the input
65 //and target values, so check them.
66 tachChanged();
Matt Spinlerabf8da32017-04-27 14:08:45 -050067}
68
69
Matt Spinlerebaae612017-04-27 14:21:48 -050070void Fan::tachChanged()
71{
72 for (auto& s : _sensors)
73 {
74 tachChanged(*s);
75 }
76}
77
78
79void Fan::tachChanged(TachSensor& sensor)
80{
Matt Spinlera4c8f1f2017-04-27 14:38:38 -050081 auto& timer = sensor.getTimer();
82 auto running = timer.running();
83
84 //If this sensor is out of range at this moment, start
85 //its timer, at the end of which the inventory
86 //for the fan may get updated to not functional.
87
88 //If this sensor is OK, put everything back into a good state.
89
90 if (outOfRange(sensor))
91 {
92 if (sensor.functional() && !running)
93 {
94 timer.start(sensor.getTimeout(), TimerType::oneshot);
95 }
96 }
97 else
98 {
99 if (!sensor.functional())
100 {
101 sensor.setFunctional(true);
102 }
103
104 if (running)
105 {
106 timer.stop();
107 }
108
109 //If the fan was nonfunctional and enough sensors are now OK,
110 //the fan can go back to functional
111 if (!_functional && !tooManySensorsNonfunctional())
112 {
113 log<level::INFO>("Setting a fan back to functional",
114 entry("FAN=%s", _name.c_str()));
115
Matt Spinlerb1e18512017-04-27 14:42:33 -0500116 updateInventory(true);
Matt Spinlera4c8f1f2017-04-27 14:38:38 -0500117 }
118 }
Matt Spinlerebaae612017-04-27 14:21:48 -0500119}
120
121
Matt Spinlerabf8da32017-04-27 14:08:45 -0500122uint64_t Fan::getTargetSpeed(const TachSensor& sensor)
123{
124 uint64_t target = 0;
125
126 if (sensor.hasTarget())
127 {
128 target = sensor.getTarget();
129 }
130 else
131 {
132 //The sensor doesn't support a target,
133 //so get it from another sensor.
134 auto s = std::find_if(_sensors.begin(), _sensors.end(),
135 [](const auto& s)
136 {
137 return s->hasTarget();
138 });
139
140 if (s != _sensors.end())
141 {
142 target = (*s)->getTarget();
143 }
144 }
145
146 return target;
147}
148
149
150bool Fan::tooManySensorsNonfunctional()
151{
152 size_t numFailed = std::count_if(_sensors.begin(), _sensors.end(),
153 [](const auto& s)
154 {
155 return !s->functional();
156 });
157
158 return (numFailed >= _numSensorFailsForNonFunc);
159}
160
161
162bool Fan::outOfRange(const TachSensor& sensor)
163{
164 auto actual = static_cast<uint64_t>(sensor.getInput());
165 auto target = getTargetSpeed(sensor);
166
167 uint64_t min = target * (100 - _deviation) / 100;
168 uint64_t max = target * (100 + _deviation) / 100;
169
170 if ((actual < min) || (actual > max))
171 {
172 return true;
173 }
174
175 return false;
176}
177
178
Matt Spinlera9406a72017-04-27 14:29:24 -0500179void Fan::timerExpired(TachSensor& sensor)
180{
181 sensor.setFunctional(false);
182
183 //If the fan is currently functional, but too many
184 //contained sensors are now nonfunctional, update
185 //the whole fan nonfunctional.
Matt Spinlerb1e18512017-04-27 14:42:33 -0500186
187 if (_functional && tooManySensorsNonfunctional())
188 {
189 log<level::ERR>("Setting a fan to nonfunctional",
Matt Spinlerce75b512017-07-26 15:10:48 -0500190 entry("FAN=%s", _name.c_str()),
191 entry("TACH_SENSOR=%s", sensor.name().c_str()),
192 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
193 entry("TARGET_SPEED=%lld", getTargetSpeed(sensor)));
Matt Spinlerb1e18512017-04-27 14:42:33 -0500194
195 updateInventory(false);
196 }
Matt Spinlera9406a72017-04-27 14:29:24 -0500197}
198
Matt Spinlerb1e18512017-04-27 14:42:33 -0500199
200void Fan::updateInventory(bool functional)
201{
202 ObjectMap objectMap = getObjectMap(functional);
203 std::string service;
204
Dinesh Chinari618027a2017-06-26 23:26:50 -0500205 service = phosphor::fan::util::getInvService(_bus);
Matt Spinlerb1e18512017-04-27 14:42:33 -0500206
207 auto msg = _bus.new_method_call(service.c_str(),
208 INVENTORY_PATH,
209 INVENTORY_INTF,
210 "Notify");
211
212 msg.append(std::move(objectMap));
213 auto response = _bus.call(msg);
214 if (response.is_method_error())
215 {
216 log<level::ERR>("Error in Notify call to update inventory");
217 return;
218 }
219
220 //This will always track the current state of the inventory.
221 _functional = functional;
222}
223
224
225Fan::ObjectMap Fan::getObjectMap(bool functional)
226{
227 ObjectMap objectMap;
228 InterfaceMap interfaceMap;
229 PropertyMap propertyMap;
230
231 propertyMap.emplace(FUNCTIONAL_PROPERTY, functional);
232 interfaceMap.emplace(OPERATIONAL_STATUS_INTF, std::move(propertyMap));
233 objectMap.emplace(_name, std::move(interfaceMap));
234
235 return objectMap;
236}
237
238
Matt Spinlerabf8da32017-04-27 14:08:45 -0500239}
240}
241}