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