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