| #include "powermode.hpp" |
| |
| #include <fmt/core.h> |
| |
| #include <com/ibm/Host/Target/server.hpp> |
| #include <phosphor-logging/log.hpp> |
| #include <xyz/openbmc_project/Control/Power/Mode/server.hpp> |
| |
| #include <cassert> |
| #include <fstream> |
| #include <regex> |
| |
| namespace open_power |
| { |
| namespace occ |
| { |
| namespace powermode |
| { |
| |
| using namespace phosphor::logging; |
| using namespace std::literals::string_literals; |
| |
| using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode; |
| |
| // Set the Master OCC |
| void PowerMode::setMasterOcc(const std::string& masterOccPath) |
| { |
| if (masterOccSet) |
| { |
| if (masterOccPath != path) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::setMasterOcc: Master changed (was OCC{}, {})", |
| occInstance, masterOccPath) |
| .c_str()); |
| if (occCmd) |
| { |
| occCmd.reset(); |
| } |
| } |
| } |
| path = masterOccPath; |
| occInstance = path.back() - '0'; |
| log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})", |
| occInstance, path.c_str()) |
| .c_str()); |
| if (!occCmd) |
| { |
| occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance, |
| path.c_str()); |
| } |
| masterOccSet = true; |
| }; |
| |
| // Called when DBus power mode gets changed |
| void PowerMode::modeChanged(sdbusplus::message::message& msg) |
| { |
| std::map<std::string, std::variant<std::string>> properties{}; |
| std::string interface; |
| std::string propVal; |
| msg.read(interface, properties); |
| const auto modeEntry = properties.find(POWER_MODE_PROP); |
| if (modeEntry != properties.end()) |
| { |
| auto modeEntryValue = modeEntry->second; |
| propVal = std::get<std::string>(modeEntryValue); |
| SysPwrMode newMode = convertStringToMode(propVal); |
| if (newMode != SysPwrMode::NO_CHANGE) |
| { |
| // Update persisted data with new mode |
| persistedData.updateMode(newMode, 0); |
| |
| log<level::INFO>( |
| fmt::format("DBus Power Mode Changed: {}", propVal).c_str()); |
| |
| // Send mode change to OCC |
| sendModeChange(); |
| } |
| } |
| } |
| |
| // Called from OCC PassThrough interface (via CE login / BMC command line) |
| bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData) |
| { |
| if (updateDbusMode(newMode) == false) |
| { |
| // Unsupported mode |
| return false; |
| } |
| |
| // Save mode |
| persistedData.updateMode(newMode, oemModeData); |
| |
| // Send mode change to OCC |
| if (sendModeChange() != CmdStatus::SUCCESS) |
| { |
| // Mode change failed |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Convert PowerMode string to OCC SysPwrMode |
| // Returns NO_CHANGE if OEM or unsupported mode |
| SysPwrMode convertStringToMode(const std::string& i_modeString) |
| { |
| SysPwrMode pmode = SysPwrMode::NO_CHANGE; |
| |
| Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString); |
| if (mode == Mode::PowerMode::MaximumPerformance) |
| { |
| pmode = SysPwrMode::MAX_PERF; |
| } |
| else if (mode == Mode::PowerMode::PowerSaving) |
| { |
| pmode = SysPwrMode::POWER_SAVING; |
| } |
| else if (mode == Mode::PowerMode::Static) |
| { |
| pmode = SysPwrMode::STATIC; |
| } |
| else |
| { |
| if (mode != Mode::PowerMode::OEM) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "convertStringToMode: Invalid Power Mode specified: {}", |
| i_modeString) |
| .c_str()); |
| } |
| } |
| |
| return pmode; |
| } |
| |
| // Check if Hypervisor target is PowerVM |
| bool isPowerVM() |
| { |
| namespace Hyper = sdbusplus::com::ibm::Host::server; |
| constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor"; |
| constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target"; |
| constexpr auto HYPE_PROP = "Target"; |
| |
| bool powerVmTarget = false; |
| |
| // This will throw exception on failure |
| auto& bus = utils::getBus(); |
| auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE); |
| auto method = bus.new_method_call(service.c_str(), HYPE_PATH, |
| "org.freedesktop.DBus.Properties", "Get"); |
| method.append(HYPE_INTERFACE, HYPE_PROP); |
| auto reply = bus.call(method); |
| |
| std::variant<std::string> hyperEntryValue; |
| reply.read(hyperEntryValue); |
| auto propVal = std::get<std::string>(hyperEntryValue); |
| if (Hyper::Target::convertHypervisorFromString(propVal) == |
| Hyper::Target::Hypervisor::PowerVM) |
| { |
| powerVmTarget = true; |
| } |
| |
| log<level::DEBUG>( |
| fmt::format("isPowerVM returning {}", powerVmTarget).c_str()); |
| |
| return powerVmTarget; |
| } |
| |
| // Initialize persistent data and return true if successful |
| bool PowerMode::initPersistentData() |
| { |
| if (!persistedData.modeAvailable()) |
| { |
| // Read the default mode |
| SysPwrMode currentMode; |
| if (!getDefaultMode(currentMode)) |
| { |
| // Unable to read defaults |
| return false; |
| } |
| log<level::INFO>( |
| fmt::format("PowerMode::initPersistentData: Using default mode: {}", |
| currentMode) |
| .c_str()); |
| |
| // Save default mode as current mode |
| persistedData.updateMode(currentMode, 0); |
| |
| // Write default mode to DBus |
| updateDbusMode(currentMode); |
| } |
| |
| if (!persistedData.ipsAvailable()) |
| { |
| // Read the default IPS parameters, write persistent file and update |
| // DBus |
| return useDefaultIPSParms(); |
| } |
| return true; |
| } |
| |
| // Get the requested power mode and return true if successful |
| bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData) |
| { |
| currentMode = SysPwrMode::NO_CHANGE; |
| oemModeData = 0; |
| |
| if (!persistedData.getMode(currentMode, oemModeData)) |
| { |
| // Persistent data not initialized, read defaults and update DBus |
| if (!initPersistentData()) |
| { |
| // Unable to read defaults from entity manager yet |
| return false; |
| } |
| return persistedData.getMode(currentMode, oemModeData); |
| } |
| |
| return true; |
| } |
| |
| // Set the power mode on DBus |
| bool PowerMode::updateDbusMode(const SysPwrMode newMode) |
| { |
| if (!VALID_POWER_MODE_SETTING(newMode) && |
| !VALID_OEM_POWER_MODE_SETTING(newMode)) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::updateDbusMode - Requested power mode not supported: {}", |
| newMode) |
| .c_str()); |
| return false; |
| } |
| |
| // Convert mode for DBus |
| ModeInterface::PowerMode dBusMode; |
| switch (newMode) |
| { |
| case SysPwrMode::STATIC: |
| dBusMode = Mode::PowerMode::Static; |
| break; |
| case SysPwrMode::POWER_SAVING: |
| dBusMode = Mode::PowerMode::PowerSaving; |
| break; |
| case SysPwrMode::MAX_PERF: |
| dBusMode = Mode::PowerMode::MaximumPerformance; |
| break; |
| default: |
| dBusMode = Mode::PowerMode::OEM; |
| } |
| |
| // true = skip update signal |
| ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true); |
| |
| return true; |
| } |
| |
| // Send mode change request to the master OCC |
| CmdStatus PowerMode::sendModeChange() |
| { |
| CmdStatus status; |
| |
| if (!masterActive || !masterOccSet) |
| { |
| // Nothing to do |
| log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active"); |
| return CmdStatus::SUCCESS; |
| } |
| |
| if (!isPowerVM()) |
| { |
| // Mode change is only supported on PowerVM systems |
| log<level::DEBUG>( |
| "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems"); |
| return CmdStatus::SUCCESS; |
| } |
| |
| SysPwrMode newMode; |
| uint16_t oemModeData = 0; |
| getMode(newMode, oemModeData); |
| |
| if (VALID_POWER_MODE_SETTING(newMode) || |
| VALID_OEM_POWER_MODE_SETTING(newMode)) |
| { |
| std::vector<std::uint8_t> cmd, rsp; |
| cmd.reserve(9); |
| cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE)); |
| cmd.push_back(0x00); // Data Length (2 bytes) |
| cmd.push_back(0x06); |
| cmd.push_back(0x30); // Data (Version) |
| cmd.push_back(uint8_t(OccState::NO_CHANGE)); |
| cmd.push_back(uint8_t(newMode)); |
| cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point) |
| cmd.push_back(oemModeData & 0xFF); // |
| cmd.push_back(0x00); // reserved |
| log<level::INFO>( |
| fmt::format( |
| "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)", |
| newMode, oemModeData, occInstance, cmd.size()) |
| .c_str()); |
| status = occCmd->send(cmd, rsp); |
| if (status == CmdStatus::SUCCESS) |
| { |
| if (rsp.size() == 5) |
| { |
| if (RspStatus::SUCCESS != RspStatus(rsp[2])) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}", |
| rsp[2]) |
| .c_str()); |
| dump_hex(rsp); |
| status = CmdStatus::FAILURE; |
| } |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::sendModeChange: INVALID SET MODE response"); |
| dump_hex(rsp); |
| status = CmdStatus::FAILURE; |
| } |
| } |
| else |
| { |
| if (status == CmdStatus::OPEN_FAILURE) |
| { |
| // OCC not active yet |
| status = CmdStatus::SUCCESS; |
| } |
| else |
| { |
| log<level::ERR>("PowerMode::sendModeChange: SET_MODE FAILED!"); |
| } |
| } |
| } |
| else |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::sendModeChange: Unable to set power mode to {}", |
| newMode) |
| .c_str()); |
| status = CmdStatus::FAILURE; |
| } |
| |
| return status; |
| } |
| |
| void PowerMode::ipsChanged(sdbusplus::message::message& msg) |
| { |
| bool parmsChanged = false; |
| std::string interface; |
| std::map<std::string, std::variant<bool, uint8_t, uint64_t>> |
| ipsProperties{}; |
| msg.read(interface, ipsProperties); |
| |
| // Read persisted values |
| bool ipsEnabled; |
| uint8_t enterUtil, exitUtil; |
| uint16_t enterTime, exitTime; |
| getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); |
| |
| // Check for any changed data |
| auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| ipsEnabled = std::get<bool>(ipsEntry->second); |
| log<level::INFO>( |
| fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled) |
| .c_str()); |
| parmsChanged = true; |
| } |
| ipsEntry = ipsProperties.find(IPS_ENTER_UTIL); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| enterUtil = std::get<uint8_t>(ipsEntry->second); |
| log<level::INFO>( |
| fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil) |
| .c_str()); |
| parmsChanged = true; |
| } |
| ipsEntry = ipsProperties.find(IPS_ENTER_TIME); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); |
| enterTime = |
| std::chrono::duration_cast<std::chrono::seconds>(ms).count(); |
| log<level::INFO>( |
| fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime) |
| .c_str()); |
| parmsChanged = true; |
| } |
| ipsEntry = ipsProperties.find(IPS_EXIT_UTIL); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| exitUtil = std::get<uint8_t>(ipsEntry->second); |
| log<level::INFO>( |
| fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil) |
| .c_str()); |
| parmsChanged = true; |
| } |
| ipsEntry = ipsProperties.find(IPS_EXIT_TIME); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); |
| exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count(); |
| log<level::INFO>( |
| fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime) |
| .c_str()); |
| parmsChanged = true; |
| } |
| |
| if (parmsChanged) |
| { |
| if (exitUtil == 0) |
| { |
| // Setting the exitUtil to 0 will force restoring the default IPS |
| // parmeters (0 is not valid exit utilization) |
| log<level::INFO>( |
| "Idle Power Saver Exit Utilization is 0%. Restoring default parameters"); |
| // Read the default IPS parameters, write persistent file and update |
| // DBus |
| useDefaultIPSParms(); |
| } |
| else |
| { |
| // Update persistant data with new DBus values |
| persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, |
| exitTime); |
| } |
| |
| // Trigger IPS data to get sent to the OCC |
| sendIpsData(); |
| } |
| |
| return; |
| } |
| |
| /** @brief Get the Idle Power Saver properties from persisted data |
| * @return true if IPS parameters were read |
| */ |
| bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil, |
| uint16_t& enterTime, uint8_t& exitUtil, |
| uint16_t& exitTime) |
| { |
| // Defaults: |
| ipsEnabled = true; // Enabled |
| enterUtil = 8; // Enter Utilization (8%) |
| enterTime = 240; // Enter Delay Time (240s) |
| exitUtil = 12; // Exit Utilization (12%) |
| exitTime = 10; // Exit Delay Time (10s) |
| |
| if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, |
| exitTime)) |
| { |
| // Persistent data not initialized, read defaults and update DBus |
| if (!initPersistentData()) |
| { |
| // Unable to read defaults from entity manager yet |
| return false; |
| } |
| |
| persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil, |
| exitTime); |
| } |
| |
| if (enterUtil > exitUtil) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", |
| enterUtil, exitUtil) |
| .c_str()); |
| enterUtil = exitUtil; |
| } |
| |
| return true; |
| } |
| |
| // Set the Idle Power Saver data on DBus |
| bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil, |
| const uint16_t enterTime, const uint8_t exitUtil, |
| const uint16_t exitTime) |
| { |
| // true = skip update signal |
| IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true); |
| IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true); |
| // Convert time from seconds to ms |
| uint64_t msTime = enterTime * 1000; |
| IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true); |
| IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true); |
| msTime = exitTime * 1000; |
| IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true); |
| |
| return true; |
| } |
| |
| // Send Idle Power Saver config data to the master OCC |
| CmdStatus PowerMode::sendIpsData() |
| { |
| if (!masterActive || !masterOccSet) |
| { |
| // Nothing to do |
| return CmdStatus::SUCCESS; |
| } |
| |
| if (!isPowerVM()) |
| { |
| // Idle Power Saver data is only supported on PowerVM systems |
| log<level::DEBUG>( |
| "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems"); |
| return CmdStatus::SUCCESS; |
| } |
| |
| bool ipsEnabled; |
| uint8_t enterUtil, exitUtil; |
| uint16_t enterTime, exitTime; |
| getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); |
| |
| log<level::INFO>( |
| fmt::format( |
| "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s", |
| ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) |
| .c_str()); |
| |
| std::vector<std::uint8_t> cmd, rsp; |
| cmd.reserve(12); |
| cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA)); |
| cmd.push_back(0x00); // Data Length (2 bytes) |
| cmd.push_back(0x09); // |
| cmd.push_back(0x11); // Config Format: IPS Settings |
| cmd.push_back(0x00); // Version |
| cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable |
| cmd.push_back(enterTime >> 8); // Enter Delay Time |
| cmd.push_back(enterTime & 0xFF); // |
| cmd.push_back(enterUtil); // Enter Utilization |
| cmd.push_back(exitTime >> 8); // Exit Delay Time |
| cmd.push_back(exitTime & 0xFF); // |
| cmd.push_back(exitUtil); // Exit Utilization |
| log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] " |
| "command to OCC{} ({} bytes)", |
| occInstance, cmd.size()) |
| .c_str()); |
| CmdStatus status = occCmd->send(cmd, rsp); |
| if (status == CmdStatus::SUCCESS) |
| { |
| if (rsp.size() == 5) |
| { |
| if (RspStatus::SUCCESS != RspStatus(rsp[2])) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}", |
| rsp[2]) |
| .c_str()); |
| dump_hex(rsp); |
| status = CmdStatus::FAILURE; |
| } |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response"); |
| dump_hex(rsp); |
| status = CmdStatus::FAILURE; |
| } |
| } |
| else |
| { |
| if (status == CmdStatus::OPEN_FAILURE) |
| { |
| // OCC not active yet |
| status = CmdStatus::SUCCESS; |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::sendIpsData: SET_CFG_DATA[IPS] FAILED!"); |
| } |
| } |
| |
| return status; |
| } |
| |
| // Print the current values |
| void OccPersistData::print() |
| { |
| if (modeData.modeInitialized) |
| { |
| log<level::INFO>( |
| fmt::format( |
| "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})", |
| modeData.mode, modeData.oemModeData, modeData.oemModeData) |
| .c_str()); |
| } |
| if (modeData.ipsInitialized) |
| { |
| log<level::INFO>( |
| fmt::format( |
| "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s", |
| modeData.ipsEnabled, modeData.ipsEnterUtil, |
| modeData.ipsEnterTime, modeData.ipsExitUtil, |
| modeData.ipsExitTime) |
| .c_str()); |
| } |
| } |
| |
| // Saves the OEM mode data in the filesystem using cereal. |
| void OccPersistData::save() |
| { |
| std::filesystem::path opath = |
| std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; |
| |
| if (!std::filesystem::exists(opath.parent_path())) |
| { |
| std::filesystem::create_directory(opath.parent_path()); |
| } |
| |
| log<level::DEBUG>( |
| fmt::format( |
| "OccPersistData::save: Writing Power Mode persisted data to {}", |
| opath.c_str()) |
| .c_str()); |
| // print(); |
| |
| std::ofstream stream{opath.c_str()}; |
| cereal::JSONOutputArchive oarchive{stream}; |
| |
| oarchive(modeData); |
| } |
| |
| // Loads the OEM mode data in the filesystem using cereal. |
| void OccPersistData::load() |
| { |
| |
| std::filesystem::path ipath = |
| std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename; |
| |
| if (!std::filesystem::exists(ipath)) |
| { |
| modeData.modeInitialized = false; |
| modeData.ipsInitialized = false; |
| return; |
| } |
| |
| log<level::DEBUG>( |
| fmt::format( |
| "OccPersistData::load: Reading Power Mode persisted data from {}", |
| ipath.c_str()) |
| .c_str()); |
| try |
| { |
| std::ifstream stream{ipath.c_str()}; |
| cereal::JSONInputArchive iarchive(stream); |
| iarchive(modeData); |
| } |
| catch (const std::exception& e) |
| { |
| auto error = errno; |
| log<level::ERR>( |
| fmt::format("OccPersistData::load: failed to read {}, errno={}", |
| ipath.c_str(), error) |
| .c_str()); |
| modeData.modeInitialized = false; |
| modeData.ipsInitialized = false; |
| } |
| |
| // print(); |
| } |
| |
| // Called when PowerModeProperties defaults are available on DBus |
| void PowerMode::defaultsReady(sdbusplus::message::message& msg) |
| { |
| std::map<std::string, std::variant<std::string>> properties{}; |
| std::string interface; |
| msg.read(interface, properties); |
| |
| // If persistent data exists, then don't need to read defaults |
| if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable())) |
| { |
| log<level::INFO>( |
| fmt::format( |
| "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})", |
| persistedData.modeAvailable() ? 'y' : 'n', |
| persistedData.modeAvailable() ? 'y' : 'n') |
| .c_str()); |
| |
| // Read default power mode defaults and update DBus |
| initPersistentData(); |
| } |
| } |
| |
| // Get the default power mode from DBus and return true if success |
| bool PowerMode::getDefaultMode(SysPwrMode& defaultMode) |
| { |
| try |
| { |
| auto& bus = utils::getBus(); |
| std::string path = "/"; |
| std::string service = |
| utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); |
| auto method = |
| bus.new_method_call(service.c_str(), path.c_str(), |
| "org.freedesktop.DBus.Properties", "Get"); |
| method.append(PMODE_DEFAULT_INTERFACE, "PowerMode"); |
| auto reply = bus.call(method); |
| |
| std::variant<std::string> stateEntryValue; |
| reply.read(stateEntryValue); |
| auto propVal = std::get<std::string>(stateEntryValue); |
| |
| const std::string fullModeString = |
| PMODE_INTERFACE + ".PowerMode."s + propVal; |
| defaultMode = powermode::convertStringToMode(fullModeString); |
| if (!VALID_POWER_MODE_SETTING(defaultMode)) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "PowerMode::getDefaultMode: Invalid default power mode found: {}", |
| defaultMode) |
| .c_str()); |
| // If default was read but not valid, use Max Performance |
| defaultMode = SysPwrMode::MAX_PERF; |
| return true; |
| } |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| log<level::ERR>( |
| fmt::format("Unable to read Default Power Mode: {}", e.what()) |
| .c_str()); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /* Get the default Idle Power Saver properties and return true if successful */ |
| bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil, |
| uint16_t& enterTime, uint8_t& exitUtil, |
| uint16_t& exitTime) |
| { |
| // Defaults: |
| ipsEnabled = true; // Enabled |
| enterUtil = 8; // Enter Utilization (8%) |
| enterTime = 240; // Enter Delay Time (240s) |
| exitUtil = 12; // Exit Utilization (12%) |
| exitTime = 10; // Exit Delay Time (10s) |
| |
| std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>> |
| ipsProperties{}; |
| |
| // Get all IPS properties from DBus |
| try |
| { |
| auto& bus = utils::getBus(); |
| std::string path = "/"; |
| std::string service = |
| utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path); |
| auto method = |
| bus.new_method_call(service.c_str(), path.c_str(), |
| "org.freedesktop.DBus.Properties", "GetAll"); |
| method.append(PMODE_DEFAULT_INTERFACE); |
| auto reply = bus.call(method); |
| reply.read(ipsProperties); |
| } |
| catch (const sdbusplus::exception::exception& e) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "Unable to read Default Idle Power Saver parameters so it will be disabled: {}", |
| e.what()) |
| .c_str()); |
| return false; |
| } |
| |
| auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled"); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| ipsEnabled = std::get<bool>(ipsEntry->second); |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled"); |
| } |
| |
| ipsEntry = ipsProperties.find("EnterUtilizationPercent"); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| enterUtil = std::get<uint64_t>(ipsEntry->second); |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent"); |
| } |
| |
| ipsEntry = ipsProperties.find("EnterUtilizationDwellTime"); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| enterTime = std::get<uint64_t>(ipsEntry->second); |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime"); |
| } |
| |
| ipsEntry = ipsProperties.find("ExitUtilizationPercent"); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| exitUtil = std::get<uint64_t>(ipsEntry->second); |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent"); |
| } |
| |
| ipsEntry = ipsProperties.find("ExitUtilizationDwellTime"); |
| if (ipsEntry != ipsProperties.end()) |
| { |
| exitTime = std::get<uint64_t>(ipsEntry->second); |
| } |
| else |
| { |
| log<level::ERR>( |
| "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime"); |
| } |
| |
| if (enterUtil > exitUtil) |
| { |
| log<level::ERR>( |
| fmt::format( |
| "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", |
| enterUtil, exitUtil) |
| .c_str()); |
| enterUtil = exitUtil; |
| } |
| |
| return true; |
| } |
| |
| /* Read default IPS parameters, save them to the persistent file and update |
| DBus. Return true if successful */ |
| bool PowerMode::useDefaultIPSParms() |
| { |
| // Read the default IPS parameters |
| bool ipsEnabled; |
| uint8_t enterUtil, exitUtil; |
| uint16_t enterTime, exitTime; |
| if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, |
| exitTime)) |
| { |
| // Unable to read defaults |
| return false; |
| } |
| log<level::INFO>( |
| fmt::format( |
| "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s", |
| ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) |
| .c_str()); |
| |
| // Save IPS parms to the persistent file |
| persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil, |
| exitTime); |
| |
| // Write IPS parms to DBus |
| return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime); |
| } |
| |
| } // namespace powermode |
| |
| } // namespace occ |
| |
| } // namespace open_power |