17f88fe61SMatt Spinler /**
27f88fe61SMatt Spinler  * Copyright © 2017 IBM Corporation
37f88fe61SMatt Spinler  *
47f88fe61SMatt Spinler  * Licensed under the Apache License, Version 2.0 (the "License");
57f88fe61SMatt Spinler  * you may not use this file except in compliance with the License.
67f88fe61SMatt Spinler  * You may obtain a copy of the License at
77f88fe61SMatt Spinler  *
87f88fe61SMatt Spinler  *     http://www.apache.org/licenses/LICENSE-2.0
97f88fe61SMatt Spinler  *
107f88fe61SMatt Spinler  * Unless required by applicable law or agreed to in writing, software
117f88fe61SMatt Spinler  * distributed under the License is distributed on an "AS IS" BASIS,
127f88fe61SMatt Spinler  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
137f88fe61SMatt Spinler  * See the License for the specific language governing permissions and
147f88fe61SMatt Spinler  * limitations under the License.
157f88fe61SMatt Spinler  */
168600d9a0SMatthew Barth #include <chrono>
17618027abSDinesh Chinari #include <phosphor-logging/log.hpp>
18df3e8d67SMatthew Barth #include <phosphor-logging/elog.hpp>
19618027abSDinesh Chinari #include <phosphor-logging/elog-errors.hpp>
20618027abSDinesh Chinari #include <xyz/openbmc_project/Common/error.hpp>
217f88fe61SMatt Spinler #include "zone.hpp"
22df3e8d67SMatthew Barth #include "utility.hpp"
23d953bb25SMatthew Barth #include "sdbusplus.hpp"
247f88fe61SMatt Spinler 
257f88fe61SMatt Spinler namespace phosphor
267f88fe61SMatt Spinler {
277f88fe61SMatt Spinler namespace fan
287f88fe61SMatt Spinler {
297f88fe61SMatt Spinler namespace control
307f88fe61SMatt Spinler {
317f88fe61SMatt Spinler 
328600d9a0SMatthew Barth using namespace std::chrono;
339014980aSMatthew Barth using namespace phosphor::fan;
34df3e8d67SMatthew Barth using namespace phosphor::logging;
35618027abSDinesh Chinari using InternalFailure = sdbusplus::xyz::openbmc_project::Common::
36618027abSDinesh Chinari                              Error::InternalFailure;
377f88fe61SMatt Spinler 
3814184131SMatthew Barth Zone::Zone(Mode mode,
3914184131SMatthew Barth            sdbusplus::bus::bus& bus,
408600d9a0SMatthew Barth            phosphor::fan::event::EventPtr& events,
417f88fe61SMatt Spinler            const ZoneDefinition& def) :
427f88fe61SMatt Spinler     _bus(bus),
437f88fe61SMatt Spinler     _fullSpeed(std::get<fullSpeedPos>(def)),
441de66629SMatthew Barth     _zoneNum(std::get<zoneNumPos>(def)),
45e0ca13ebSMatthew Barth     _defFloorSpeed(std::get<floorSpeedPos>(def)),
468600d9a0SMatthew Barth     _defCeilingSpeed(std::get<fullSpeedPos>(def)),
47a956184bSMatthew Barth     _incDelay(std::get<incDelayPos>(def)),
48a956184bSMatthew Barth     _decInterval(std::get<decIntervalPos>(def)),
491ee48f2bSMatthew Barth     _incTimer(events, [this](){ this->incTimerExpired(); }),
509014980aSMatthew Barth     _decTimer(events, [this](){ this->decTimerExpired(); }),
519014980aSMatthew Barth     _sdEvents(events)
527f88fe61SMatt Spinler {
537f88fe61SMatt Spinler     auto& fanDefs = std::get<fanListPos>(def);
547f88fe61SMatt Spinler 
557f88fe61SMatt Spinler     for (auto& def : fanDefs)
567f88fe61SMatt Spinler     {
577f88fe61SMatt Spinler         _fans.emplace_back(std::make_unique<Fan>(bus, def));
587f88fe61SMatt Spinler     }
5938a93a8aSMatthew Barth 
6014184131SMatthew Barth     // Do not enable set speed events when in init mode
6114184131SMatthew Barth     if (mode != Mode::init)
6214184131SMatthew Barth     {
63ccc7770eSMatthew Barth         // Setup signal trigger for set speed events
64ccc7770eSMatthew Barth         for (auto& event : std::get<setSpeedEventsPos>(def))
65ccc7770eSMatthew Barth         {
66ccc7770eSMatthew Barth             initEvent(event);
67ccc7770eSMatthew Barth         }
688600d9a0SMatthew Barth         // Start timer for fan speed decreases
69a956184bSMatthew Barth         if (!_decTimer.running() && _decInterval != seconds::zero())
708600d9a0SMatthew Barth         {
71a956184bSMatthew Barth             _decTimer.start(_decInterval,
72d953bb25SMatthew Barth                             util::Timer::TimerType::repeating);
738600d9a0SMatthew Barth         }
747f88fe61SMatt Spinler     }
7514184131SMatthew Barth }
767f88fe61SMatt Spinler 
777f88fe61SMatt Spinler void Zone::setSpeed(uint64_t speed)
787f88fe61SMatt Spinler {
7960b00766SMatthew Barth     if (_isActive)
8060b00766SMatthew Barth     {
8160b00766SMatthew Barth         _targetSpeed = speed;
827f88fe61SMatt Spinler         for (auto& fan : _fans)
837f88fe61SMatt Spinler         {
8460b00766SMatthew Barth             fan->setSpeed(_targetSpeed);
8560b00766SMatthew Barth         }
8660b00766SMatthew Barth     }
8760b00766SMatthew Barth }
8860b00766SMatthew Barth 
8960b00766SMatthew Barth void Zone::setFullSpeed()
9060b00766SMatthew Barth {
9160b00766SMatthew Barth     if (_fullSpeed != 0)
9260b00766SMatthew Barth     {
9360b00766SMatthew Barth         _targetSpeed = _fullSpeed;
9460b00766SMatthew Barth         for (auto& fan : _fans)
9560b00766SMatthew Barth         {
9660b00766SMatthew Barth             fan->setSpeed(_targetSpeed);
9760b00766SMatthew Barth         }
987f88fe61SMatt Spinler     }
997f88fe61SMatt Spinler }
1007f88fe61SMatt Spinler 
101861d77c3SMatthew Barth void Zone::setActiveAllow(const Group* group, bool isActiveAllow)
102861d77c3SMatthew Barth {
10360b00766SMatthew Barth     _active[*(group)] = isActiveAllow;
104861d77c3SMatthew Barth     if (!isActiveAllow)
105861d77c3SMatthew Barth     {
106861d77c3SMatthew Barth         _isActive = false;
107861d77c3SMatthew Barth     }
108861d77c3SMatthew Barth     else
109861d77c3SMatthew Barth     {
110861d77c3SMatthew Barth         // Check all entries are set to allow control active
111861d77c3SMatthew Barth         auto actPred = [](auto const& entry) {return entry.second;};
112861d77c3SMatthew Barth         _isActive = std::all_of(_active.begin(),
113861d77c3SMatthew Barth                                 _active.end(),
114861d77c3SMatthew Barth                                 actPred);
115861d77c3SMatthew Barth     }
116861d77c3SMatthew Barth }
117861d77c3SMatthew Barth 
118e59fdf70SMatthew Barth void Zone::setServiceOwner(const Group* group,
119e59fdf70SMatthew Barth                            const std::string& name,
120e59fdf70SMatthew Barth                            const bool hasOwner)
121e59fdf70SMatthew Barth {
122e59fdf70SMatthew Barth     try
123e59fdf70SMatthew Barth     {
124e59fdf70SMatthew Barth         auto& sNames = _services.at(*group);
125e59fdf70SMatthew Barth         auto it = std::find_if(
126e59fdf70SMatthew Barth             sNames.begin(),
127e59fdf70SMatthew Barth             sNames.end(),
128e59fdf70SMatthew Barth             [&name](auto const& entry)
129e59fdf70SMatthew Barth             {
130e59fdf70SMatthew Barth                 return name == std::get<namePos>(entry);
131e59fdf70SMatthew Barth             }
132e59fdf70SMatthew Barth         );
133e59fdf70SMatthew Barth         if (it != std::end(sNames))
134e59fdf70SMatthew Barth         {
135e59fdf70SMatthew Barth             std::get<hasOwnerPos>(*it) = hasOwner;
136e59fdf70SMatthew Barth         }
137e59fdf70SMatthew Barth         else
138e59fdf70SMatthew Barth         {
139e59fdf70SMatthew Barth             _services[*group].emplace_back(name, hasOwner);
140e59fdf70SMatthew Barth         }
141e59fdf70SMatthew Barth     }
142e59fdf70SMatthew Barth     catch (const std::out_of_range& oore)
143e59fdf70SMatthew Barth     {
144e59fdf70SMatthew Barth         _services[*group].emplace_back(name, hasOwner);
145e59fdf70SMatthew Barth     }
146e59fdf70SMatthew Barth }
147e59fdf70SMatthew Barth 
148b4a7cb99SMatthew Barth void Zone::setFloor(uint64_t speed)
149b4a7cb99SMatthew Barth {
150b4a7cb99SMatthew Barth     _floorSpeed = speed;
151b4a7cb99SMatthew Barth     // Floor speed above target, update target to floor speed
152b4a7cb99SMatthew Barth     if (_targetSpeed < _floorSpeed)
153b4a7cb99SMatthew Barth     {
154b4a7cb99SMatthew Barth         requestSpeedIncrease(_floorSpeed - _targetSpeed);
155b4a7cb99SMatthew Barth     }
156b4a7cb99SMatthew Barth }
157b4a7cb99SMatthew Barth 
158240397b9SMatthew Barth void Zone::requestSpeedIncrease(uint64_t targetDelta)
159240397b9SMatthew Barth {
160240397b9SMatthew Barth     // Only increase speed when delta is higher than
161240397b9SMatthew Barth     // the current increase delta for the zone and currently under ceiling
162240397b9SMatthew Barth     if (targetDelta > _incSpeedDelta &&
163240397b9SMatthew Barth         _targetSpeed < _ceilingSpeed)
164240397b9SMatthew Barth     {
1654e728542SMatthew Barth         auto requestTarget = getRequestSpeedBase();
16660b00766SMatthew Barth         requestTarget = (targetDelta - _incSpeedDelta) + requestTarget;
167240397b9SMatthew Barth         _incSpeedDelta = targetDelta;
168240397b9SMatthew Barth         // Target speed can not go above a defined ceiling speed
16960b00766SMatthew Barth         if (requestTarget > _ceilingSpeed)
170240397b9SMatthew Barth         {
17160b00766SMatthew Barth             requestTarget = _ceilingSpeed;
172240397b9SMatthew Barth         }
1731ee48f2bSMatthew Barth         // Cancel current timer countdown
1741ee48f2bSMatthew Barth         if (_incTimer.running())
1751ee48f2bSMatthew Barth         {
1761ee48f2bSMatthew Barth             _incTimer.stop();
177240397b9SMatthew Barth         }
17860b00766SMatthew Barth         setSpeed(requestTarget);
1791ee48f2bSMatthew Barth         // Start timer countdown for fan speed increase
180a956184bSMatthew Barth         _incTimer.start(_incDelay,
181d953bb25SMatthew Barth                         util::Timer::TimerType::oneshot);
1821ee48f2bSMatthew Barth     }
1831ee48f2bSMatthew Barth }
1841ee48f2bSMatthew Barth 
1851ee48f2bSMatthew Barth void Zone::incTimerExpired()
1861ee48f2bSMatthew Barth {
1871ee48f2bSMatthew Barth     // Clear increase delta when timer expires allowing additional speed
1881ee48f2bSMatthew Barth     // increase requests or speed decreases to occur
189240397b9SMatthew Barth     _incSpeedDelta = 0;
190240397b9SMatthew Barth }
191240397b9SMatthew Barth 
1920ce99d8bSMatthew Barth void Zone::requestSpeedDecrease(uint64_t targetDelta)
1930ce99d8bSMatthew Barth {
1940ce99d8bSMatthew Barth     // Only decrease the lowest target delta requested
1950ce99d8bSMatthew Barth     if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta)
1960ce99d8bSMatthew Barth     {
1970ce99d8bSMatthew Barth         _decSpeedDelta = targetDelta;
1980ce99d8bSMatthew Barth     }
1998600d9a0SMatthew Barth }
2000ce99d8bSMatthew Barth 
2018600d9a0SMatthew Barth void Zone::decTimerExpired()
2028600d9a0SMatthew Barth {
2031ee48f2bSMatthew Barth     // Only decrease speeds when no requested increases exist and
2041ee48f2bSMatthew Barth     // the increase timer is not running (i.e. not in the middle of increasing)
2051ee48f2bSMatthew Barth     if (_incSpeedDelta == 0 && !_incTimer.running())
2060ce99d8bSMatthew Barth     {
2074e728542SMatthew Barth         auto requestTarget = getRequestSpeedBase();
2080ce99d8bSMatthew Barth         // Target speed can not go below the defined floor speed
20960b00766SMatthew Barth         if ((requestTarget < _decSpeedDelta) ||
21060b00766SMatthew Barth             (requestTarget - _decSpeedDelta < _floorSpeed))
2110ce99d8bSMatthew Barth         {
21260b00766SMatthew Barth             requestTarget = _floorSpeed;
2130ce99d8bSMatthew Barth         }
2140ce99d8bSMatthew Barth         else
2150ce99d8bSMatthew Barth         {
21660b00766SMatthew Barth             requestTarget = requestTarget - _decSpeedDelta;
2170ce99d8bSMatthew Barth         }
21860b00766SMatthew Barth         setSpeed(requestTarget);
2190ce99d8bSMatthew Barth     }
2200ce99d8bSMatthew Barth     // Clear decrease delta when timer expires
2210ce99d8bSMatthew Barth     _decSpeedDelta = 0;
2228600d9a0SMatthew Barth     // Decrease timer is restarted since its repeating
2230ce99d8bSMatthew Barth }
2240ce99d8bSMatthew Barth 
225ccc7770eSMatthew Barth void Zone::initEvent(const SetSpeedEvent& event)
2261bf0ce4bSMatthew Barth {
227336f18a5SMatthew Barth     sdbusplus::message::message nullMsg{nullptr};
228336f18a5SMatthew Barth 
22967967f9aSMatthew Barth     for (auto& sig : std::get<signalsPos>(event))
2301bf0ce4bSMatthew Barth     {
231336f18a5SMatthew Barth         // Initialize the event signal using handler
232336f18a5SMatthew Barth         std::get<sigHandlerPos>(sig)(_bus, nullMsg, *this);
233336f18a5SMatthew Barth         // Setup signal matches of the property for event
234f6b76d8eSMatthew Barth         std::unique_ptr<EventData> eventData =
2351bf0ce4bSMatthew Barth             std::make_unique<EventData>(
2361bf0ce4bSMatthew Barth                 EventData
2371bf0ce4bSMatthew Barth                 {
2381bf0ce4bSMatthew Barth                     std::get<groupPos>(event),
239336f18a5SMatthew Barth                     std::get<sigMatchPos>(sig),
240336f18a5SMatthew Barth                     std::get<sigHandlerPos>(sig),
241f9201abbSMatthew Barth                     std::get<actionsPos>(event)
242f6b76d8eSMatthew Barth                 }
243f6b76d8eSMatthew Barth             );
244336f18a5SMatthew Barth         std::unique_ptr<sdbusplus::server::match::match> match = nullptr;
245336f18a5SMatthew Barth         if (!std::get<sigMatchPos>(sig).empty())
246336f18a5SMatthew Barth         {
247336f18a5SMatthew Barth             match = std::make_unique<sdbusplus::server::match::match>(
2481bf0ce4bSMatthew Barth                     _bus,
249336f18a5SMatthew Barth                     std::get<sigMatchPos>(sig).c_str(),
2501bf0ce4bSMatthew Barth                     std::bind(std::mem_fn(&Zone::handleEvent),
2511bf0ce4bSMatthew Barth                               this,
2521bf0ce4bSMatthew Barth                               std::placeholders::_1,
253f6b76d8eSMatthew Barth                               eventData.get())
254f6b76d8eSMatthew Barth                 );
255336f18a5SMatthew Barth         }
256f6b76d8eSMatthew Barth         _signalEvents.emplace_back(std::move(eventData), std::move(match));
2571bf0ce4bSMatthew Barth     }
2589014980aSMatthew Barth     // Attach a timer to run the action of an event
2599014980aSMatthew Barth     auto eventTimer = std::get<timerPos>(event);
2609014980aSMatthew Barth     if (std::get<intervalPos>(eventTimer) != seconds(0))
2619014980aSMatthew Barth     {
2629014980aSMatthew Barth         std::unique_ptr<util::Timer> timer =
2639014980aSMatthew Barth             std::make_unique<util::Timer>(
2649014980aSMatthew Barth                 _sdEvents,
2659014980aSMatthew Barth                 [this,
266f9201abbSMatthew Barth                  action = &(std::get<actionsPos>(event)),
2679014980aSMatthew Barth                  group = &(std::get<groupPos>(event))]()
2689014980aSMatthew Barth                  {
2699014980aSMatthew Barth                      this->timerExpired(*group, *action);
2709014980aSMatthew Barth                  });
2719014980aSMatthew Barth         if (!timer->running())
2729014980aSMatthew Barth         {
2739014980aSMatthew Barth             timer->start(std::get<intervalPos>(eventTimer),
274*7b7ceb8dSMatthew Barth                          std::get<typePos>(eventTimer));
2759014980aSMatthew Barth         }
2769014980aSMatthew Barth         _timerEvents.emplace_back(std::move(timer));
2779014980aSMatthew Barth     }
278f9201abbSMatthew Barth     // Run action functions for initial event state
279f9201abbSMatthew Barth     std::for_each(
280f9201abbSMatthew Barth         std::get<actionsPos>(event).begin(),
281f9201abbSMatthew Barth         std::get<actionsPos>(event).end(),
282f9201abbSMatthew Barth         [this, &event](auto const& action)
283f9201abbSMatthew Barth         {
284f9201abbSMatthew Barth             action(*this,
2851bf0ce4bSMatthew Barth                    std::get<groupPos>(event));
286f9201abbSMatthew Barth         });
2871bf0ce4bSMatthew Barth }
2881bf0ce4bSMatthew Barth 
289f6b76d8eSMatthew Barth void Zone::removeEvent(const SetSpeedEvent& event)
290f6b76d8eSMatthew Barth {
291f6b76d8eSMatthew Barth     // Find the signal event to be removed
292f6b76d8eSMatthew Barth     auto it = std::find_if(
293f6b76d8eSMatthew Barth         _signalEvents.begin(),
294f6b76d8eSMatthew Barth         _signalEvents.end(),
295f6b76d8eSMatthew Barth         [&event](auto const& se)
296f6b76d8eSMatthew Barth         {
297f6b76d8eSMatthew Barth             auto seEventData = *std::get<signalEventDataPos>(se);
298f9201abbSMatthew Barth             if (std::get<eventActionsPos>(seEventData).size() !=
299f9201abbSMatthew Barth                 std::get<actionsPos>(event).size())
300f9201abbSMatthew Barth             {
301f9201abbSMatthew Barth                 return false;
302f9201abbSMatthew Barth             }
303f9201abbSMatthew Barth             else
304f9201abbSMatthew Barth             {
305f9201abbSMatthew Barth                 // TODO openbmc/openbmc#2328 - Use the action function target
306f9201abbSMatthew Barth                 // for comparison
307f9201abbSMatthew Barth                 auto actsEqual = [](auto const& a1,
308f9201abbSMatthew Barth                                     auto const& a2)
309f9201abbSMatthew Barth                         {
310f9201abbSMatthew Barth                             return a1.target_type().name() ==
311f9201abbSMatthew Barth                                    a2.target_type().name();
312f9201abbSMatthew Barth                         };
313f6b76d8eSMatthew Barth                 return
314f6b76d8eSMatthew Barth                 (
315f6b76d8eSMatthew Barth                     std::get<eventGroupPos>(seEventData) ==
316f6b76d8eSMatthew Barth                         std::get<groupPos>(event) &&
317f9201abbSMatthew Barth                     std::equal(std::get<actionsPos>(event).begin(),
318f9201abbSMatthew Barth                                std::get<actionsPos>(event).end(),
319f9201abbSMatthew Barth                                std::get<eventActionsPos>(seEventData).begin(),
320f9201abbSMatthew Barth                                actsEqual)
321f6b76d8eSMatthew Barth                 );
322f9201abbSMatthew Barth             }
323f6b76d8eSMatthew Barth         });
324f6b76d8eSMatthew Barth     if (it != std::end(_signalEvents))
325f6b76d8eSMatthew Barth     {
326f6b76d8eSMatthew Barth         std::get<signalEventDataPos>(*it).reset();
327336f18a5SMatthew Barth         if (std::get<signalMatchPos>(*it) != nullptr)
328336f18a5SMatthew Barth         {
329f6b76d8eSMatthew Barth             std::get<signalMatchPos>(*it).reset();
330336f18a5SMatthew Barth         }
331f6b76d8eSMatthew Barth         _signalEvents.erase(it);
332f6b76d8eSMatthew Barth     }
333f6b76d8eSMatthew Barth }
334f6b76d8eSMatthew Barth 
335f9201abbSMatthew Barth void Zone::timerExpired(Group eventGroup, std::vector<Action> eventActions)
3369014980aSMatthew Barth {
337f9201abbSMatthew Barth     // Perform the actions
338f9201abbSMatthew Barth     std::for_each(eventActions.begin(),
339f9201abbSMatthew Barth                   eventActions.end(),
340f9201abbSMatthew Barth                   [this, &eventGroup](auto const& action)
341f9201abbSMatthew Barth                   {
342f9201abbSMatthew Barth                       action(*this, eventGroup);
343f9201abbSMatthew Barth                   });
3449014980aSMatthew Barth }
3459014980aSMatthew Barth 
34638a93a8aSMatthew Barth void Zone::handleEvent(sdbusplus::message::message& msg,
34734f1bda2SMatthew Barth                        const EventData* eventData)
34838a93a8aSMatthew Barth {
34938a93a8aSMatthew Barth     // Handle the callback
35034f1bda2SMatthew Barth     std::get<eventHandlerPos>(*eventData)(_bus, msg, *this);
351f9201abbSMatthew Barth     // Perform the actions
352f9201abbSMatthew Barth     std::for_each(
353f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).begin(),
354f9201abbSMatthew Barth         std::get<eventActionsPos>(*eventData).end(),
355f9201abbSMatthew Barth         [this, &eventData](auto const& action)
356f9201abbSMatthew Barth         {
357f9201abbSMatthew Barth             action(*this,
35834f1bda2SMatthew Barth                    std::get<eventGroupPos>(*eventData));
359f9201abbSMatthew Barth         });
36038a93a8aSMatthew Barth }
36138a93a8aSMatthew Barth 
3627f88fe61SMatt Spinler }
3637f88fe61SMatt Spinler }
3647f88fe61SMatt Spinler }
365