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