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