xref: /openbmc/phosphor-fan-presence/monitor/fan.cpp (revision 956a0a5ada59c937f9360939a25196c4c1e526aa)
1 /**
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"
20 #include "utility.hpp"
21 
22 namespace phosphor
23 {
24 namespace fan
25 {
26 namespace monitor
27 {
28 
29 using namespace phosphor::logging;
30 
31 constexpr auto INVENTORY_PATH = "/xyz/openbmc_project/inventory";
32 constexpr auto INVENTORY_INTF = "xyz.openbmc_project.Inventory.Manager";
33 
34 constexpr auto FUNCTIONAL_PROPERTY = "Functional";
35 constexpr auto OPERATIONAL_STATUS_INTF  =
36     "xyz.openbmc_project.State.Decorator.OperationalStatus";
37 
38 
39 Fan::Fan(Mode mode,
40          sdbusplus::bus::bus& bus,
41          phosphor::fan::event::EventPtr&  events,
42          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     //Start from a known state of functional
49     updateInventory(true);
50 
51     // 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     }
78 }
79 
80 
81 void Fan::tachChanged()
82 {
83     for (auto& s : _sensors)
84     {
85         tachChanged(*s);
86     }
87 }
88 
89 
90 void Fan::tachChanged(TachSensor& sensor)
91 {
92     auto running = sensor.timerRunning();
93 
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         {
104             sensor.startTimer();
105         }
106     }
107     else
108     {
109         if (!sensor.functional())
110         {
111             sensor.setFunctional(true);
112         }
113 
114         if (running)
115         {
116             sensor.stopTimer();
117         }
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 
126             updateInventory(true);
127         }
128     }
129 }
130 
131 
132 uint64_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 
160 bool 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 
172 bool 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 
189 void 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.
196 
197     if (_functional && tooManySensorsNonfunctional())
198     {
199         log<level::ERR>("Setting a fan to nonfunctional",
200                 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)));
204 
205         updateInventory(false);
206     }
207 }
208 
209 
210 void Fan::updateInventory(bool functional)
211 {
212     ObjectMap objectMap = getObjectMap(functional);
213     std::string service;
214 
215     service = phosphor::fan::util::getInvService(_bus);
216 
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 
235 Fan::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 
249 }
250 }
251 }
252