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