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