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