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 = util::SDBusPlus::getService( 190 _bus, 191 it->first, 192 std::get<intfPos>(it->second)); 193 hasOwner = util::SDBusPlus::callMethodAndRead<bool>( 194 _bus, 195 "org.freedesktop.DBus", 196 "/org/freedesktop/DBus", 197 "org.freedesktop.DBus", 198 "NameHasOwner", 199 name); 200 } 201 catch (const InternalFailure& ife) 202 { 203 // Failed to get service name owner state 204 hasOwner = false; 205 } 206 setServiceOwner(group, name, hasOwner); 207 } 208 } 209 210 void Zone::setFloor(uint64_t speed) 211 { 212 // Check all entries are set to allow floor to be set 213 auto pred = [](auto const& entry) {return entry.second;}; 214 auto setFloor = std::all_of(_floorChange.begin(), 215 _floorChange.end(), 216 pred); 217 if (setFloor) 218 { 219 _floorSpeed = speed; 220 // Floor speed above target, update target to floor speed 221 if (_targetSpeed < _floorSpeed) 222 { 223 requestSpeedIncrease(_floorSpeed - _targetSpeed); 224 } 225 } 226 } 227 228 void Zone::requestSpeedIncrease(uint64_t targetDelta) 229 { 230 // Only increase speed when delta is higher than 231 // the current increase delta for the zone and currently under ceiling 232 if (targetDelta > _incSpeedDelta && 233 _targetSpeed < _ceilingSpeed) 234 { 235 auto requestTarget = getRequestSpeedBase(); 236 requestTarget = (targetDelta - _incSpeedDelta) + requestTarget; 237 _incSpeedDelta = targetDelta; 238 // Target speed can not go above a defined ceiling speed 239 if (requestTarget > _ceilingSpeed) 240 { 241 requestTarget = _ceilingSpeed; 242 } 243 // Cancel current timer countdown 244 if (_incTimer.running()) 245 { 246 _incTimer.stop(); 247 } 248 setSpeed(requestTarget); 249 // Start timer countdown for fan speed increase 250 _incTimer.start(_incDelay, 251 util::Timer::TimerType::oneshot); 252 } 253 } 254 255 void Zone::incTimerExpired() 256 { 257 // Clear increase delta when timer expires allowing additional speed 258 // increase requests or speed decreases to occur 259 _incSpeedDelta = 0; 260 } 261 262 void Zone::requestSpeedDecrease(uint64_t targetDelta) 263 { 264 // Only decrease the lowest target delta requested 265 if (_decSpeedDelta == 0 || targetDelta < _decSpeedDelta) 266 { 267 _decSpeedDelta = targetDelta; 268 } 269 } 270 271 void Zone::decTimerExpired() 272 { 273 // Check all entries are set to allow a decrease 274 auto pred = [](auto const& entry) {return entry.second;}; 275 auto decAllowed = std::all_of(_decAllowed.begin(), 276 _decAllowed.end(), 277 pred); 278 279 // Only decrease speeds when allowed, 280 // where no requested increases exist and 281 // the increase timer is not running 282 // (i.e. not in the middle of increasing) 283 if (decAllowed && _incSpeedDelta == 0 && !_incTimer.running()) 284 { 285 auto requestTarget = getRequestSpeedBase(); 286 // Request target speed should not start above ceiling 287 if (requestTarget > _ceilingSpeed) 288 { 289 requestTarget = _ceilingSpeed; 290 } 291 // Target speed can not go below the defined floor speed 292 if ((requestTarget < _decSpeedDelta) || 293 (requestTarget - _decSpeedDelta < _floorSpeed)) 294 { 295 requestTarget = _floorSpeed; 296 } 297 else 298 { 299 requestTarget = requestTarget - _decSpeedDelta; 300 } 301 setSpeed(requestTarget); 302 } 303 // Clear decrease delta when timer expires 304 _decSpeedDelta = 0; 305 // Decrease timer is restarted since its repeating 306 } 307 308 void Zone::initEvent(const SetSpeedEvent& event) 309 { 310 sdbusplus::message::message nullMsg{nullptr}; 311 312 for (auto& sig : std::get<signalsPos>(event)) 313 { 314 // Initialize the event signal using handler 315 std::get<sigHandlerPos>(sig)(_bus, nullMsg, *this); 316 // Setup signal matches of the property for event 317 std::unique_ptr<EventData> eventData = 318 std::make_unique<EventData>( 319 std::get<groupPos>(event), 320 std::get<sigMatchPos>(sig), 321 std::get<sigHandlerPos>(sig), 322 std::get<actionsPos>(event) 323 ); 324 std::unique_ptr<sdbusplus::server::match::match> match = nullptr; 325 if (!std::get<sigMatchPos>(sig).empty()) 326 { 327 match = std::make_unique<sdbusplus::server::match::match>( 328 _bus, 329 std::get<sigMatchPos>(sig).c_str(), 330 std::bind(std::mem_fn(&Zone::handleEvent), 331 this, 332 std::placeholders::_1, 333 eventData.get()) 334 ); 335 } 336 _signalEvents.emplace_back(std::move(eventData), std::move(match)); 337 } 338 // Attach a timer to run the action of an event 339 auto eventTimer = std::get<timerPos>(event); 340 if (std::get<intervalPos>(eventTimer) != seconds(0)) 341 { 342 // Associate event data with timer 343 std::unique_ptr<EventData> eventData = 344 std::make_unique<EventData>( 345 std::get<groupPos>(event), 346 "", 347 nullptr, 348 std::get<actionsPos>(event) 349 ); 350 std::unique_ptr<util::Timer> timer = 351 std::make_unique<util::Timer>( 352 _sdEvents, 353 [this, 354 action = &(std::get<actionsPos>(event)), 355 group = &(std::get<groupPos>(event))]() 356 { 357 this->timerExpired(*group, *action); 358 }); 359 if (!timer->running()) 360 { 361 timer->start(std::get<intervalPos>(eventTimer), 362 std::get<typePos>(eventTimer)); 363 } 364 addTimer(std::move(eventData), std::move(timer)); 365 } 366 // Run action functions for initial event state 367 std::for_each( 368 std::get<actionsPos>(event).begin(), 369 std::get<actionsPos>(event).end(), 370 [this, &event](auto const& action) 371 { 372 action(*this, 373 std::get<groupPos>(event)); 374 }); 375 } 376 377 void Zone::removeEvent(const SetSpeedEvent& event) 378 { 379 // Find the signal event to be removed 380 auto it = std::find_if( 381 _signalEvents.begin(), 382 _signalEvents.end(), 383 [&event](auto const& se) 384 { 385 auto seEventData = *std::get<signalEventDataPos>(se); 386 if (std::get<eventActionsPos>(seEventData).size() != 387 std::get<actionsPos>(event).size()) 388 { 389 return false; 390 } 391 else 392 { 393 // TODO openbmc/openbmc#2328 - Use the action function target 394 // for comparison 395 auto actsEqual = [](auto const& a1, 396 auto const& a2) 397 { 398 return a1.target_type().name() == 399 a2.target_type().name(); 400 }; 401 return 402 ( 403 std::get<eventGroupPos>(seEventData) == 404 std::get<groupPos>(event) && 405 std::equal(std::get<actionsPos>(event).begin(), 406 std::get<actionsPos>(event).end(), 407 std::get<eventActionsPos>(seEventData).begin(), 408 actsEqual) 409 ); 410 } 411 }); 412 if (it != std::end(_signalEvents)) 413 { 414 std::get<signalEventDataPos>(*it).reset(); 415 if (std::get<signalMatchPos>(*it) != nullptr) 416 { 417 std::get<signalMatchPos>(*it).reset(); 418 } 419 _signalEvents.erase(it); 420 } 421 } 422 423 std::vector<TimerEvent>::iterator Zone::findTimer( 424 const Group& eventGroup, 425 const std::vector<Action>& eventActions) 426 { 427 for (auto it = _timerEvents.begin(); it != _timerEvents.end(); ++it) 428 { 429 auto teEventData = *std::get<timerEventDataPos>(*it); 430 if (std::get<eventActionsPos>(teEventData).size() == 431 eventActions.size()) 432 { 433 // TODO openbmc/openbmc#2328 - Use the action function target 434 // for comparison 435 auto actsEqual = [](auto const& a1, 436 auto const& a2) 437 { 438 return a1.target_type().name() == 439 a2.target_type().name(); 440 }; 441 if (std::get<eventGroupPos>(teEventData) == eventGroup && 442 std::equal(eventActions.begin(), 443 eventActions.end(), 444 std::get<eventActionsPos>(teEventData).begin(), 445 actsEqual)) 446 { 447 return it; 448 } 449 } 450 } 451 452 return _timerEvents.end(); 453 } 454 455 void Zone::timerExpired(Group eventGroup, std::vector<Action> eventActions) 456 { 457 // Perform the actions 458 std::for_each(eventActions.begin(), 459 eventActions.end(), 460 [this, &eventGroup](auto const& action) 461 { 462 action(*this, eventGroup); 463 }); 464 } 465 466 void Zone::handleEvent(sdbusplus::message::message& msg, 467 const EventData* eventData) 468 { 469 // Handle the callback 470 std::get<eventHandlerPos>(*eventData)(_bus, msg, *this); 471 // Perform the actions 472 std::for_each( 473 std::get<eventActionsPos>(*eventData).begin(), 474 std::get<eventActionsPos>(*eventData).end(), 475 [this, &eventData](auto const& action) 476 { 477 action(*this, 478 std::get<eventGroupPos>(*eventData)); 479 }); 480 } 481 482 } 483 } 484 } 485