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