1 #include "powermode.hpp" 2 3 #include <fcntl.h> 4 #include <fmt/core.h> 5 #include <sys/ioctl.h> 6 7 #include <com/ibm/Host/Target/server.hpp> 8 #include <org/open_power/OCC/Device/error.hpp> 9 #include <phosphor-logging/elog-errors.hpp> 10 #include <phosphor-logging/log.hpp> 11 #include <xyz/openbmc_project/Common/error.hpp> 12 #include <xyz/openbmc_project/Control/Power/Mode/server.hpp> 13 14 #include <cassert> 15 #include <fstream> 16 #include <regex> 17 18 namespace open_power 19 { 20 namespace occ 21 { 22 namespace powermode 23 { 24 25 using namespace phosphor::logging; 26 using namespace std::literals::string_literals; 27 using namespace sdbusplus::org::open_power::OCC::Device::Error; 28 29 using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode; 30 31 using NotAllowed = sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 32 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 33 34 // Set the Master OCC 35 void PowerMode::setMasterOcc(const std::string& masterOccPath) 36 { 37 if (masterOccSet) 38 { 39 if (masterOccPath != path) 40 { 41 log<level::ERR>( 42 fmt::format( 43 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})", 44 occInstance, masterOccPath) 45 .c_str()); 46 if (occCmd) 47 { 48 occCmd.reset(); 49 } 50 } 51 } 52 path = masterOccPath; 53 occInstance = path.back() - '0'; 54 log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})", 55 occInstance, path.c_str()) 56 .c_str()); 57 if (!occCmd) 58 { 59 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance, 60 path.c_str()); 61 } 62 masterOccSet = true; 63 }; 64 65 // Called when DBus power mode gets changed 66 void PowerMode::modeChanged(sdbusplus::message_t& msg) 67 { 68 std::map<std::string, std::variant<std::string>> properties{}; 69 std::string interface; 70 std::string propVal; 71 msg.read(interface, properties); 72 const auto modeEntry = properties.find(POWER_MODE_PROP); 73 if (modeEntry != properties.end()) 74 { 75 auto modeEntryValue = modeEntry->second; 76 propVal = std::get<std::string>(modeEntryValue); 77 SysPwrMode newMode = convertStringToMode(propVal); 78 79 // If mode set was requested via direct dbus property change then we 80 // need to see if mode set is locked and ignore the request. We should 81 // not get to this path since the property change method should also be 82 // checking the mode lock status. 83 if (persistedData.getModeLock()) 84 { 85 // Fix up the mode property since we are going to ignore this 86 // set mode request. 87 log<level::ERR>( 88 "PowerMode::modeChanged: mode property changed while locked"); 89 SysPwrMode currentMode; 90 uint16_t oemModeData; 91 getMode(currentMode, oemModeData); 92 updateDbusMode(currentMode); 93 return; 94 } 95 96 if (newMode != SysPwrMode::NO_CHANGE) 97 { 98 // Update persisted data with new mode 99 persistedData.updateMode(newMode, 0); 100 101 log<level::INFO>( 102 fmt::format("DBus Power Mode Changed: {}", propVal).c_str()); 103 104 // Send mode change to OCC 105 sendModeChange(); 106 } 107 } 108 } 109 110 // Set the state of power mode lock. Writing persistent data via dbus method. 111 bool PowerMode::powerModeLock() 112 { 113 log<level::INFO>("PowerMode::powerModeLock: locking mode change"); 114 persistedData.updateModeLock(true); // write persistent data 115 return true; 116 } 117 118 // Get the state of power mode. Reading persistent data via dbus method. 119 bool PowerMode::powerModeLockStatus() 120 { 121 bool status = persistedData.getModeLock(); // read persistent data 122 log<level::INFO>(fmt::format("PowerMode::powerModeLockStatus: {}", 123 status ? "locked" : "unlocked") 124 .c_str()); 125 return status; 126 } 127 128 // Called from OCC PassThrough interface (via CE login / BMC command line) 129 bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData) 130 { 131 if (persistedData.getModeLock()) 132 { 133 log<level::INFO>("PowerMode::setMode: mode change blocked"); 134 return false; 135 } 136 137 if (updateDbusMode(newMode) == false) 138 { 139 // Unsupported mode 140 return false; 141 } 142 143 // Save mode 144 persistedData.updateMode(newMode, oemModeData); 145 146 // Send mode change to OCC 147 if (sendModeChange() != CmdStatus::SUCCESS) 148 { 149 // Mode change failed 150 return false; 151 } 152 153 return true; 154 } 155 156 // Convert PowerMode string to OCC SysPwrMode 157 // Returns NO_CHANGE if OEM or unsupported mode 158 SysPwrMode convertStringToMode(const std::string& i_modeString) 159 { 160 SysPwrMode pmode = SysPwrMode::NO_CHANGE; 161 162 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString); 163 if (mode == Mode::PowerMode::MaximumPerformance) 164 { 165 pmode = SysPwrMode::MAX_PERF; 166 } 167 else if (mode == Mode::PowerMode::PowerSaving) 168 { 169 pmode = SysPwrMode::POWER_SAVING; 170 } 171 else if (mode == Mode::PowerMode::Static) 172 { 173 pmode = SysPwrMode::STATIC; 174 } 175 else 176 { 177 if (mode != Mode::PowerMode::OEM) 178 { 179 log<level::ERR>( 180 fmt::format( 181 "convertStringToMode: Invalid Power Mode specified: {}", 182 i_modeString) 183 .c_str()); 184 } 185 } 186 187 return pmode; 188 } 189 190 // Check if Hypervisor target is PowerVM 191 bool isPowerVM() 192 { 193 namespace Hyper = sdbusplus::com::ibm::Host::server; 194 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor"; 195 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target"; 196 constexpr auto HYPE_PROP = "Target"; 197 198 bool powerVmTarget = false; 199 200 // This will throw exception on failure 201 auto& bus = utils::getBus(); 202 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE); 203 auto method = bus.new_method_call(service.c_str(), HYPE_PATH, 204 "org.freedesktop.DBus.Properties", "Get"); 205 method.append(HYPE_INTERFACE, HYPE_PROP); 206 auto reply = bus.call(method); 207 208 std::variant<std::string> hyperEntryValue; 209 reply.read(hyperEntryValue); 210 auto propVal = std::get<std::string>(hyperEntryValue); 211 if (Hyper::Target::convertHypervisorFromString(propVal) == 212 Hyper::Target::Hypervisor::PowerVM) 213 { 214 powerVmTarget = true; 215 } 216 217 log<level::DEBUG>( 218 fmt::format("isPowerVM returning {}", powerVmTarget).c_str()); 219 220 return powerVmTarget; 221 } 222 223 // Initialize persistent data and return true if successful 224 bool PowerMode::initPersistentData() 225 { 226 if (!persistedData.modeAvailable()) 227 { 228 // Read the default mode 229 SysPwrMode currentMode; 230 if (!getDefaultMode(currentMode)) 231 { 232 // Unable to read defaults 233 return false; 234 } 235 log<level::INFO>( 236 fmt::format("PowerMode::initPersistentData: Using default mode: {}", 237 currentMode) 238 .c_str()); 239 240 // Save default mode as current mode 241 persistedData.updateMode(currentMode, 0); 242 243 // Write default mode to DBus 244 updateDbusMode(currentMode); 245 } 246 247 if (!persistedData.ipsAvailable()) 248 { 249 // Read the default IPS parameters, write persistent file and update 250 // DBus 251 return useDefaultIPSParms(); 252 } 253 return true; 254 } 255 256 // Get the requested power mode and return true if successful 257 bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData) 258 { 259 currentMode = SysPwrMode::NO_CHANGE; 260 oemModeData = 0; 261 262 if (!persistedData.getMode(currentMode, oemModeData)) 263 { 264 // Persistent data not initialized, read defaults and update DBus 265 if (!initPersistentData()) 266 { 267 // Unable to read defaults from entity manager yet 268 return false; 269 } 270 return persistedData.getMode(currentMode, oemModeData); 271 } 272 273 return true; 274 } 275 276 // Set the power mode on DBus 277 bool PowerMode::updateDbusMode(const SysPwrMode newMode) 278 { 279 if (!VALID_POWER_MODE_SETTING(newMode) && 280 !VALID_OEM_POWER_MODE_SETTING(newMode)) 281 { 282 log<level::ERR>( 283 fmt::format( 284 "PowerMode::updateDbusMode - Requested power mode not supported: {}", 285 newMode) 286 .c_str()); 287 return false; 288 } 289 290 // Convert mode for DBus 291 ModeInterface::PowerMode dBusMode; 292 switch (newMode) 293 { 294 case SysPwrMode::STATIC: 295 dBusMode = Mode::PowerMode::Static; 296 break; 297 case SysPwrMode::POWER_SAVING: 298 dBusMode = Mode::PowerMode::PowerSaving; 299 break; 300 case SysPwrMode::MAX_PERF: 301 dBusMode = Mode::PowerMode::MaximumPerformance; 302 break; 303 default: 304 dBusMode = Mode::PowerMode::OEM; 305 } 306 307 // true = skip update signal 308 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true); 309 310 return true; 311 } 312 313 // Send mode change request to the master OCC 314 CmdStatus PowerMode::sendModeChange() 315 { 316 CmdStatus status; 317 318 if (!masterActive || !masterOccSet) 319 { 320 // Nothing to do 321 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active"); 322 return CmdStatus::SUCCESS; 323 } 324 325 if (!isPowerVM()) 326 { 327 // Mode change is only supported on PowerVM systems 328 log<level::DEBUG>( 329 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems"); 330 return CmdStatus::SUCCESS; 331 } 332 333 SysPwrMode newMode; 334 uint16_t oemModeData = 0; 335 getMode(newMode, oemModeData); 336 337 if (VALID_POWER_MODE_SETTING(newMode) || 338 VALID_OEM_POWER_MODE_SETTING(newMode)) 339 { 340 std::vector<std::uint8_t> cmd, rsp; 341 cmd.reserve(9); 342 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE)); 343 cmd.push_back(0x00); // Data Length (2 bytes) 344 cmd.push_back(0x06); 345 cmd.push_back(0x30); // Data (Version) 346 cmd.push_back(uint8_t(OccState::NO_CHANGE)); 347 cmd.push_back(uint8_t(newMode)); 348 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point) 349 cmd.push_back(oemModeData & 0xFF); // 350 cmd.push_back(0x00); // reserved 351 log<level::INFO>( 352 fmt::format( 353 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)", 354 newMode, oemModeData, occInstance, cmd.size()) 355 .c_str()); 356 status = occCmd->send(cmd, rsp); 357 if (status == CmdStatus::SUCCESS) 358 { 359 if (rsp.size() == 5) 360 { 361 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 362 { 363 log<level::ERR>( 364 fmt::format( 365 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}", 366 rsp[2]) 367 .c_str()); 368 dump_hex(rsp); 369 status = CmdStatus::FAILURE; 370 } 371 } 372 else 373 { 374 log<level::ERR>( 375 "PowerMode::sendModeChange: INVALID SET MODE response"); 376 dump_hex(rsp); 377 status = CmdStatus::FAILURE; 378 } 379 } 380 else 381 { 382 log<level::ERR>( 383 fmt::format( 384 "PowerMode::sendModeChange: SET_MODE FAILED with status={}", 385 status) 386 .c_str()); 387 } 388 } 389 else 390 { 391 log<level::ERR>( 392 fmt::format( 393 "PowerMode::sendModeChange: Unable to set power mode to {}", 394 newMode) 395 .c_str()); 396 status = CmdStatus::FAILURE; 397 } 398 399 return status; 400 } 401 402 void PowerMode::ipsChanged(sdbusplus::message_t& msg) 403 { 404 bool parmsChanged = false; 405 std::string interface; 406 std::map<std::string, std::variant<bool, uint8_t, uint64_t>> 407 ipsProperties{}; 408 msg.read(interface, ipsProperties); 409 410 // Read persisted values 411 bool ipsEnabled; 412 uint8_t enterUtil, exitUtil; 413 uint16_t enterTime, exitTime; 414 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 415 416 // Check for any changed data 417 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP); 418 if (ipsEntry != ipsProperties.end()) 419 { 420 ipsEnabled = std::get<bool>(ipsEntry->second); 421 log<level::INFO>( 422 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled) 423 .c_str()); 424 parmsChanged = true; 425 } 426 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL); 427 if (ipsEntry != ipsProperties.end()) 428 { 429 enterUtil = std::get<uint8_t>(ipsEntry->second); 430 log<level::INFO>( 431 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil) 432 .c_str()); 433 parmsChanged = true; 434 } 435 ipsEntry = ipsProperties.find(IPS_ENTER_TIME); 436 if (ipsEntry != ipsProperties.end()) 437 { 438 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 439 enterTime = 440 std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 441 log<level::INFO>( 442 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime) 443 .c_str()); 444 parmsChanged = true; 445 } 446 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL); 447 if (ipsEntry != ipsProperties.end()) 448 { 449 exitUtil = std::get<uint8_t>(ipsEntry->second); 450 log<level::INFO>( 451 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil) 452 .c_str()); 453 parmsChanged = true; 454 } 455 ipsEntry = ipsProperties.find(IPS_EXIT_TIME); 456 if (ipsEntry != ipsProperties.end()) 457 { 458 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 459 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 460 log<level::INFO>( 461 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime) 462 .c_str()); 463 parmsChanged = true; 464 } 465 466 if (parmsChanged) 467 { 468 if (exitUtil == 0) 469 { 470 // Setting the exitUtil to 0 will force restoring the default IPS 471 // parmeters (0 is not valid exit utilization) 472 log<level::INFO>( 473 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters"); 474 // Read the default IPS parameters, write persistent file and update 475 // DBus 476 useDefaultIPSParms(); 477 } 478 else 479 { 480 // Update persistant data with new DBus values 481 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 482 exitTime); 483 } 484 485 // Trigger IPS data to get sent to the OCC 486 sendIpsData(); 487 } 488 489 return; 490 } 491 492 /** @brief Get the Idle Power Saver properties from persisted data 493 * @return true if IPS parameters were read 494 */ 495 bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil, 496 uint16_t& enterTime, uint8_t& exitUtil, 497 uint16_t& exitTime) 498 { 499 // Defaults: 500 ipsEnabled = true; // Enabled 501 enterUtil = 8; // Enter Utilization (8%) 502 enterTime = 240; // Enter Delay Time (240s) 503 exitUtil = 12; // Exit Utilization (12%) 504 exitTime = 10; // Exit Delay Time (10s) 505 506 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 507 exitTime)) 508 { 509 // Persistent data not initialized, read defaults and update DBus 510 if (!initPersistentData()) 511 { 512 // Unable to read defaults from entity manager yet 513 return false; 514 } 515 516 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 517 exitTime); 518 } 519 520 if (enterUtil > exitUtil) 521 { 522 log<level::ERR>( 523 fmt::format( 524 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", 525 enterUtil, exitUtil) 526 .c_str()); 527 enterUtil = exitUtil; 528 } 529 530 return true; 531 } 532 533 // Set the Idle Power Saver data on DBus 534 bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil, 535 const uint16_t enterTime, const uint8_t exitUtil, 536 const uint16_t exitTime) 537 { 538 // true = skip update signal 539 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true); 540 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true); 541 // Convert time from seconds to ms 542 uint64_t msTime = enterTime * 1000; 543 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true); 544 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true); 545 msTime = exitTime * 1000; 546 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true); 547 548 return true; 549 } 550 551 // Send Idle Power Saver config data to the master OCC 552 CmdStatus PowerMode::sendIpsData() 553 { 554 if (!masterActive || !masterOccSet) 555 { 556 // Nothing to do 557 return CmdStatus::SUCCESS; 558 } 559 560 if (!isPowerVM()) 561 { 562 // Idle Power Saver data is only supported on PowerVM systems 563 log<level::DEBUG>( 564 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems"); 565 return CmdStatus::SUCCESS; 566 } 567 568 bool ipsEnabled; 569 uint8_t enterUtil, exitUtil; 570 uint16_t enterTime, exitTime; 571 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 572 573 log<level::INFO>( 574 fmt::format( 575 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s", 576 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) 577 .c_str()); 578 579 std::vector<std::uint8_t> cmd, rsp; 580 cmd.reserve(12); 581 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA)); 582 cmd.push_back(0x00); // Data Length (2 bytes) 583 cmd.push_back(0x09); // 584 cmd.push_back(0x11); // Config Format: IPS Settings 585 cmd.push_back(0x00); // Version 586 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable 587 cmd.push_back(enterTime >> 8); // Enter Delay Time 588 cmd.push_back(enterTime & 0xFF); // 589 cmd.push_back(enterUtil); // Enter Utilization 590 cmd.push_back(exitTime >> 8); // Exit Delay Time 591 cmd.push_back(exitTime & 0xFF); // 592 cmd.push_back(exitUtil); // Exit Utilization 593 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] " 594 "command to OCC{} ({} bytes)", 595 occInstance, cmd.size()) 596 .c_str()); 597 CmdStatus status = occCmd->send(cmd, rsp); 598 if (status == CmdStatus::SUCCESS) 599 { 600 if (rsp.size() == 5) 601 { 602 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 603 { 604 log<level::ERR>( 605 fmt::format( 606 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}", 607 rsp[2]) 608 .c_str()); 609 dump_hex(rsp); 610 status = CmdStatus::FAILURE; 611 } 612 } 613 else 614 { 615 log<level::ERR>( 616 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response"); 617 dump_hex(rsp); 618 status = CmdStatus::FAILURE; 619 } 620 } 621 else 622 { 623 log<level::ERR>( 624 fmt::format( 625 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}", 626 status) 627 .c_str()); 628 } 629 630 return status; 631 } 632 633 // Print the current values 634 void OccPersistData::print() 635 { 636 if (modeData.modeInitialized) 637 { 638 log<level::INFO>( 639 fmt::format( 640 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X} Locked{})", 641 modeData.mode, modeData.oemModeData, modeData.oemModeData, 642 modeData.modeLocked) 643 .c_str()); 644 } 645 if (modeData.ipsInitialized) 646 { 647 log<level::INFO>( 648 fmt::format( 649 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s", 650 modeData.ipsEnabled, modeData.ipsEnterUtil, 651 modeData.ipsEnterTime, modeData.ipsExitUtil, 652 modeData.ipsExitTime) 653 .c_str()); 654 } 655 } 656 657 // Saves the OEM mode data in the filesystem using cereal. 658 void OccPersistData::save() 659 { 660 std::filesystem::path opath = 661 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; 662 663 if (!std::filesystem::exists(opath.parent_path())) 664 { 665 std::filesystem::create_directory(opath.parent_path()); 666 } 667 668 log<level::DEBUG>( 669 fmt::format( 670 "OccPersistData::save: Writing Power Mode persisted data to {}", 671 opath.c_str()) 672 .c_str()); 673 // print(); 674 675 std::ofstream stream{opath.c_str()}; 676 cereal::JSONOutputArchive oarchive{stream}; 677 678 oarchive(modeData); 679 } 680 681 // Loads the OEM mode data in the filesystem using cereal. 682 void OccPersistData::load() 683 { 684 685 std::filesystem::path ipath = 686 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; 687 688 if (!std::filesystem::exists(ipath)) 689 { 690 modeData.modeInitialized = false; 691 modeData.ipsInitialized = false; 692 return; 693 } 694 695 log<level::DEBUG>( 696 fmt::format( 697 "OccPersistData::load: Reading Power Mode persisted data from {}", 698 ipath.c_str()) 699 .c_str()); 700 try 701 { 702 std::ifstream stream{ipath.c_str()}; 703 cereal::JSONInputArchive iarchive(stream); 704 iarchive(modeData); 705 } 706 catch (const std::exception& e) 707 { 708 auto error = errno; 709 log<level::ERR>( 710 fmt::format("OccPersistData::load: failed to read {}, errno={}", 711 ipath.c_str(), error) 712 .c_str()); 713 modeData.modeInitialized = false; 714 modeData.ipsInitialized = false; 715 } 716 717 // print(); 718 } 719 720 // Called when PowerModeProperties defaults are available on DBus 721 void PowerMode::defaultsReady(sdbusplus::message_t& msg) 722 { 723 std::map<std::string, std::variant<std::string>> properties{}; 724 std::string interface; 725 msg.read(interface, properties); 726 727 // If persistent data exists, then don't need to read defaults 728 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable())) 729 { 730 log<level::INFO>( 731 fmt::format( 732 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})", 733 persistedData.modeAvailable() ? 'y' : 'n', 734 persistedData.modeAvailable() ? 'y' : 'n') 735 .c_str()); 736 737 // Read default power mode defaults and update DBus 738 initPersistentData(); 739 } 740 } 741 742 // Get the default power mode from DBus and return true if success 743 bool PowerMode::getDefaultMode(SysPwrMode& defaultMode) 744 { 745 try 746 { 747 auto& bus = utils::getBus(); 748 std::string path = "/"; 749 std::string service = 750 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); 751 auto method = 752 bus.new_method_call(service.c_str(), path.c_str(), 753 "org.freedesktop.DBus.Properties", "Get"); 754 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode"); 755 auto reply = bus.call(method); 756 757 std::variant<std::string> stateEntryValue; 758 reply.read(stateEntryValue); 759 auto propVal = std::get<std::string>(stateEntryValue); 760 761 const std::string fullModeString = 762 PMODE_INTERFACE + ".PowerMode."s + propVal; 763 defaultMode = powermode::convertStringToMode(fullModeString); 764 if (!VALID_POWER_MODE_SETTING(defaultMode)) 765 { 766 log<level::ERR>( 767 fmt::format( 768 "PowerMode::getDefaultMode: Invalid default power mode found: {}", 769 defaultMode) 770 .c_str()); 771 // If default was read but not valid, use Max Performance 772 defaultMode = SysPwrMode::MAX_PERF; 773 return true; 774 } 775 } 776 catch (const sdbusplus::exception_t& e) 777 { 778 log<level::ERR>( 779 fmt::format("Unable to read Default Power Mode: {}", e.what()) 780 .c_str()); 781 return false; 782 } 783 784 return true; 785 } 786 787 /* Get the default Idle Power Saver properties and return true if successful */ 788 bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil, 789 uint16_t& enterTime, uint8_t& exitUtil, 790 uint16_t& exitTime) 791 { 792 // Defaults: 793 ipsEnabled = true; // Enabled 794 enterUtil = 8; // Enter Utilization (8%) 795 enterTime = 240; // Enter Delay Time (240s) 796 exitUtil = 12; // Exit Utilization (12%) 797 exitTime = 10; // Exit Delay Time (10s) 798 799 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>> 800 ipsProperties{}; 801 802 // Get all IPS properties from DBus 803 try 804 { 805 auto& bus = utils::getBus(); 806 std::string path = "/"; 807 std::string service = 808 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); 809 auto method = 810 bus.new_method_call(service.c_str(), path.c_str(), 811 "org.freedesktop.DBus.Properties", "GetAll"); 812 method.append(PMODE_DEFAULT_INTERFACE); 813 auto reply = bus.call(method); 814 reply.read(ipsProperties); 815 } 816 catch (const sdbusplus::exception_t& e) 817 { 818 log<level::ERR>( 819 fmt::format( 820 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}", 821 e.what()) 822 .c_str()); 823 return false; 824 } 825 826 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled"); 827 if (ipsEntry != ipsProperties.end()) 828 { 829 ipsEnabled = std::get<bool>(ipsEntry->second); 830 } 831 else 832 { 833 log<level::ERR>( 834 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled"); 835 } 836 837 ipsEntry = ipsProperties.find("EnterUtilizationPercent"); 838 if (ipsEntry != ipsProperties.end()) 839 { 840 enterUtil = std::get<uint64_t>(ipsEntry->second); 841 } 842 else 843 { 844 log<level::ERR>( 845 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent"); 846 } 847 848 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime"); 849 if (ipsEntry != ipsProperties.end()) 850 { 851 enterTime = std::get<uint64_t>(ipsEntry->second); 852 } 853 else 854 { 855 log<level::ERR>( 856 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime"); 857 } 858 859 ipsEntry = ipsProperties.find("ExitUtilizationPercent"); 860 if (ipsEntry != ipsProperties.end()) 861 { 862 exitUtil = std::get<uint64_t>(ipsEntry->second); 863 } 864 else 865 { 866 log<level::ERR>( 867 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent"); 868 } 869 870 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime"); 871 if (ipsEntry != ipsProperties.end()) 872 { 873 exitTime = std::get<uint64_t>(ipsEntry->second); 874 } 875 else 876 { 877 log<level::ERR>( 878 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime"); 879 } 880 881 if (enterUtil > exitUtil) 882 { 883 log<level::ERR>( 884 fmt::format( 885 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", 886 enterUtil, exitUtil) 887 .c_str()); 888 enterUtil = exitUtil; 889 } 890 891 return true; 892 } 893 894 /* Read default IPS parameters, save them to the persistent file and update 895 DBus. Return true if successful */ 896 bool PowerMode::useDefaultIPSParms() 897 { 898 // Read the default IPS parameters 899 bool ipsEnabled; 900 uint8_t enterUtil, exitUtil; 901 uint16_t enterTime, exitTime; 902 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, 903 exitTime)) 904 { 905 // Unable to read defaults 906 return false; 907 } 908 log<level::INFO>( 909 fmt::format( 910 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s", 911 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) 912 .c_str()); 913 914 // Save IPS parms to the persistent file 915 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, 916 exitTime); 917 918 // Write IPS parms to DBus 919 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); 920 } 921 922 #ifdef POWER10 923 924 // Starts to watch for IPS active state changes. 925 bool PowerMode::openIpsFile() 926 { 927 bool rc = true; 928 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK); 929 const int open_errno = errno; 930 if (fd < 0) 931 { 932 log<level::ERR>(fmt::format("openIpsFile Error({})={} : File={}", 933 open_errno, strerror(open_errno), 934 ipsStatusFile.c_str()) 935 .c_str()); 936 937 close(fd); 938 939 using namespace sdbusplus::org::open_power::OCC::Device::Error; 940 report<OpenFailure>( 941 phosphor::logging::org::open_power::OCC::Device::OpenFailure:: 942 CALLOUT_ERRNO(open_errno), 943 phosphor::logging::org::open_power::OCC::Device::OpenFailure:: 944 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str())); 945 946 // We are no longer watching the error 947 active(false); 948 949 watching = false; 950 rc = false; 951 // NOTE: this will leave the system not reporting IPS active state to 952 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 953 } 954 return rc; 955 } 956 957 // Starts to watch for IPS active state changes. 958 void PowerMode::addIpsWatch(bool poll) 959 { 960 // open file and register callback on file if we are not currently watching, 961 // and if poll=true, and if we are the master. 962 if ((!watching) && poll) 963 { 964 // Open the file 965 if (openIpsFile()) 966 { 967 // register the callback handler which sets 'watching' 968 registerIpsStatusCallBack(); 969 } 970 } 971 } 972 973 // Stops watching for IPS active state changes. 974 void PowerMode::removeIpsWatch() 975 { 976 // NOTE: we want to remove event, close file, and IPS active false no 977 // matter what the 'watching' flags is set to. 978 979 // We are no longer watching the error 980 active(false); 981 982 watching = false; 983 984 // Close file 985 close(fd); 986 987 // clears sourcePtr in the event source. 988 eventSource.reset(); 989 } 990 991 // Attaches the FD to event loop and registers the callback handler 992 void PowerMode::registerIpsStatusCallBack() 993 { 994 decltype(eventSource.get()) sourcePtr = nullptr; 995 996 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR, 997 ipsStatusCallBack, this); 998 if (r < 0) 999 { 1000 log<level::ERR>(fmt::format("sd_event_add_io: Error({})={} : File={}", 1001 r, strerror(-r), ipsStatusFile.c_str()) 1002 .c_str()); 1003 1004 using InternalFailure = 1005 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 1006 report<InternalFailure>(); 1007 1008 removeIpsWatch(); 1009 // NOTE: this will leave the system not reporting IPS active state to 1010 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 1011 } 1012 else 1013 { 1014 // puts sourcePtr in the event source. 1015 eventSource.reset(sourcePtr); 1016 // Set we are watching the error 1017 watching = true; 1018 } 1019 } 1020 1021 // Static function to redirect to non static analyze event function to be 1022 // able to read file and push onto dBus. 1023 int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/, 1024 uint32_t /*revents*/, void* userData) 1025 { 1026 auto pmode = static_cast<PowerMode*>(userData); 1027 pmode->analyzeIpsEvent(); 1028 return 0; 1029 } 1030 1031 // Function to Read SysFs file change on IPS state and push on dBus. 1032 void PowerMode::analyzeIpsEvent() 1033 { 1034 // Need to seek to START, else the poll returns immediately telling 1035 // there is data to be read. if not done this floods the system. 1036 auto r = lseek(fd, 0, SEEK_SET); 1037 const int open_errno = errno; 1038 if (r < 0) 1039 { 1040 // NOTE: upon file access error we can not just re-open file, we have to 1041 // remove and add to watch. 1042 removeIpsWatch(); 1043 addIpsWatch(true); 1044 } 1045 1046 // if we are 'watching' that is the file seek, or the re-open passed.. we 1047 // can read the data 1048 if (watching) 1049 { 1050 // This file gets created when polling OCCs. A value or length of 0 is 1051 // deemed success. That means we would disable IPS active on dbus. 1052 char data; 1053 bool ipsState = false; 1054 const auto len = read(fd, &data, sizeof(data)); 1055 const int readErrno = errno; 1056 if (len <= 0) 1057 { 1058 removeIpsWatch(); 1059 1060 log<level::ERR>( 1061 fmt::format("IPS state Read Error({})={} : File={} : len={}", 1062 readErrno, strerror(readErrno), 1063 ipsStatusFile.c_str(), len) 1064 .c_str()); 1065 1066 report<ReadFailure>( 1067 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 1068 CALLOUT_ERRNO(readErrno), 1069 phosphor::logging::org::open_power::OCC::Device::ReadFailure:: 1070 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str())); 1071 1072 // NOTE: this will leave the system not reporting IPS active state 1073 // to Fan Controls, Until an APP reload, or IPL and we will attempt 1074 // again. 1075 } 1076 else 1077 { 1078 // Data returned in ASCII. 1079 // convert to integer. atoi() 1080 // from OCC_P10_FW_Interfaces spec 1081 // Bit 6: IPS active 1 indicates enabled. 1082 // Bit 7: IPS enabled. 1 indicates enabled. 1083 // mask off bit 6 --> & 0x02 1084 // Shift left one bit and store as bool. >> 1 1085 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1); 1086 } 1087 1088 // This will only set IPS active dbus if different than current. 1089 active(ipsState); 1090 } 1091 else 1092 { 1093 removeIpsWatch(); 1094 1095 // If the Retry did not get to "watching = true" we already have an 1096 // error log, just post trace. 1097 log<level::ERR>(fmt::format("Retry on File seek Error({})={} : File={}", 1098 open_errno, strerror(open_errno), 1099 ipsStatusFile.c_str()) 1100 .c_str()); 1101 1102 // NOTE: this will leave the system not reporting IPS active state to 1103 // Fan Controls, Until an APP reload, or IPL and we will attempt again. 1104 } 1105 1106 return; 1107 } 1108 1109 // overrides read/write to powerMode dbus property. 1110 Mode::PowerMode PowerMode::powerMode(Mode::PowerMode value) 1111 { 1112 if (persistedData.getModeLock()) 1113 { 1114 log<level::INFO>("PowerMode::powerMode: mode property change blocked"); 1115 elog<NotAllowed>(xyz::openbmc_project::Common::NotAllowed::REASON( 1116 "mode change not allowed due to lock")); 1117 return value; 1118 } 1119 else 1120 { 1121 return Mode::powerMode(value); 1122 } 1123 } 1124 #endif 1125 1126 /* Set dbus property to SAFE mode(true) or clear(false) only if different */ 1127 void PowerMode::updateDbusSafeMode(const bool safeModeReq) 1128 { 1129 log<level::DEBUG>( 1130 fmt::format("PowerMode:updateDbusSafeMode: Update dbus state ({})", 1131 safeModeReq) 1132 .c_str()); 1133 1134 // Note; this function checks and only updates if different. 1135 Mode::safeMode(safeModeReq); 1136 } 1137 1138 } // namespace powermode 1139 1140 } // namespace occ 1141 1142 } // namespace open_power 1143