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     _setFuncOnPresent(std::get<funcOnPresentField>(def))
66 {
67     // Setup tach sensors for monitoring
68     auto& sensors = std::get<sensorListField>(def);
69     for (auto& s : sensors)
70     {
71         _sensors.emplace_back(std::make_shared<TachSensor>(
72             mode, bus, *this, std::get<sensorNameField>(s),
73             std::get<hasTargetField>(s), std::get<funcDelay>(def),
74             std::get<targetInterfaceField>(s), std::get<factorField>(s),
75             std::get<offsetField>(s), std::get<methodField>(def),
76             std::get<thresholdField>(s), std::get<timeoutField>(def),
77             std::get<nonfuncRotorErrDelayField>(def),
78             std::get<countIntervalField>(def), event));
79 
80         _trustManager->registerSensor(_sensors.back());
81     }
82 
83     bool functionalState =
84         (_numSensorFailsForNonFunc == 0) ||
85         (countNonFunctionalSensors() < _numSensorFailsForNonFunc);
86 
87     if (updateInventory(functionalState) && !functionalState)
88     {
89         // the inventory update threw an exception, possibly because D-Bus
90         // wasn't ready. Try to update sensors back to functional to avoid a
91         // false-alarm. They will be updated again from subscribing to the
92         // properties-changed event
93 
94         for (auto& sensor : _sensors)
95             sensor->setFunctional(true);
96     }
97 
98 #ifndef MONITOR_USE_JSON
99     // Check current tach state when entering monitor mode
100     if (mode != Mode::init)
101     {
102         _monitorReady = true;
103 
104         // The TachSensors will now have already read the input
105         // and target values, so check them.
106         tachChanged();
107     }
108 #else
109     if (_system.isPowerOn())
110     {
111         _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
112     }
113 #endif
114 
115     if (_fanMissingErrorDelay)
116     {
117         _fanMissingErrorTimer = std::make_unique<
118             sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
119             event, std::bind(&System::fanMissingErrorTimerExpired, &system,
120                              std::ref(*this)));
121     }
122 
123     try
124     {
125         _present = util::SDBusPlus::getProperty<bool>(
126             util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
127 
128         if (!_present)
129         {
130             getLogger().log(
131                 fmt::format("On startup, fan {} is missing", _name));
132             if (_system.isPowerOn() && _fanMissingErrorTimer)
133             {
134                 _fanMissingErrorTimer->restartOnce(
135                     std::chrono::seconds{*_fanMissingErrorDelay});
136             }
137         }
138     }
139     catch (const util::DBusServiceError& e)
140     {
141         // This could happen on the first BMC boot if the presence
142         // detect app hasn't started yet and there isn't an inventory
143         // cache yet.
144     }
145 }
146 
147 void Fan::presenceIfaceAdded(sdbusplus::message::message& msg)
148 {
149     sdbusplus::message::object_path path;
150     std::map<std::string, std::map<std::string, std::variant<bool>>> interfaces;
151 
152     msg.read(path, interfaces);
153 
154     auto properties = interfaces.find(util::INV_ITEM_IFACE);
155     if (properties == interfaces.end())
156     {
157         return;
158     }
159 
160     auto property = properties->second.find("Present");
161     if (property == properties->second.end())
162     {
163         return;
164     }
165 
166     _present = std::get<bool>(property->second);
167 
168     if (!_present)
169     {
170         getLogger().log(fmt::format(
171             "New fan {} interface added and fan is not present", _name));
172         if (_system.isPowerOn() && _fanMissingErrorTimer)
173         {
174             _fanMissingErrorTimer->restartOnce(
175                 std::chrono::seconds{*_fanMissingErrorDelay});
176         }
177     }
178 
179     _system.fanStatusChange(*this);
180 }
181 
182 void Fan::startMonitor()
183 {
184     _monitorReady = true;
185 
186     std::for_each(_sensors.begin(), _sensors.end(), [this](auto& sensor) {
187         if (_present)
188         {
189             try
190             {
191                 // Force a getProperty call to check if the tach sensor is
192                 // on D-Bus.  If it isn't, now set it to nonfunctional.
193                 // This isn't done earlier so that code watching for
194                 // nonfunctional tach sensors doesn't take actions before
195                 // those sensors show up on D-Bus.
196                 sensor->updateTachAndTarget();
197                 tachChanged(*sensor);
198             }
199             catch (const util::DBusServiceError& e)
200             {
201                 // The tach property still isn't on D-Bus, ensure
202                 // sensor is nonfunctional.
203                 getLogger().log(fmt::format(
204                     "Monitoring starting but {} sensor value not on D-Bus",
205                     sensor->name()));
206 
207                 sensor->setFunctional(false);
208 
209                 if (_numSensorFailsForNonFunc)
210                 {
211                     if (_functional && (countNonFunctionalSensors() >=
212                                         _numSensorFailsForNonFunc))
213                     {
214                         updateInventory(false);
215                     }
216                 }
217 
218                 _system.fanStatusChange(*this);
219             }
220         }
221     });
222 }
223 
224 void Fan::tachChanged()
225 {
226     if (_monitorReady)
227     {
228         for (auto& s : _sensors)
229         {
230             tachChanged(*s);
231         }
232     }
233 }
234 
235 void Fan::tachChanged(TachSensor& sensor)
236 {
237     if (!_system.isPowerOn() || !_monitorReady)
238     {
239         return;
240     }
241 
242     if (_trustManager->active())
243     {
244         if (!_trustManager->checkTrust(sensor))
245         {
246             return;
247         }
248     }
249 
250     // If the error checking method is 'count', if a tach change leads
251     // to an out of range sensor the count timer will take over in calling
252     // process() until the sensor is healthy again.
253     if (!sensor.countTimerRunning())
254     {
255         process(sensor);
256     }
257 }
258 
259 void Fan::countTimerExpired(TachSensor& sensor)
260 {
261     if (_trustManager->active() && !_trustManager->checkTrust(sensor))
262     {
263         return;
264     }
265     process(sensor);
266 }
267 
268 void Fan::process(TachSensor& sensor)
269 {
270     // If this sensor is out of range at this moment, start
271     // its timer, at the end of which the inventory
272     // for the fan may get updated to not functional.
273 
274     // If this sensor is OK, put everything back into a good state.
275 
276     if (outOfRange(sensor))
277     {
278         if (sensor.functional())
279         {
280             switch (sensor.getMethod())
281             {
282                 case MethodMode::timebased:
283                     // Start nonfunctional timer if not already running
284                     sensor.startTimer(TimerMode::nonfunc);
285                     break;
286                 case MethodMode::count:
287 
288                     if (!sensor.countTimerRunning())
289                     {
290                         sensor.startCountTimer();
291                     }
292                     sensor.setCounter(true);
293                     if (sensor.getCounter() >= sensor.getThreshold())
294                     {
295                         updateState(sensor);
296                     }
297                     break;
298             }
299         }
300     }
301     else
302     {
303         switch (sensor.getMethod())
304         {
305             case MethodMode::timebased:
306                 if (sensor.functional())
307                 {
308                     if (sensor.timerRunning())
309                     {
310                         sensor.stopTimer();
311                     }
312                 }
313                 else
314                 {
315                     // Start functional timer if not already running
316                     sensor.startTimer(TimerMode::func);
317                 }
318                 break;
319             case MethodMode::count:
320                 sensor.setCounter(false);
321                 if (sensor.getCounter() == 0)
322                 {
323                     if (!sensor.functional())
324                     {
325                         updateState(sensor);
326                     }
327 
328                     sensor.stopCountTimer();
329                 }
330                 break;
331         }
332     }
333 }
334 
335 uint64_t Fan::findTargetSpeed()
336 {
337     uint64_t target = 0;
338     // The sensor doesn't support a target,
339     // so get it from another sensor.
340     auto s = std::find_if(_sensors.begin(), _sensors.end(),
341                           [](const auto& s) { return s->hasTarget(); });
342 
343     if (s != _sensors.end())
344     {
345         target = (*s)->getTarget();
346     }
347 
348     return target;
349 }
350 
351 size_t Fan::countNonFunctionalSensors() const
352 {
353     return std::count_if(_sensors.begin(), _sensors.end(),
354                          [](const auto& s) { return !s->functional(); });
355 }
356 
357 bool Fan::outOfRange(const TachSensor& sensor)
358 {
359     if (!sensor.hasOwner())
360     {
361         return true;
362     }
363 
364     auto actual = static_cast<uint64_t>(sensor.getInput());
365     auto range = sensor.getRange(_deviation);
366 
367     return ((actual < range.first) || (actual > range.second));
368 }
369 
370 void Fan::updateState(TachSensor& sensor)
371 {
372     auto range = sensor.getRange(_deviation);
373 
374     if (!_system.isPowerOn())
375     {
376         return;
377     }
378 
379     sensor.setFunctional(!sensor.functional());
380     getLogger().log(
381         fmt::format("Setting tach sensor {} functional state to {}. "
382                     "[target = {}, input = {}, allowed range = ({} - {})]",
383                     sensor.name(), sensor.functional(), sensor.getTarget(),
384                     sensor.getInput(), range.first, range.second));
385 
386     // A zero value for _numSensorFailsForNonFunc means we aren't dealing
387     // with fan FRU functional status, only sensor functional status.
388     if (_numSensorFailsForNonFunc)
389     {
390         auto numNonFuncSensors = countNonFunctionalSensors();
391         // If the fan was nonfunctional and enough sensors are now OK,
392         // the fan can be set to functional as long as `set_func_on_present` was
393         // not set
394         if (!_setFuncOnPresent && !_functional &&
395             !(numNonFuncSensors >= _numSensorFailsForNonFunc))
396         {
397             getLogger().log(fmt::format("Setting fan {} to functional, number "
398                                         "of nonfunctional sensors = {}",
399                                         _name, numNonFuncSensors));
400             updateInventory(true);
401         }
402 
403         // If the fan is currently functional, but too many
404         // contained sensors are now nonfunctional, update
405         // the fan to nonfunctional.
406         if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc))
407         {
408             getLogger().log(fmt::format("Setting fan {} to nonfunctional, "
409                                         "number of nonfunctional sensors = {}",
410                                         _name, numNonFuncSensors));
411             updateInventory(false);
412         }
413     }
414 
415     _system.fanStatusChange(*this);
416 }
417 
418 bool Fan::updateInventory(bool functional)
419 {
420     bool dbusError = false;
421 
422     try
423     {
424         auto objectMap =
425             util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
426                                   util::FUNCTIONAL_PROPERTY, functional);
427 
428         auto response = util::SDBusPlus::lookupAndCallMethod(
429             _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify",
430             objectMap);
431 
432         if (response.is_method_error())
433         {
434             log<level::ERR>("Error in Notify call to update inventory");
435 
436             dbusError = true;
437         }
438     }
439     catch (const util::DBusError& e)
440     {
441         dbusError = true;
442 
443         getLogger().log(
444             fmt::format("D-Bus Exception reading/updating inventory : {}",
445                         e.what()),
446             Logger::error);
447     }
448 
449     // This will always track the current state of the inventory.
450     _functional = functional;
451 
452     return dbusError;
453 }
454 
455 void Fan::presenceChanged(sdbusplus::message::message& msg)
456 {
457     std::string interface;
458     std::map<std::string, std::variant<bool>> properties;
459 
460     msg.read(interface, properties);
461 
462     auto presentProp = properties.find("Present");
463     if (presentProp != properties.end())
464     {
465         _present = std::get<bool>(presentProp->second);
466 
467         getLogger().log(
468             fmt::format("Fan {} presence state change to {}", _name, _present));
469 
470         _system.fanStatusChange(*this);
471 
472         if (_present && _setFuncOnPresent)
473         {
474             updateInventory(true);
475             std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) {
476                 sensor->setFunctional(true);
477                 sensor->resetMethod();
478             });
479         }
480 
481         if (_fanMissingErrorDelay)
482         {
483             if (!_present && _system.isPowerOn())
484             {
485                 _fanMissingErrorTimer->restartOnce(
486                     std::chrono::seconds{*_fanMissingErrorDelay});
487             }
488             else if (_present && _fanMissingErrorTimer->isEnabled())
489             {
490                 _fanMissingErrorTimer->setEnabled(false);
491             }
492         }
493     }
494 }
495 
496 void Fan::sensorErrorTimerExpired(const TachSensor& sensor)
497 {
498     if (_present && _system.isPowerOn())
499     {
500         _system.sensorErrorTimerExpired(*this, sensor);
501     }
502 }
503 
504 void Fan::powerStateChanged(bool powerStateOn)
505 {
506 #ifdef MONITOR_USE_JSON
507     if (powerStateOn)
508     {
509         _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
510 
511         _numSensorsOnDBusAtPowerOn = 0;
512 
513         std::for_each(_sensors.begin(), _sensors.end(), [this](auto& sensor) {
514             try
515             {
516                 // Force a getProperty call.  If sensor is on D-Bus,
517                 // then make sure it's functional.
518                 sensor->updateTachAndTarget();
519 
520                 _numSensorsOnDBusAtPowerOn++;
521 
522                 if (_present)
523                 {
524                     // If not functional, set it back to functional.
525                     if (!sensor->functional())
526                     {
527                         sensor->setFunctional(true);
528                         _system.fanStatusChange(*this, true);
529                     }
530 
531                     // Set the counters back to zero
532                     if (sensor->getMethod() == MethodMode::count)
533                     {
534                         sensor->resetMethod();
535                     }
536                 }
537             }
538             catch (const util::DBusError& e)
539             {
540                 // Properties still aren't on D-Bus.  Let startMonitor()
541                 // deal with it, or maybe System::powerStateChanged() if
542                 // there aren't any sensors at all on D-Bus.
543                 getLogger().log(fmt::format(
544                     "At power on, tach sensor {} value not on D-Bus",
545                     sensor->name()));
546             }
547         });
548 
549         if (_present)
550         {
551             // If configured to change functional state on the fan itself,
552             // Set it back to true now if necessary.
553             if (_numSensorFailsForNonFunc)
554             {
555                 if (!_functional &&
556                     (countNonFunctionalSensors() < _numSensorFailsForNonFunc))
557                 {
558                     updateInventory(true);
559                 }
560             }
561         }
562         else
563         {
564             getLogger().log(
565                 fmt::format("At power on, fan {} is missing", _name));
566 
567             if (_fanMissingErrorTimer)
568             {
569                 _fanMissingErrorTimer->restartOnce(
570                     std::chrono::seconds{*_fanMissingErrorDelay});
571             }
572         }
573     }
574     else
575     {
576         _monitorReady = false;
577 
578         if (_monitorTimer.isEnabled())
579         {
580             _monitorTimer.setEnabled(false);
581         }
582 
583         if (_fanMissingErrorTimer && _fanMissingErrorTimer->isEnabled())
584         {
585             _fanMissingErrorTimer->setEnabled(false);
586         }
587 
588         std::for_each(_sensors.begin(), _sensors.end(), [](auto& sensor) {
589             if (sensor->timerRunning())
590             {
591                 sensor->stopTimer();
592             }
593 
594             sensor->stopCountTimer();
595         });
596     }
597 #endif
598 }
599 
600 } // namespace monitor
601 } // namespace fan
602 } // namespace phosphor
603