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 <chrono>
17 #include <functional>
18 #include <phosphor-logging/log.hpp>
19 #include <phosphor-logging/elog.hpp>
20 #include <phosphor-logging/elog-errors.hpp>
21 #include <stdexcept>
22 #include <xyz/openbmc_project/Common/error.hpp>
23 #include "zone.hpp"
24 #include "utility.hpp"
25 #include "sdbusplus.hpp"
26 
27 namespace phosphor
28 {
29 namespace fan
30 {
31 namespace control
32 {
33 
34 using namespace std::chrono;
35 using namespace phosphor::fan;
36 using namespace phosphor::logging;
37 using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
38                              Error::InternalFailure;
39 
40 Zone::Zone(Mode mode,
41            sdbusplus::bus::bus& bus,
42            const sdeventplus::Event& event,
43            const ZoneDefinition& def) :
44     _bus(bus),
45     _fullSpeed(std::get<fullSpeedPos>(def)),
46     _zoneNum(std::get<zoneNumPos>(def)),
47     _defFloorSpeed(std::get<floorSpeedPos>(def)),
48     _defCeilingSpeed(std::get<fullSpeedPos>(def)),
49     _incDelay(std::get<incDelayPos>(def)),
50     _decInterval(std::get<decIntervalPos>(def)),
51     _incTimer(event, std::bind(&Zone::incTimerExpired, this)),
52     _decTimer(event, std::bind(&Zone::decTimerExpired, this)),
53     _eventLoop(event)
54 {
55     auto& fanDefs = std::get<fanListPos>(def);
56 
57     for (auto& def : fanDefs)
58     {
59         _fans.emplace_back(std::make_unique<Fan>(bus, def));
60     }
61 
62     // Do not enable set speed events when in init mode
63     if (mode != Mode::init)
64     {
65         // Update target speed to current zone target speed
66         if (!_fans.empty())
67         {
68             _targetSpeed = _fans.front()->getTargetSpeed();
69         }
70         // Setup signal trigger for set speed events
71         for (auto& event : std::get<setSpeedEventsPos>(def))
72         {
73             initEvent(event);
74         }
75         // Start timer for fan speed decreases
76         _decTimer.restart(_decInterval);
77     }
78 }
79 
80 void Zone::setSpeed(uint64_t speed)
81 {
82     if (_isActive)
83     {
84         _targetSpeed = speed;
85         for (auto& fan : _fans)
86         {
87             fan->setSpeed(_targetSpeed);
88         }
89     }
90 }
91 
92 void Zone::setFullSpeed()
93 {
94     if (_fullSpeed != 0)
95     {
96         _targetSpeed = _fullSpeed;
97         for (auto& fan : _fans)
98         {
99             fan->setSpeed(_targetSpeed);
100         }
101     }
102 }
103 
104 void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
105 {
106     _active[*(group)] = isActiveAllow;
107     if (!isActiveAllow)
108     {
109         _isActive = false;
110     }
111     else
112     {
113         // Check all entries are set to allow control active
114         auto actPred = [](auto const& entry) {return entry.second;};
115         _isActive = std::all_of(_active.begin(),
116                                 _active.end(),
117                                 actPred);
118     }
119 }
120 
121 void Zone::removeService(const Group* group,
122                          const std::string& name)
123 {
124     try
125     {
126         auto& sNames = _services.at(*group);
127         auto it = std::find_if(
128             sNames.begin(),
129             sNames.end(),
130             [&name](auto const& entry)
131             {
132                 return name == std::get<namePos>(entry);
133             }
134         );
135         if (it != std::end(sNames))
136         {
137             // Remove service name from group
138             sNames.erase(it);
139         }
140     }
141     catch (const std::out_of_range& oore)
142     {
143         // No services for group found
144     }
145 }
146 
147 void Zone::setServiceOwner(const Group* group,
148                            const std::string& name,
149                            const bool hasOwner)
150 {
151     try
152     {
153         auto& sNames = _services.at(*group);
154         auto it = std::find_if(
155             sNames.begin(),
156             sNames.end(),
157             [&name](auto const& entry)
158             {
159                 return name == std::get<namePos>(entry);
160             }
161         );
162         if (it != std::end(sNames))
163         {
164             std::get<hasOwnerPos>(*it) = hasOwner;
165         }
166         else
167         {
168             _services[*group].emplace_back(name, hasOwner);
169         }
170     }
171     catch (const std::out_of_range& oore)
172     {
173         _services[*group].emplace_back(name, hasOwner);
174     }
175 }
176 
177 void Zone::setServices(const Group* group)
178 {
179     // Remove the empty service name if exists
180     removeService(group, "");
181     for (auto it = group->begin(); it != group->end(); ++it)
182     {
183         std::string name;
184         bool hasOwner = false;
185         try
186         {
187             name = getService(it->first,
188                               std::get<intfPos>(it->second));
189             hasOwner = util::SDBusPlus::callMethodAndRead<bool>(
190                     _bus,
191                     "org.freedesktop.DBus",
192                     "/org/freedesktop/DBus",
193                     "org.freedesktop.DBus",
194                     "NameHasOwner",
195                     name);
196         }
197         catch (const util::DBusMethodError& e)
198         {
199             // Failed to get service name owner state
200             hasOwner = false;
201         }
202         setServiceOwner(group, name, hasOwner);
203     }
204 }
205 
206 void Zone::setFloor(uint64_t speed)
207 {
208     // Check all entries are set to allow floor to be set
209     auto pred = [](auto const& entry) {return entry.second;};
210     auto setFloor = std::all_of(_floorChange.begin(),
211                                 _floorChange.end(),
212                                 pred);
213     if (setFloor)
214     {
215         _floorSpeed = speed;
216         // Floor speed above target, update target to floor speed
217         if (_targetSpeed < _floorSpeed)
218         {
219             requestSpeedIncrease(_floorSpeed - _targetSpeed);
220         }
221     }
222 }
223 
224 void Zone::requestSpeedIncrease(uint64_t targetDelta)
225 {
226     // Only increase speed when delta is higher than
227     // the current increase delta for the zone and currently under ceiling
228     if (targetDelta > _incSpeedDelta &&
229         _targetSpeed < _ceilingSpeed)
230     {
231         auto requestTarget = getRequestSpeedBase();
232         requestTarget = (targetDelta - _incSpeedDelta) + requestTarget;
233         _incSpeedDelta = targetDelta;
234         // Target speed can not go above a defined ceiling speed
235         if (requestTarget > _ceilingSpeed)
236         {
237             requestTarget = _ceilingSpeed;
238         }
239         setSpeed(requestTarget);
240         // Retart timer countdown for fan speed increase
241         _incTimer.restartOnce(_incDelay);
242     }
243 }
244 
245 void Zone::incTimerExpired()
246 {
247     // Clear increase delta when timer expires allowing additional speed
248     // increase requests or speed decreases to occur
249     _incSpeedDelta = 0;
250 }
251 
252 void Zone::requestSpeedDecrease(uint64_t targetDelta)
253 {
254     // Only decrease the lowest target delta requested
255     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
256     {
257         _decSpeedDelta = targetDelta;
258     }
259 }
260 
261 void Zone::decTimerExpired()
262 {
263     // Check all entries are set to allow a decrease
264     auto pred = [](auto const& entry) {return entry.second;};
265     auto decAllowed = std::all_of(_decAllowed.begin(),
266                                   _decAllowed.end(),
267                                   pred);
268 
269     // Only decrease speeds when allowed,
270     // where no requested increases exist and
271     // the increase timer is not running
272     // (i.e. not in the middle of increasing)
273     if (decAllowed && _incSpeedDelta == 0 && !_incTimer.isEnabled())
274     {
275         auto requestTarget = getRequestSpeedBase();
276         // Request target speed should not start above ceiling
277         if (requestTarget > _ceilingSpeed)
278         {
279             requestTarget = _ceilingSpeed;
280         }
281         // Target speed can not go below the defined floor speed
282         if ((requestTarget < _decSpeedDelta) ||
283             (requestTarget - _decSpeedDelta < _floorSpeed))
284         {
285             requestTarget = _floorSpeed;
286         }
287         else
288         {
289             requestTarget = requestTarget - _decSpeedDelta;
290         }
291         setSpeed(requestTarget);
292     }
293     // Clear decrease delta when timer expires
294     _decSpeedDelta = 0;
295     // Decrease timer is restarted since its repeating
296 }
297 
298 void Zone::initEvent(const SetSpeedEvent& event)
299 {
300     sdbusplus::message::message nullMsg{nullptr};
301 
302     for (auto& sig : std::get<signalsPos>(event))
303     {
304         // Initialize the event signal using handler
305         std::get<sigHandlerPos>(sig)(_bus, nullMsg, *this);
306         // Setup signal matches of the property for event
307         std::unique_ptr<EventData> eventData =
308             std::make_unique<EventData>(
309                     std::get<groupPos>(event),
310                     std::get<sigMatchPos>(sig),
311                     std::get<sigHandlerPos>(sig),
312                     std::get<actionsPos>(event)
313             );
314         std::unique_ptr<sdbusplus::server::match::match> match = nullptr;
315         if (!std::get<sigMatchPos>(sig).empty())
316         {
317             match = std::make_unique<sdbusplus::server::match::match>(
318                     _bus,
319                     std::get<sigMatchPos>(sig).c_str(),
320                     std::bind(std::mem_fn(&Zone::handleEvent),
321                               this,
322                               std::placeholders::_1,
323                               eventData.get())
324                 );
325         }
326         _signalEvents.emplace_back(std::move(eventData), std::move(match));
327     }
328     // Attach a timer to run the action of an event
329     auto timerConf = std::get<timerConfPos>(event);
330     if (std::get<intervalPos>(timerConf) != seconds(0))
331     {
332         addTimer(std::get<groupPos>(event),
333                  std::get<actionsPos>(event),
334                  timerConf);
335     }
336     // Run action functions for initial event state
337     std::for_each(
338         std::get<actionsPos>(event).begin(),
339         std::get<actionsPos>(event).end(),
340         [this, &event](auto const& action)
341         {
342             action(*this,
343                    std::get<groupPos>(event));
344         });
345 }
346 
347 void Zone::removeEvent(const SetSpeedEvent& event)
348 {
349     // Remove signals of the event
350     for (auto& sig : std::get<signalsPos>(event))
351     {
352         auto it = findSignal(sig,
353                              std::get<groupPos>(event),
354                              std::get<actionsPos>(event));
355         if (it != std::end(getSignalEvents()))
356         {
357             removeSignal(it);
358         }
359     }
360     // Remove timers of the event
361     if (std::get<intervalPos>(std::get<timerConfPos>(event)) != seconds(0))
362     {
363         auto it = findTimer(std::get<groupPos>(event),
364                             std::get<actionsPos>(event));
365         if (it != std::end(getTimerEvents()))
366         {
367             removeTimer(it);
368         }
369     }
370 }
371 
372 std::vector<SignalEvent>::iterator Zone::findSignal(
373         const Signal& signal,
374         const Group& eGroup,
375         const std::vector<Action>& eActions)
376 {
377     // Find the signal in the event to be removed
378     for (auto it = _signalEvents.begin(); it != _signalEvents.end(); ++ it)
379     {
380         const auto& seEventData = *std::get<signalEventDataPos>(*it);
381         if (eGroup == std::get<eventGroupPos>(seEventData) &&
382             std::get<sigMatchPos>(signal) ==
383                 std::get<eventMatchPos>(seEventData) &&
384             std::get<sigHandlerPos>(signal).target_type().name() ==
385                 std::get<eventHandlerPos>(seEventData).target_type().name() &&
386             eActions.size() == std::get<eventActionsPos>(seEventData).size())
387         {
388             // TODO openbmc/openbmc#2328 - Use the function target
389             // for comparison
390             auto actsEqual = [](auto const& a1,
391                                 auto const& a2)
392                     {
393                         return a1.target_type().name() ==
394                                a2.target_type().name();
395                     };
396             if (std::equal(eActions.begin(),
397                            eActions.end(),
398                            std::get<eventActionsPos>(seEventData).begin(),
399                            actsEqual))
400             {
401                 return it;
402             }
403         }
404     }
405 
406     return _signalEvents.end();
407 }
408 
409 std::vector<TimerEvent>::iterator Zone::findTimer(
410         const Group& eventGroup,
411         const std::vector<Action>& eventActions)
412 {
413     for (auto it = _timerEvents.begin(); it != _timerEvents.end(); ++it)
414     {
415         const auto& teEventData = *std::get<timerEventDataPos>(*it);
416         if (std::get<eventActionsPos>(teEventData).size() ==
417             eventActions.size())
418         {
419             // TODO openbmc/openbmc#2328 - Use the action function target
420             // for comparison
421             auto actsEqual = [](auto const& a1,
422                                 auto const& a2)
423                     {
424                         return a1.target_type().name() ==
425                                a2.target_type().name();
426                     };
427             if (std::get<eventGroupPos>(teEventData) == eventGroup &&
428                 std::equal(eventActions.begin(),
429                            eventActions.end(),
430                            std::get<eventActionsPos>(teEventData).begin(),
431                            actsEqual))
432             {
433                 return it;
434             }
435         }
436     }
437 
438     return _timerEvents.end();
439 }
440 
441 void Zone::addTimer(const Group& group,
442                     const std::vector<Action>& actions,
443                     const TimerConf& tConf)
444 {
445     auto eventData = std::make_unique<EventData>(
446             group,
447             "",
448             nullptr,
449             actions
450     );
451     Timer timer(
452         _eventLoop,
453         std::bind(&Zone::timerExpired,
454                   this,
455                   std::cref(std::get<Group>(*eventData)),
456                   std::cref(std::get<std::vector<Action>>(*eventData))));
457     if (std::get<TimerType>(tConf) == TimerType::repeating)
458     {
459         timer.restart(std::get<intervalPos>(tConf));
460     }
461     else if (std::get<TimerType>(tConf) == TimerType::oneshot)
462     {
463         timer.restartOnce(std::get<intervalPos>(tConf));
464     }
465     else
466     {
467         throw std::invalid_argument("Invalid Timer Type");
468     }
469     _timerEvents.emplace_back(std::move(eventData), std::move(timer));
470 }
471 
472 void Zone::timerExpired(const Group& eventGroup,
473                         const std::vector<Action>& eventActions)
474 {
475     // Perform the actions
476     std::for_each(eventActions.begin(),
477                   eventActions.end(),
478                   [this, &eventGroup](auto const& action)
479                   {
480                       action(*this, eventGroup);
481                   });
482 }
483 
484 void Zone::handleEvent(sdbusplus::message::message& msg,
485                        const EventData* eventData)
486 {
487     // Handle the callback
488     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
489     // Perform the actions
490     std::for_each(
491         std::get<eventActionsPos>(*eventData).begin(),
492         std::get<eventActionsPos>(*eventData).end(),
493         [this, &eventData](auto const& action)
494         {
495             action(*this,
496                    std::get<eventGroupPos>(*eventData));
497         });
498 }
499 
500 const std::string& Zone::getService(const std::string& path,
501                                     const std::string& intf)
502 {
503     // Retrieve service from cache
504     auto srvIter = _servTree.find(path);
505     if (srvIter != _servTree.end())
506     {
507         for (auto& serv : srvIter->second)
508         {
509             auto it = std::find_if(
510                 serv.second.begin(),
511                 serv.second.end(),
512                 [&intf](auto const& interface)
513                 {
514                     return intf == interface;
515                 });
516             if (it != std::end(serv.second))
517             {
518                 // Service found
519                 return serv.first;
520             }
521         }
522         // Interface not found in cache, add and return
523         return addServices(path, intf, 0);
524     }
525     else
526     {
527         // Path not found in cache, add and return
528         return addServices(path, intf, 0);
529     }
530 }
531 
532 const std::string& Zone::addServices(const std::string& path,
533                                      const std::string& intf,
534                                      int32_t depth)
535 {
536     static const std::string empty = "";
537     auto it = _servTree.end();
538 
539     // Get all subtree objects for the given interface
540     auto objects = util::SDBusPlus::getSubTree(_bus, "/", intf, depth);
541     // Add what's returned to the cache of path->services
542     for (auto& pIter : objects)
543     {
544         auto pathIter = _servTree.find(pIter.first);
545         if (pathIter != _servTree.end())
546         {
547             // Path found in cache
548             for (auto& sIter : pIter.second)
549             {
550                 auto servIter = pathIter->second.find(sIter.first);
551                 if (servIter != pathIter->second.end())
552                 {
553                     // Service found in cache
554                     for (auto& iIter : sIter.second)
555                     {
556                         if (std::find(servIter->second.begin(),
557                                       servIter->second.end(),
558                                       iIter) == servIter->second.end())
559                         {
560                             // Add interface to cache
561                             servIter->second.emplace_back(iIter);
562                         }
563                     }
564                 }
565                 else
566                 {
567                     // Service not found in cache
568                     pathIter->second.insert(sIter);
569                 }
570             }
571         }
572         else
573         {
574             _servTree.insert(pIter);
575         }
576         // When the paths match, since a single interface constraint is given,
577         // that is the service to return
578         if (path == pIter.first)
579         {
580             it = _servTree.find(pIter.first);
581         }
582     }
583 
584     if (it != _servTree.end())
585     {
586         return it->second.begin()->first;
587     }
588 
589     return empty;
590 }
591 
592 }
593 }
594 }
595