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 "fan.hpp"
17 
18 #include "logging.hpp"
19 #include "sdbusplus.hpp"
20 #include "system.hpp"
21 #include "types.hpp"
22 #include "utility.hpp"
23 
24 #include <fmt/format.h>
25 
26 #include <phosphor-logging/log.hpp>
27 
28 #include <algorithm>
29 
30 namespace phosphor
31 {
32 namespace fan
33 {
34 namespace monitor
35 {
36 
37 using namespace phosphor::logging;
38 using namespace sdbusplus::bus::match;
39 
40 Fan::Fan(Mode mode, sdbusplus::bus::bus& bus, const sdeventplus::Event& event,
41          std::unique_ptr<trust::Manager>& trust, const FanDefinition& def,
42          System& system) :
43     _bus(bus),
44     _name(std::get<fanNameField>(def)),
45     _deviation(std::get<fanDeviationField>(def)),
46     _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
47     _trustManager(trust),
48 #ifdef MONITOR_USE_JSON
49     _monitorDelay(std::get<monitorStartDelayField>(def)),
50     _monitorTimer(event, std::bind(std::mem_fn(&Fan::startMonitor), this)),
51 #endif
52     _system(system),
53     _presenceMatch(bus,
54                    rules::propertiesChanged(util::INVENTORY_PATH + _name,
55                                             util::INV_ITEM_IFACE),
56                    std::bind(std::mem_fn(&Fan::presenceChanged), this,
57                              std::placeholders::_1)),
58     _presenceIfaceAddedMatch(
59         bus,
60         rules::interfacesAdded() +
61             rules::argNpath(0, util::INVENTORY_PATH + _name),
62         std::bind(std::mem_fn(&Fan::presenceIfaceAdded), this,
63                   std::placeholders::_1)),
64     _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def))
65 {
66     // Start from a known state of functional (even if
67     // _numSensorFailsForNonFunc is 0)
68     updateInventory(true);
69 
70     // Setup tach sensors for monitoring
71     auto& sensors = std::get<sensorListField>(def);
72     for (auto& s : sensors)
73     {
74         _sensors.emplace_back(std::make_shared<TachSensor>(
75             mode, bus, *this, std::get<sensorNameField>(s),
76             std::get<hasTargetField>(s), std::get<funcDelay>(def),
77             std::get<targetInterfaceField>(s), std::get<factorField>(s),
78             std::get<offsetField>(s), std::get<methodField>(def),
79             std::get<thresholdField>(s), std::get<timeoutField>(def),
80             std::get<nonfuncRotorErrDelayField>(def), event));
81 
82         _trustManager->registerSensor(_sensors.back());
83     }
84 
85 #ifndef MONITOR_USE_JSON
86     // Check current tach state when entering monitor mode
87     if (mode != Mode::init)
88     {
89         _monitorReady = true;
90 
91         // The TachSensors will now have already read the input
92         // and target values, so check them.
93         tachChanged();
94     }
95 #else
96     if (_system.isPowerOn())
97     {
98         _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
99     }
100 #endif
101 
102     if (_fanMissingErrorDelay)
103     {
104         _fanMissingErrorTimer = std::make_unique<
105             sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
106             event, std::bind(&System::fanMissingErrorTimerExpired, &system,
107                              std::ref(*this)));
108     }
109 
110     try
111     {
112         _present = util::SDBusPlus::getProperty<bool>(
113             util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
114 
115         if (!_present)
116         {
117             getLogger().log(
118                 fmt::format("On startup, fan {} is missing", _name));
119             if (_system.isPowerOn() && _fanMissingErrorTimer)
120             {
121                 _fanMissingErrorTimer->restartOnce(
122                     std::chrono::seconds{*_fanMissingErrorDelay});
123             }
124         }
125     }
126     catch (const util::DBusServiceError& e)
127     {
128         // This could happen on the first BMC boot if the presence
129         // detect app hasn't started yet and there isn't an inventory
130         // cache yet.
131     }
132 }
133 
134 void Fan::presenceIfaceAdded(sdbusplus::message::message& msg)
135 {
136     sdbusplus::message::object_path path;
137     std::map<std::string, std::map<std::string, std::variant<bool>>> interfaces;
138 
139     msg.read(path, interfaces);
140 
141     auto properties = interfaces.find(util::INV_ITEM_IFACE);
142     if (properties == interfaces.end())
143     {
144         return;
145     }
146 
147     auto property = properties->second.find("Present");
148     if (property == properties->second.end())
149     {
150         return;
151     }
152 
153     _present = std::get<bool>(property->second);
154 
155     if (!_present)
156     {
157         getLogger().log(fmt::format(
158             "New fan {} interface added and fan is not present", _name));
159         if (_system.isPowerOn() && _fanMissingErrorTimer)
160         {
161             _fanMissingErrorTimer->restartOnce(
162                 std::chrono::seconds{*_fanMissingErrorDelay});
163         }
164     }
165 
166     _system.fanStatusChange(*this);
167 }
168 
169 void Fan::startMonitor()
170 {
171     _monitorReady = true;
172 
173     std::for_each(_sensors.begin(), _sensors.end(), [this](auto& sensor) {
174         if (_present)
175         {
176             try
177             {
178                 // Force a getProperty call to check if the tach sensor is
179                 // on D-Bus.  If it isn't, now set it to nonfunctional.
180                 // This isn't done earlier so that code watching for
181                 // nonfunctional tach sensors doesn't take actions before
182                 // those sensors show up on D-Bus.
183                 sensor->updateTachAndTarget();
184                 tachChanged(*sensor);
185             }
186             catch (const util::DBusServiceError& e)
187             {
188                 // The tach property still isn't on D-Bus, ensure
189                 // sensor is nonfunctional.
190                 getLogger().log(fmt::format(
191                     "Monitoring starting but {} sensor value not on D-Bus",
192                     sensor->name()));
193 
194                 sensor->setFunctional(false);
195 
196                 if (_numSensorFailsForNonFunc)
197                 {
198                     if (_functional && (countNonFunctionalSensors() >=
199                                         _numSensorFailsForNonFunc))
200                     {
201                         updateInventory(false);
202                     }
203                 }
204 
205                 _system.fanStatusChange(*this);
206             }
207         }
208     });
209 }
210 
211 void Fan::tachChanged()
212 {
213     if (_monitorReady)
214     {
215         for (auto& s : _sensors)
216         {
217             tachChanged(*s);
218         }
219     }
220 }
221 
222 void Fan::tachChanged(TachSensor& sensor)
223 {
224     if (!_system.isPowerOn() || !_monitorReady)
225     {
226         return;
227     }
228 
229     if (_trustManager->active())
230     {
231         if (!_trustManager->checkTrust(sensor))
232         {
233             return;
234         }
235     }
236 
237     process(sensor);
238 }
239 
240 void Fan::process(TachSensor& sensor)
241 {
242     // If this sensor is out of range at this moment, start
243     // its timer, at the end of which the inventory
244     // for the fan may get updated to not functional.
245 
246     // If this sensor is OK, put everything back into a good state.
247 
248     if (outOfRange(sensor))
249     {
250         if (sensor.functional())
251         {
252             switch (sensor.getMethod())
253             {
254                 case MethodMode::timebased:
255                     // Start nonfunctional timer if not already running
256                     sensor.startTimer(TimerMode::nonfunc);
257                     break;
258                 case MethodMode::count:
259                     sensor.setCounter(true);
260                     if (sensor.getCounter() >= sensor.getThreshold())
261                     {
262                         updateState(sensor);
263                     }
264                     break;
265             }
266         }
267     }
268     else
269     {
270         switch (sensor.getMethod())
271         {
272             case MethodMode::timebased:
273                 if (sensor.functional())
274                 {
275                     if (sensor.timerRunning())
276                     {
277                         sensor.stopTimer();
278                     }
279                 }
280                 else
281                 {
282                     // Start functional timer if not already running
283                     sensor.startTimer(TimerMode::func);
284                 }
285                 break;
286             case MethodMode::count:
287                 sensor.setCounter(false);
288                 if (!sensor.functional() && sensor.getCounter() == 0)
289                 {
290                     updateState(sensor);
291                 }
292                 break;
293         }
294     }
295 }
296 
297 uint64_t Fan::findTargetSpeed()
298 {
299     uint64_t target = 0;
300     // The sensor doesn't support a target,
301     // so get it from another sensor.
302     auto s = std::find_if(_sensors.begin(), _sensors.end(),
303                           [](const auto& s) { return s->hasTarget(); });
304 
305     if (s != _sensors.end())
306     {
307         target = (*s)->getTarget();
308     }
309 
310     return target;
311 }
312 
313 size_t Fan::countNonFunctionalSensors()
314 {
315     return std::count_if(_sensors.begin(), _sensors.end(),
316                          [](const auto& s) { return !s->functional(); });
317 }
318 
319 bool Fan::outOfRange(const TachSensor& sensor)
320 {
321     auto actual = static_cast<uint64_t>(sensor.getInput());
322     auto range = sensor.getRange(_deviation);
323 
324     if ((actual < range.first) || (actual > range.second))
325     {
326         return true;
327     }
328 
329     return false;
330 }
331 
332 void Fan::updateState(TachSensor& sensor)
333 {
334     auto range = sensor.getRange(_deviation);
335 
336     if (!_system.isPowerOn())
337     {
338         return;
339     }
340 
341     sensor.setFunctional(!sensor.functional());
342     getLogger().log(
343         fmt::format("Setting tach sensor {} functional state to {}. "
344                     "[target = {}, input = {}, allowed range = ({} - {})]",
345                     sensor.name(), sensor.functional(), sensor.getTarget(),
346                     sensor.getInput(), range.first, range.second));
347 
348     // A zero value for _numSensorFailsForNonFunc means we aren't dealing
349     // with fan FRU functional status, only sensor functional status.
350     if (_numSensorFailsForNonFunc)
351     {
352         auto numNonFuncSensors = countNonFunctionalSensors();
353         // If the fan was nonfunctional and enough sensors are now OK,
354         // the fan can be set to functional
355         if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc))
356         {
357             getLogger().log(fmt::format("Setting fan {} to functional, number "
358                                         "of nonfunctional sensors = {}",
359                                         _name, numNonFuncSensors));
360             updateInventory(true);
361         }
362 
363         // If the fan is currently functional, but too many
364         // contained sensors are now nonfunctional, update
365         // the fan to nonfunctional.
366         if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc))
367         {
368             getLogger().log(fmt::format("Setting fan {} to nonfunctional, "
369                                         "number of nonfunctional sensors = {}",
370                                         _name, numNonFuncSensors));
371             updateInventory(false);
372         }
373     }
374 
375     _system.fanStatusChange(*this);
376 }
377 
378 void Fan::updateInventory(bool functional)
379 {
380     auto objectMap =
381         util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
382                               util::FUNCTIONAL_PROPERTY, functional);
383     auto response = util::SDBusPlus::lookupAndCallMethod(
384         _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
385     if (response.is_method_error())
386     {
387         log<level::ERR>("Error in Notify call to update inventory");
388         return;
389     }
390 
391     // This will always track the current state of the inventory.
392     _functional = functional;
393 }
394 
395 void Fan::presenceChanged(sdbusplus::message::message& msg)
396 {
397     std::string interface;
398     std::map<std::string, std::variant<bool>> properties;
399 
400     msg.read(interface, properties);
401 
402     auto presentProp = properties.find("Present");
403     if (presentProp != properties.end())
404     {
405         _present = std::get<bool>(presentProp->second);
406 
407         getLogger().log(
408             fmt::format("Fan {} presence state change to {}", _name, _present));
409 
410         _system.fanStatusChange(*this);
411 
412         if (_fanMissingErrorDelay)
413         {
414             if (!_present && _system.isPowerOn())
415             {
416                 _fanMissingErrorTimer->restartOnce(
417                     std::chrono::seconds{*_fanMissingErrorDelay});
418             }
419             else if (_present && _fanMissingErrorTimer->isEnabled())
420             {
421                 _fanMissingErrorTimer->setEnabled(false);
422             }
423         }
424     }
425 }
426 
427 void Fan::sensorErrorTimerExpired(const TachSensor& sensor)
428 {
429     if (_present && _system.isPowerOn())
430     {
431         _system.sensorErrorTimerExpired(*this, sensor);
432     }
433 }
434 
435 void Fan::powerStateChanged(bool powerStateOn)
436 {
437 #ifdef MONITOR_USE_JSON
438     if (powerStateOn)
439     {
440         _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
441 
442         if (_present)
443         {
444             std::for_each(
445                 _sensors.begin(), _sensors.end(), [this](auto& sensor) {
446                     try
447                     {
448                         // Force a getProperty call.  If sensor is on D-Bus,
449                         // then make sure it's functional.
450                         sensor->updateTachAndTarget();
451 
452                         // If not functional, set it back to functional.
453                         if (!sensor->functional())
454                         {
455                             sensor->setFunctional(true);
456                             _system.fanStatusChange(*this, true);
457                         }
458                     }
459                     catch (const util::DBusServiceError& e)
460                     {
461                         // Properties still aren't on D-Bus.  Let startMonitor()
462                         // deal with it.
463                         getLogger().log(fmt::format(
464                             "At power on, tach sensor {} value not on D-Bus",
465                             sensor->name()));
466                     }
467                 });
468 
469             // If configured to change functional state on the fan itself,
470             // Set it back to true now if necessary.
471             if (_numSensorFailsForNonFunc)
472             {
473                 if (!_functional &&
474                     (countNonFunctionalSensors() < _numSensorFailsForNonFunc))
475                 {
476                     updateInventory(true);
477                 }
478             }
479         }
480         else
481         {
482             getLogger().log(
483                 fmt::format("At power on, fan {} is missing", _name));
484 
485             if (_fanMissingErrorTimer)
486             {
487                 _fanMissingErrorTimer->restartOnce(
488                     std::chrono::seconds{*_fanMissingErrorDelay});
489             }
490         }
491     }
492     else
493     {
494         _monitorReady = false;
495 
496         if (_monitorTimer.isEnabled())
497         {
498             _monitorTimer.setEnabled(false);
499         }
500 
501         if (_fanMissingErrorTimer && _fanMissingErrorTimer->isEnabled())
502         {
503             _fanMissingErrorTimer->setEnabled(false);
504         }
505 
506         std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) {
507             if (sensor->timerRunning())
508             {
509                 sensor->stopTimer();
510             }
511         });
512     }
513 #endif
514 }
515 
516 } // namespace monitor
517 } // namespace fan
518 } // namespace phosphor
519