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