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