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     _fanMissingErrorDelay(std::get<fanMissingErrDelayField>(def))
59 {
60     // Start from a known state of functional (even if
61     // _numSensorFailsForNonFunc is 0)
62     updateInventory(true);
63 
64     // Setup tach sensors for monitoring
65     auto& sensors = std::get<sensorListField>(def);
66     for (auto& s : sensors)
67     {
68         try
69         {
70             _sensors.emplace_back(std::make_shared<TachSensor>(
71                 mode, bus, *this, std::get<sensorNameField>(s),
72                 std::get<hasTargetField>(s), std::get<funcDelay>(def),
73                 std::get<targetInterfaceField>(s), std::get<factorField>(s),
74                 std::get<offsetField>(s), std::get<methodField>(def),
75                 std::get<thresholdField>(s), std::get<timeoutField>(def),
76                 std::get<nonfuncRotorErrDelayField>(def), event));
77 
78             _trustManager->registerSensor(_sensors.back());
79         }
80         catch (InvalidSensorError& e)
81         {
82             // Count the number of failed tach sensors, though if
83             // _numSensorFailsForNonFunc is zero that means the fan should not
84             // be set to nonfunctional.
85             if (_numSensorFailsForNonFunc &&
86                 (++_numFailedSensor >= _numSensorFailsForNonFunc))
87             {
88                 // Mark associated fan as nonfunctional
89                 updateInventory(false);
90             }
91         }
92     }
93 
94 #ifndef MONITOR_USE_JSON
95     // Check current tach state when entering monitor mode
96     if (mode != Mode::init)
97     {
98         _monitorReady = true;
99 
100         // The TachSensors will now have already read the input
101         // and target values, so check them.
102         tachChanged();
103     }
104 #else
105     // If it used the JSON config, then it also will do all the work
106     // out of fan-monitor-init, after _monitorDelay.
107     _monitorTimer.restartOnce(std::chrono::seconds(_monitorDelay));
108 #endif
109 
110     // Get the initial presence state
111     bool available = true;
112 
113     try
114     {
115         _present = util::SDBusPlus::getProperty<bool>(
116             util::INVENTORY_PATH + _name, util::INV_ITEM_IFACE, "Present");
117     }
118     catch (const util::DBusServiceError& e)
119     {
120         // This could be the initial boot and phosphor-fan-presence hasn't
121         // written to the inventory yet.
122         available = false;
123     }
124 
125     if (_fanMissingErrorDelay)
126     {
127         _fanMissingErrorTimer = std::make_unique<
128             sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>>(
129             event, std::bind(&System::fanMissingErrorTimerExpired, &system,
130                              std::ref(*this)));
131 
132         if (!_present && available)
133         {
134             getLogger().log(
135                 fmt::format("On startup, fan {} is missing", _name));
136             _fanMissingErrorTimer->restartOnce(
137                 std::chrono::seconds{*_fanMissingErrorDelay});
138         }
139     }
140 }
141 
142 void Fan::startMonitor()
143 {
144     _monitorReady = true;
145 
146     tachChanged();
147 }
148 
149 void Fan::tachChanged()
150 {
151     if (_monitorReady)
152     {
153         for (auto& s : _sensors)
154         {
155             tachChanged(*s);
156         }
157     }
158 }
159 
160 void Fan::tachChanged(TachSensor& sensor)
161 {
162     if (_trustManager->active())
163     {
164         if (!_trustManager->checkTrust(sensor))
165         {
166             return;
167         }
168     }
169 
170     process(sensor);
171 }
172 
173 void Fan::process(TachSensor& sensor)
174 {
175     // If this sensor is out of range at this moment, start
176     // its timer, at the end of which the inventory
177     // for the fan may get updated to not functional.
178 
179     // If this sensor is OK, put everything back into a good state.
180 
181     if (outOfRange(sensor))
182     {
183         if (sensor.functional())
184         {
185             switch (sensor.getMethod())
186             {
187                 case MethodMode::timebased:
188                     // Start nonfunctional timer if not already running
189                     sensor.startTimer(TimerMode::nonfunc);
190                     break;
191                 case MethodMode::count:
192                     sensor.setCounter(true);
193                     if (sensor.getCounter() >= sensor.getThreshold())
194                     {
195                         updateState(sensor);
196                     }
197                     break;
198             }
199         }
200     }
201     else
202     {
203         switch (sensor.getMethod())
204         {
205             case MethodMode::timebased:
206                 if (sensor.functional())
207                 {
208                     if (sensor.timerRunning())
209                     {
210                         sensor.stopTimer();
211                     }
212                 }
213                 else
214                 {
215                     // Start functional timer if not already running
216                     sensor.startTimer(TimerMode::func);
217                 }
218                 break;
219             case MethodMode::count:
220                 sensor.setCounter(false);
221                 if (!sensor.functional() && sensor.getCounter() == 0)
222                 {
223                     updateState(sensor);
224                 }
225                 break;
226         }
227     }
228 }
229 
230 uint64_t Fan::findTargetSpeed()
231 {
232     uint64_t target = 0;
233     // The sensor doesn't support a target,
234     // so get it from another sensor.
235     auto s = std::find_if(_sensors.begin(), _sensors.end(),
236                           [](const auto& s) { return s->hasTarget(); });
237 
238     if (s != _sensors.end())
239     {
240         target = (*s)->getTarget();
241     }
242 
243     return target;
244 }
245 
246 size_t Fan::countNonFunctionalSensors()
247 {
248     return std::count_if(_sensors.begin(), _sensors.end(),
249                          [](const auto& s) { return !s->functional(); });
250 }
251 
252 bool Fan::outOfRange(const TachSensor& sensor)
253 {
254     auto actual = static_cast<uint64_t>(sensor.getInput());
255     auto range = sensor.getRange(_deviation);
256 
257     if ((actual < range.first) || (actual > range.second))
258     {
259         return true;
260     }
261 
262     return false;
263 }
264 
265 void Fan::updateState(TachSensor& sensor)
266 {
267     auto range = sensor.getRange(_deviation);
268     sensor.setFunctional(!sensor.functional());
269     getLogger().log(
270         fmt::format("Setting tach sensor {} functional state to {}. "
271                     "[target = {}, input = {}, allowed range = ({} - {})]",
272                     sensor.name(), sensor.functional(), sensor.getTarget(),
273                     sensor.getInput(), range.first, range.second));
274 
275     // A zero value for _numSensorFailsForNonFunc means we aren't dealing
276     // with fan FRU functional status, only sensor functional status.
277     if (_numSensorFailsForNonFunc)
278     {
279         auto numNonFuncSensors = countNonFunctionalSensors();
280         // If the fan was nonfunctional and enough sensors are now OK,
281         // the fan can be set to functional
282         if (!_functional && !(numNonFuncSensors >= _numSensorFailsForNonFunc))
283         {
284             getLogger().log(fmt::format("Setting fan {} to functional, number "
285                                         "of nonfunctional sensors = {}",
286                                         _name, numNonFuncSensors));
287             updateInventory(true);
288         }
289 
290         // If the fan is currently functional, but too many
291         // contained sensors are now nonfunctional, update
292         // the fan to nonfunctional.
293         if (_functional && (numNonFuncSensors >= _numSensorFailsForNonFunc))
294         {
295             getLogger().log(fmt::format("Setting fan {} to nonfunctional, "
296                                         "number of nonfunctional sensors = {}",
297                                         _name, numNonFuncSensors));
298             updateInventory(false);
299         }
300     }
301 
302     _system.fanStatusChange(*this);
303 }
304 
305 void Fan::updateInventory(bool functional)
306 {
307     auto objectMap =
308         util::getObjMap<bool>(_name, util::OPERATIONAL_STATUS_INTF,
309                               util::FUNCTIONAL_PROPERTY, functional);
310     auto response = util::SDBusPlus::lookupAndCallMethod(
311         _bus, util::INVENTORY_PATH, util::INVENTORY_INTF, "Notify", objectMap);
312     if (response.is_method_error())
313     {
314         log<level::ERR>("Error in Notify call to update inventory");
315         return;
316     }
317 
318     // This will always track the current state of the inventory.
319     _functional = functional;
320 }
321 
322 void Fan::presenceChanged(sdbusplus::message::message& msg)
323 {
324     std::string interface;
325     std::map<std::string, std::variant<bool>> properties;
326 
327     msg.read(interface, properties);
328 
329     auto presentProp = properties.find("Present");
330     if (presentProp != properties.end())
331     {
332         _present = std::get<bool>(presentProp->second);
333 
334         getLogger().log(
335             fmt::format("Fan {} presence state change to {}", _name, _present));
336 
337         _system.fanStatusChange(*this);
338 
339         if (_fanMissingErrorDelay)
340         {
341             if (!_present)
342             {
343                 _fanMissingErrorTimer->restartOnce(
344                     std::chrono::seconds{*_fanMissingErrorDelay});
345             }
346             else if (_fanMissingErrorTimer->isEnabled())
347             {
348                 _fanMissingErrorTimer->setEnabled(false);
349             }
350         }
351     }
352 }
353 
354 void Fan::sensorErrorTimerExpired(const TachSensor& sensor)
355 {
356     if (_present)
357     {
358         _system.sensorErrorTimerExpired(*this, sensor);
359     }
360 }
361 
362 } // namespace monitor
363 } // namespace fan
364 } // namespace phosphor
365