xref: /openbmc/phosphor-fan-presence/monitor/fan.cpp (revision 8e5d197b840d4498dcb714b60cc1d38202a7a7a7)
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 #include "sdbusplus.hpp"
22 
23 namespace phosphor
24 {
25 namespace fan
26 {
27 namespace monitor
28 {
29 
30 using namespace phosphor::logging;
31 
32 Fan::Fan(Mode mode,
33          sdbusplus::bus::bus& bus,
34          phosphor::fan::event::EventPtr&  events,
35          std::unique_ptr<trust::Manager>& trust,
36          const FanDefinition& def) :
37     _bus(bus),
38     _name(std::get<fanNameField>(def)),
39     _deviation(std::get<fanDeviationField>(def)),
40     _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
41     _trustManager(trust)
42 {
43     // Setup tach sensors for monitoring
44     auto& sensors = std::get<sensorListField>(def);
45     for (auto& s : sensors)
46     {
47         try
48         {
49             _sensors.emplace_back(
50                     std::make_unique<TachSensor>(
51                             mode,
52                             bus,
53                             *this,
54                             std::get<sensorNameField>(s),
55                             std::get<hasTargetField>(s),
56                             std::get<factorField>(s),
57                             std::get<offsetField>(s),
58                             std::get<timeoutField>(def),
59                             events));
60 
61             _trustManager->registerSensor(_sensors.back());
62         }
63         catch (InvalidSensorError& e)
64         {
65 
66         }
67     }
68 
69     //Start from a known state of functional
70     updateInventory(true);
71 
72     // Check current tach state when entering monitor mode
73     if (mode != Mode::init)
74     {
75         //The TachSensors will now have already read the input
76         //and target values, so check them.
77         tachChanged();
78     }
79 }
80 
81 
82 void Fan::tachChanged()
83 {
84     for (auto& s : _sensors)
85     {
86         tachChanged(*s);
87     }
88 }
89 
90 
91 void Fan::tachChanged(TachSensor& sensor)
92 {
93     if (_trustManager->active())
94     {
95         if (!_trustManager->checkTrust(sensor))
96         {
97             return;
98         }
99     }
100 
101     auto running = sensor.timerRunning();
102 
103     //If this sensor is out of range at this moment, start
104     //its timer, at the end of which the inventory
105     //for the fan may get updated to not functional.
106 
107     //If this sensor is OK, put everything back into a good state.
108 
109     if (outOfRange(sensor))
110     {
111         if (sensor.functional() && !running)
112         {
113             sensor.startTimer();
114         }
115     }
116     else
117     {
118         if (!sensor.functional())
119         {
120             sensor.setFunctional(true);
121         }
122 
123         if (running)
124         {
125             sensor.stopTimer();
126         }
127 
128         //If the fan was nonfunctional and enough sensors are now OK,
129         //the fan can go back to functional
130         if (!_functional && !tooManySensorsNonfunctional())
131         {
132             log<level::INFO>("Setting a fan back to functional",
133                              entry("FAN=%s", _name.c_str()));
134 
135             updateInventory(true);
136         }
137     }
138 }
139 
140 
141 uint64_t Fan::findTargetSpeed()
142 {
143     uint64_t target = 0;
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     return target;
158 }
159 
160 
161 bool Fan::tooManySensorsNonfunctional()
162 {
163     size_t numFailed =  std::count_if(_sensors.begin(), _sensors.end(),
164                                       [](const auto& s)
165                                       {
166                                           return !s->functional();
167                                       });
168 
169     return (numFailed >= _numSensorFailsForNonFunc);
170 }
171 
172 
173 bool Fan::outOfRange(const TachSensor& sensor)
174 {
175     auto actual = static_cast<uint64_t>(sensor.getInput());
176     auto target = sensor.getTarget();
177     auto factor = sensor.getFactor();
178     auto offset = sensor.getOffset();
179 
180     uint64_t min = target * (100 - _deviation) / 100;
181     uint64_t max = target * (100 + _deviation) / 100;
182 
183     // TODO: openbmc/openbmc#2937 enhance this function
184     // either by making it virtual, or by predefining different
185     // outOfRange ops and selecting by yaml config
186     min = min * factor + offset;
187     max = max * factor + offset;
188     if ((actual < min) || (actual > max))
189     {
190         return true;
191     }
192 
193     return false;
194 }
195 
196 
197 void Fan::timerExpired(TachSensor& sensor)
198 {
199     sensor.setFunctional(false);
200 
201     //If the fan is currently functional, but too many
202     //contained sensors are now nonfunctional, update
203     //the whole fan nonfunctional.
204 
205     if (_functional && tooManySensorsNonfunctional())
206     {
207         log<level::ERR>("Setting a fan to nonfunctional",
208                 entry("FAN=%s", _name.c_str()),
209                 entry("TACH_SENSOR=%s", sensor.name().c_str()),
210                 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
211                 entry("TARGET_SPEED=%lld", sensor.getTarget()));
212 
213         updateInventory(false);
214     }
215 }
216 
217 
218 void Fan::updateInventory(bool functional)
219 {
220     auto objectMap = util::getObjMap<bool>(
221             _name,
222             util::OPERATIONAL_STATUS_INTF,
223             util::FUNCTIONAL_PROPERTY,
224             functional);
225     auto response = util::SDBusPlus::lookupAndCallMethod(
226             _bus,
227             util::INVENTORY_PATH,
228             util::INVENTORY_INTF,
229             "Notify",
230             objectMap);
231     if (response.is_method_error())
232     {
233         log<level::ERR>("Error in Notify call to update inventory");
234         return;
235     }
236 
237     //This will always track the current state of the inventory.
238     _functional = functional;
239 }
240 
241 }
242 }
243 }
244