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 <algorithm>
17 #include <phosphor-logging/log.hpp>
18 #include "fan.hpp"
19 #include "types.hpp"
20 #include "utility.hpp"
21 #include "sdbusplus.hpp"
22 
23 namespace phosphor
24 {
25 namespace fan
26 {
27 namespace monitor
28 {
29 
30 using namespace phosphor::logging;
31 
32 Fan::Fan(Mode mode,
33          sdbusplus::bus::bus& bus,
34          phosphor::fan::event::EventPtr&  events,
35          std::unique_ptr<trust::Manager>& trust,
36          const FanDefinition& def) :
37     _bus(bus),
38     _name(std::get<fanNameField>(def)),
39     _deviation(std::get<fanDeviationField>(def)),
40     _numSensorFailsForNonFunc(std::get<numSensorFailsForNonfuncField>(def)),
41     _trustManager(trust)
42 {
43     // Setup tach sensors for monitoring
44     auto& sensors = std::get<sensorListField>(def);
45     for (auto& s : sensors)
46     {
47         try
48         {
49             _sensors.emplace_back(
50                     std::make_unique<TachSensor>(
51                             mode,
52                             bus,
53                             *this,
54                             std::get<sensorNameField>(s),
55                             std::get<hasTargetField>(s),
56                             std::get<targetInterfaceField>(s),
57                             std::get<factorField>(s),
58                             std::get<offsetField>(s),
59                             std::get<timeoutField>(def),
60                             events));
61 
62             _trustManager->registerSensor(_sensors.back());
63         }
64         catch (InvalidSensorError& e)
65         {
66 
67         }
68     }
69 
70     //Start from a known state of functional
71     updateInventory(true);
72 
73     // Check current tach state when entering monitor mode
74     if (mode != Mode::init)
75     {
76         //The TachSensors will now have already read the input
77         //and target values, so check them.
78         tachChanged();
79     }
80 }
81 
82 
83 void Fan::tachChanged()
84 {
85     for (auto& s : _sensors)
86     {
87         tachChanged(*s);
88     }
89 }
90 
91 
92 void Fan::tachChanged(TachSensor& sensor)
93 {
94     if (_trustManager->active())
95     {
96         if (!_trustManager->checkTrust(sensor))
97         {
98             return;
99         }
100     }
101 
102     auto running = sensor.timerRunning();
103 
104     //If this sensor is out of range at this moment, start
105     //its timer, at the end of which the inventory
106     //for the fan may get updated to not functional.
107 
108     //If this sensor is OK, put everything back into a good state.
109 
110     if (outOfRange(sensor))
111     {
112         if (sensor.functional() && !running)
113         {
114             sensor.startTimer();
115         }
116     }
117     else
118     {
119         if (!sensor.functional())
120         {
121             sensor.setFunctional(true);
122         }
123 
124         if (running)
125         {
126             sensor.stopTimer();
127         }
128 
129         //If the fan was nonfunctional and enough sensors are now OK,
130         //the fan can go back to functional
131         if (!_functional && !tooManySensorsNonfunctional())
132         {
133             log<level::INFO>("Setting a fan back to functional",
134                              entry("FAN=%s", _name.c_str()));
135 
136             updateInventory(true);
137         }
138     }
139 }
140 
141 
142 uint64_t Fan::findTargetSpeed()
143 {
144     uint64_t target = 0;
145     //The sensor doesn't support a target,
146     //so get it from another sensor.
147     auto s = std::find_if(_sensors.begin(), _sensors.end(),
148                           [](const auto& s)
149                           {
150                               return s->hasTarget();
151                           });
152 
153     if (s != _sensors.end())
154     {
155         target = (*s)->getTarget();
156     }
157 
158     return target;
159 }
160 
161 
162 bool Fan::tooManySensorsNonfunctional()
163 {
164     size_t numFailed =  std::count_if(_sensors.begin(), _sensors.end(),
165                                       [](const auto& s)
166                                       {
167                                           return !s->functional();
168                                       });
169 
170     return (numFailed >= _numSensorFailsForNonFunc);
171 }
172 
173 
174 bool Fan::outOfRange(const TachSensor& sensor)
175 {
176     auto actual = static_cast<uint64_t>(sensor.getInput());
177     auto target = sensor.getTarget();
178     auto factor = sensor.getFactor();
179     auto offset = sensor.getOffset();
180 
181     uint64_t min = target * (100 - _deviation) / 100;
182     uint64_t max = target * (100 + _deviation) / 100;
183 
184     // TODO: openbmc/openbmc#2937 enhance this function
185     // either by making it virtual, or by predefining different
186     // outOfRange ops and selecting by yaml config
187     min = min * factor + offset;
188     max = max * factor + offset;
189     if ((actual < min) || (actual > max))
190     {
191         return true;
192     }
193 
194     return false;
195 }
196 
197 
198 void Fan::timerExpired(TachSensor& sensor)
199 {
200     sensor.setFunctional(false);
201 
202     //If the fan is currently functional, but too many
203     //contained sensors are now nonfunctional, update
204     //the whole fan nonfunctional.
205 
206     if (_functional && tooManySensorsNonfunctional())
207     {
208         log<level::ERR>("Setting a fan to nonfunctional",
209                 entry("FAN=%s", _name.c_str()),
210                 entry("TACH_SENSOR=%s", sensor.name().c_str()),
211                 entry("ACTUAL_SPEED=%lld", sensor.getInput()),
212                 entry("TARGET_SPEED=%lld", sensor.getTarget()));
213 
214         updateInventory(false);
215     }
216 }
217 
218 
219 void Fan::updateInventory(bool functional)
220 {
221     auto objectMap = util::getObjMap<bool>(
222             _name,
223             util::OPERATIONAL_STATUS_INTF,
224             util::FUNCTIONAL_PROPERTY,
225             functional);
226     auto response = util::SDBusPlus::lookupAndCallMethod(
227             _bus,
228             util::INVENTORY_PATH,
229             util::INVENTORY_INTF,
230             "Notify",
231             objectMap);
232     if (response.is_method_error())
233     {
234         log<level::ERR>("Error in Notify call to update inventory");
235         return;
236     }
237 
238     //This will always track the current state of the inventory.
239     _functional = functional;
240 }
241 
242 }
243 }
244 }
245