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