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