blob: f0910880424628a677d544b343b0c1026b1e63bf [file] [log] [blame]
Chris Cain36f9cde2021-11-22 11:18:21 -06001#include "powermode.hpp"
2
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05003#include "elog-errors.hpp"
4
5#include <fcntl.h>
Chris Cain78e86012021-03-04 16:15:31 -06006#include <fmt/core.h>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -05007#include <sys/ioctl.h>
Chris Cain78e86012021-03-04 16:15:31 -06008
Chris Cain36f9cde2021-11-22 11:18:21 -06009#include <com/ibm/Host/Target/server.hpp>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050010#include <org/open_power/OCC/Device/error.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060011#include <phosphor-logging/log.hpp>
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050012#include <xyz/openbmc_project/Common/error.hpp>
Chris Cain78e86012021-03-04 16:15:31 -060013#include <xyz/openbmc_project/Control/Power/Mode/server.hpp>
14
George Liub5ca1012021-09-10 12:53:11 +080015#include <cassert>
Chris Cain36f9cde2021-11-22 11:18:21 -060016#include <fstream>
George Liub5ca1012021-09-10 12:53:11 +080017#include <regex>
18
Chris Cain78e86012021-03-04 16:15:31 -060019namespace open_power
20{
21namespace occ
22{
23namespace powermode
24{
25
26using namespace phosphor::logging;
Chris Cain1be43372021-12-09 19:29:37 -060027using namespace std::literals::string_literals;
Sheldon Baileyea2b22e2022-04-04 12:24:46 -050028using namespace sdbusplus::org::open_power::OCC::Device::Error;
Chris Cain1be43372021-12-09 19:29:37 -060029
Chris Cain78e86012021-03-04 16:15:31 -060030using Mode = sdbusplus::xyz::openbmc_project::Control::Power::server::Mode;
31
Chris Cain6fa848a2022-01-24 14:54:38 -060032// Set the Master OCC
Chris Cain1be43372021-12-09 19:29:37 -060033void PowerMode::setMasterOcc(const std::string& masterOccPath)
Chris Cain6fa848a2022-01-24 14:54:38 -060034{
Chris Cain1be43372021-12-09 19:29:37 -060035 if (masterOccSet)
36 {
37 if (masterOccPath != path)
38 {
39 log<level::ERR>(
40 fmt::format(
41 "PowerMode::setMasterOcc: Master changed (was OCC{}, {})",
42 occInstance, masterOccPath)
43 .c_str());
44 if (occCmd)
45 {
46 occCmd.reset();
47 }
48 }
49 }
50 path = masterOccPath;
Chris Cain6fa848a2022-01-24 14:54:38 -060051 occInstance = path.back() - '0';
52 log<level::DEBUG>(fmt::format("PowerMode::setMasterOcc(OCC{}, {})",
53 occInstance, path.c_str())
54 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -060055 if (!occCmd)
56 {
57 occCmd = std::make_unique<open_power::occ::OccCommand>(occInstance,
58 path.c_str());
59 }
Chris Cain6fa848a2022-01-24 14:54:38 -060060 masterOccSet = true;
61};
62
Chris Cain36f9cde2021-11-22 11:18:21 -060063// Called when DBus power mode gets changed
Chris Cain78e86012021-03-04 16:15:31 -060064void PowerMode::modeChanged(sdbusplus::message::message& msg)
65{
Chris Cain78e86012021-03-04 16:15:31 -060066 std::map<std::string, std::variant<std::string>> properties{};
67 std::string interface;
68 std::string propVal;
69 msg.read(interface, properties);
70 const auto modeEntry = properties.find(POWER_MODE_PROP);
71 if (modeEntry != properties.end())
72 {
73 auto modeEntryValue = modeEntry->second;
74 propVal = std::get<std::string>(modeEntryValue);
Chris Cain6fa848a2022-01-24 14:54:38 -060075 SysPwrMode newMode = convertStringToMode(propVal);
Chris Cain36f9cde2021-11-22 11:18:21 -060076 if (newMode != SysPwrMode::NO_CHANGE)
Chris Cain78e86012021-03-04 16:15:31 -060077 {
Chris Cain1be43372021-12-09 19:29:37 -060078 // Update persisted data with new mode
79 persistedData.updateMode(newMode, 0);
Chris Cain36f9cde2021-11-22 11:18:21 -060080
Chris Cain78e86012021-03-04 16:15:31 -060081 log<level::INFO>(
Chris Cain1be43372021-12-09 19:29:37 -060082 fmt::format("DBus Power Mode Changed: {}", propVal).c_str());
Chris Cain78e86012021-03-04 16:15:31 -060083
Chris Cain36f9cde2021-11-22 11:18:21 -060084 // Send mode change to OCC
85 sendModeChange();
86 }
87 }
88}
89
90// Called from OCC PassThrough interface (via CE login / BMC command line)
Chris Cain1be43372021-12-09 19:29:37 -060091bool PowerMode::setMode(const SysPwrMode newMode, const uint16_t oemModeData)
Chris Cain36f9cde2021-11-22 11:18:21 -060092{
93 if (updateDbusMode(newMode) == false)
94 {
95 // Unsupported mode
96 return false;
97 }
98
Chris Cain1be43372021-12-09 19:29:37 -060099 // Save mode
100 persistedData.updateMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600101
Chris Cain1be43372021-12-09 19:29:37 -0600102 // Send mode change to OCC
103 if (sendModeChange() != CmdStatus::SUCCESS)
104 {
105 // Mode change failed
106 return false;
Chris Cain78e86012021-03-04 16:15:31 -0600107 }
108
Chris Cain36f9cde2021-11-22 11:18:21 -0600109 return true;
Chris Cain78e86012021-03-04 16:15:31 -0600110}
111
112// Convert PowerMode string to OCC SysPwrMode
Chris Cain36f9cde2021-11-22 11:18:21 -0600113// Returns NO_CHANGE if OEM or unsupported mode
Chris Cain78e86012021-03-04 16:15:31 -0600114SysPwrMode convertStringToMode(const std::string& i_modeString)
115{
116 SysPwrMode pmode = SysPwrMode::NO_CHANGE;
117
118 Mode::PowerMode mode = Mode::convertPowerModeFromString(i_modeString);
119 if (mode == Mode::PowerMode::MaximumPerformance)
120 {
121 pmode = SysPwrMode::MAX_PERF;
122 }
123 else if (mode == Mode::PowerMode::PowerSaving)
124 {
125 pmode = SysPwrMode::POWER_SAVING;
126 }
127 else if (mode == Mode::PowerMode::Static)
128 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600129 pmode = SysPwrMode::STATIC;
Chris Cain78e86012021-03-04 16:15:31 -0600130 }
131 else
132 {
Chris Cain36f9cde2021-11-22 11:18:21 -0600133 if (mode != Mode::PowerMode::OEM)
134 {
135 log<level::ERR>(
136 fmt::format(
137 "convertStringToMode: Invalid Power Mode specified: {}",
138 i_modeString)
139 .c_str());
140 }
Chris Cain78e86012021-03-04 16:15:31 -0600141 }
142
143 return pmode;
144}
145
Chris Cain36f9cde2021-11-22 11:18:21 -0600146// Check if Hypervisor target is PowerVM
147bool isPowerVM()
Chris Cain1d51da22021-09-21 14:13:41 -0500148{
Chris Cain36f9cde2021-11-22 11:18:21 -0600149 namespace Hyper = sdbusplus::com::ibm::Host::server;
150 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor";
151 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target";
152 constexpr auto HYPE_PROP = "Target";
153
154 bool powerVmTarget = false;
155
156 // This will throw exception on failure
157 auto& bus = utils::getBus();
158 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE);
159 auto method = bus.new_method_call(service.c_str(), HYPE_PATH,
160 "org.freedesktop.DBus.Properties", "Get");
161 method.append(HYPE_INTERFACE, HYPE_PROP);
162 auto reply = bus.call(method);
163
164 std::variant<std::string> hyperEntryValue;
165 reply.read(hyperEntryValue);
166 auto propVal = std::get<std::string>(hyperEntryValue);
167 if (Hyper::Target::convertHypervisorFromString(propVal) ==
168 Hyper::Target::Hypervisor::PowerVM)
169 {
170 powerVmTarget = true;
171 }
172
173 log<level::DEBUG>(
174 fmt::format("isPowerVM returning {}", powerVmTarget).c_str());
175
176 return powerVmTarget;
177}
178
Chris Cain1be43372021-12-09 19:29:37 -0600179// Initialize persistent data and return true if successful
180bool PowerMode::initPersistentData()
Chris Cain36f9cde2021-11-22 11:18:21 -0600181{
Chris Cain1be43372021-12-09 19:29:37 -0600182 if (!persistedData.modeAvailable())
Chris Cain36f9cde2021-11-22 11:18:21 -0600183 {
Chris Cain1be43372021-12-09 19:29:37 -0600184 // Read the default mode
185 SysPwrMode currentMode;
186 if (!getDefaultMode(currentMode))
187 {
188 // Unable to read defaults
189 return false;
190 }
191 log<level::INFO>(
192 fmt::format("PowerMode::initPersistentData: Using default mode: {}",
193 currentMode)
Chris Cain36f9cde2021-11-22 11:18:21 -0600194 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600195
196 // Save default mode as current mode
197 persistedData.updateMode(currentMode, 0);
198
199 // Write default mode to DBus
200 updateDbusMode(currentMode);
Chris Cain36f9cde2021-11-22 11:18:21 -0600201 }
202
Chris Cain1be43372021-12-09 19:29:37 -0600203 if (!persistedData.ipsAvailable())
204 {
Chris Caincde7bea2022-01-28 15:54:24 -0600205 // Read the default IPS parameters, write persistent file and update
206 // DBus
207 return useDefaultIPSParms();
Chris Cain1be43372021-12-09 19:29:37 -0600208 }
209 return true;
210}
211
212// Get the requested power mode and return true if successful
213bool PowerMode::getMode(SysPwrMode& currentMode, uint16_t& oemModeData)
214{
215 currentMode = SysPwrMode::NO_CHANGE;
216 oemModeData = 0;
217
218 if (!persistedData.getMode(currentMode, oemModeData))
219 {
220 // Persistent data not initialized, read defaults and update DBus
221 if (!initPersistentData())
222 {
223 // Unable to read defaults from entity manager yet
224 return false;
225 }
226 return persistedData.getMode(currentMode, oemModeData);
227 }
228
229 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600230}
231
232// Set the power mode on DBus
233bool PowerMode::updateDbusMode(const SysPwrMode newMode)
234{
Chris Cain36f9cde2021-11-22 11:18:21 -0600235 if (!VALID_POWER_MODE_SETTING(newMode) &&
236 !VALID_OEM_POWER_MODE_SETTING(newMode))
237 {
238 log<level::ERR>(
239 fmt::format(
240 "PowerMode::updateDbusMode - Requested power mode not supported: {}",
241 newMode)
242 .c_str());
243 return false;
244 }
245
Chris Cain1be43372021-12-09 19:29:37 -0600246 // Convert mode for DBus
247 ModeInterface::PowerMode dBusMode;
Chris Cain36f9cde2021-11-22 11:18:21 -0600248 switch (newMode)
249 {
250 case SysPwrMode::STATIC:
Chris Cain1be43372021-12-09 19:29:37 -0600251 dBusMode = Mode::PowerMode::Static;
Chris Cain36f9cde2021-11-22 11:18:21 -0600252 break;
253 case SysPwrMode::POWER_SAVING:
Chris Cain1be43372021-12-09 19:29:37 -0600254 dBusMode = Mode::PowerMode::PowerSaving;
Chris Cain36f9cde2021-11-22 11:18:21 -0600255 break;
256 case SysPwrMode::MAX_PERF:
Chris Cain1be43372021-12-09 19:29:37 -0600257 dBusMode = Mode::PowerMode::MaximumPerformance;
Chris Cain36f9cde2021-11-22 11:18:21 -0600258 break;
259 default:
Chris Cain1be43372021-12-09 19:29:37 -0600260 dBusMode = Mode::PowerMode::OEM;
Chris Cain36f9cde2021-11-22 11:18:21 -0600261 }
262
Chris Cain1be43372021-12-09 19:29:37 -0600263 // true = skip update signal
264 ModeInterface::setPropertyByName(POWER_MODE_PROP, dBusMode, true);
Chris Cain36f9cde2021-11-22 11:18:21 -0600265
266 return true;
267}
268
269// Send mode change request to the master OCC
270CmdStatus PowerMode::sendModeChange()
271{
Chris Cain6fa848a2022-01-24 14:54:38 -0600272 CmdStatus status;
Chris Cain36f9cde2021-11-22 11:18:21 -0600273
Chris Cain6fa848a2022-01-24 14:54:38 -0600274 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600275 {
276 // Nothing to do
Chris Cain6fa848a2022-01-24 14:54:38 -0600277 log<level::DEBUG>("PowerMode::sendModeChange: OCC master not active");
Chris Cain36f9cde2021-11-22 11:18:21 -0600278 return CmdStatus::SUCCESS;
279 }
280
281 if (!isPowerVM())
282 {
283 // Mode change is only supported on PowerVM systems
284 log<level::DEBUG>(
285 "PowerMode::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems");
286 return CmdStatus::SUCCESS;
287 }
288
Chris Cain1be43372021-12-09 19:29:37 -0600289 SysPwrMode newMode;
290 uint16_t oemModeData = 0;
291 getMode(newMode, oemModeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600292
293 if (VALID_POWER_MODE_SETTING(newMode) ||
294 VALID_OEM_POWER_MODE_SETTING(newMode))
295 {
296 std::vector<std::uint8_t> cmd, rsp;
297 cmd.reserve(9);
298 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE));
299 cmd.push_back(0x00); // Data Length (2 bytes)
300 cmd.push_back(0x06);
301 cmd.push_back(0x30); // Data (Version)
302 cmd.push_back(uint8_t(OccState::NO_CHANGE));
303 cmd.push_back(uint8_t(newMode));
Chris Cain1be43372021-12-09 19:29:37 -0600304 cmd.push_back(oemModeData >> 8); // Mode Data (Freq Point)
305 cmd.push_back(oemModeData & 0xFF); //
306 cmd.push_back(0x00); // reserved
Chris Cain36f9cde2021-11-22 11:18:21 -0600307 log<level::INFO>(
308 fmt::format(
309 "PowerMode::sendModeChange: SET_MODE({},{}) command to OCC{} ({} bytes)",
Chris Cain1be43372021-12-09 19:29:37 -0600310 newMode, oemModeData, occInstance, cmd.size())
Chris Cain36f9cde2021-11-22 11:18:21 -0600311 .c_str());
Chris Cain6fa848a2022-01-24 14:54:38 -0600312 status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600313 if (status == CmdStatus::SUCCESS)
314 {
315 if (rsp.size() == 5)
316 {
317 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
318 {
319 log<level::ERR>(
320 fmt::format(
321 "PowerMode::sendModeChange: SET MODE failed with status 0x{:02X}",
322 rsp[2])
323 .c_str());
324 dump_hex(rsp);
325 status = CmdStatus::FAILURE;
326 }
327 }
328 else
329 {
330 log<level::ERR>(
331 "PowerMode::sendModeChange: INVALID SET MODE response");
332 dump_hex(rsp);
333 status = CmdStatus::FAILURE;
334 }
335 }
336 else
337 {
Chris Cainc567dc82022-04-01 15:09:17 -0500338 log<level::ERR>(
339 fmt::format(
340 "PowerMode::sendModeChange: SET_MODE FAILED with status={}",
341 status)
342 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600343 }
344 }
345 else
346 {
347 log<level::ERR>(
348 fmt::format(
349 "PowerMode::sendModeChange: Unable to set power mode to {}",
350 newMode)
351 .c_str());
352 status = CmdStatus::FAILURE;
353 }
354
355 return status;
356}
357
358void PowerMode::ipsChanged(sdbusplus::message::message& msg)
359{
Chris Cain1d51da22021-09-21 14:13:41 -0500360 bool parmsChanged = false;
361 std::string interface;
362 std::map<std::string, std::variant<bool, uint8_t, uint64_t>>
363 ipsProperties{};
364 msg.read(interface, ipsProperties);
365
Chris Cain1be43372021-12-09 19:29:37 -0600366 // Read persisted values
367 bool ipsEnabled;
368 uint8_t enterUtil, exitUtil;
369 uint16_t enterTime, exitTime;
370 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
371
372 // Check for any changed data
Chris Cain1d51da22021-09-21 14:13:41 -0500373 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP);
374 if (ipsEntry != ipsProperties.end())
375 {
Chris Cain1be43372021-12-09 19:29:37 -0600376 ipsEnabled = std::get<bool>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500377 log<level::INFO>(
378 fmt::format("Idle Power Saver change: Enabled={}", ipsEnabled)
379 .c_str());
380 parmsChanged = true;
381 }
382 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL);
383 if (ipsEntry != ipsProperties.end())
384 {
Chris Cain1be43372021-12-09 19:29:37 -0600385 enterUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500386 log<level::INFO>(
387 fmt::format("Idle Power Saver change: Enter Util={}%", enterUtil)
388 .c_str());
389 parmsChanged = true;
390 }
391 ipsEntry = ipsProperties.find(IPS_ENTER_TIME);
392 if (ipsEntry != ipsProperties.end())
393 {
394 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600395 enterTime =
Chris Cain1d51da22021-09-21 14:13:41 -0500396 std::chrono::duration_cast<std::chrono::seconds>(ms).count();
397 log<level::INFO>(
398 fmt::format("Idle Power Saver change: Enter Time={}sec", enterTime)
399 .c_str());
400 parmsChanged = true;
401 }
402 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL);
403 if (ipsEntry != ipsProperties.end())
404 {
Chris Cain1be43372021-12-09 19:29:37 -0600405 exitUtil = std::get<uint8_t>(ipsEntry->second);
Chris Cain1d51da22021-09-21 14:13:41 -0500406 log<level::INFO>(
407 fmt::format("Idle Power Saver change: Exit Util={}%", exitUtil)
408 .c_str());
409 parmsChanged = true;
410 }
411 ipsEntry = ipsProperties.find(IPS_EXIT_TIME);
412 if (ipsEntry != ipsProperties.end())
413 {
414 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second));
Chris Cain1be43372021-12-09 19:29:37 -0600415 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count();
Chris Cain1d51da22021-09-21 14:13:41 -0500416 log<level::INFO>(
417 fmt::format("Idle Power Saver change: Exit Time={}sec", exitTime)
418 .c_str());
419 parmsChanged = true;
420 }
421
422 if (parmsChanged)
423 {
Chris Caincde7bea2022-01-28 15:54:24 -0600424 if (exitUtil == 0)
425 {
426 // Setting the exitUtil to 0 will force restoring the default IPS
427 // parmeters (0 is not valid exit utilization)
428 log<level::INFO>(
429 "Idle Power Saver Exit Utilization is 0%. Restoring default parameters");
430 // Read the default IPS parameters, write persistent file and update
431 // DBus
432 useDefaultIPSParms();
433 }
434 else
435 {
436 // Update persistant data with new DBus values
437 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
438 exitTime);
439 }
Chris Cain1be43372021-12-09 19:29:37 -0600440
441 // Trigger IPS data to get sent to the OCC
Chris Cain36f9cde2021-11-22 11:18:21 -0600442 sendIpsData();
Chris Cain1d51da22021-09-21 14:13:41 -0500443 }
444
445 return;
446}
447
Chris Cain1be43372021-12-09 19:29:37 -0600448/** @brief Get the Idle Power Saver properties from persisted data
449 * @return true if IPS parameters were read
Chris Cain36f9cde2021-11-22 11:18:21 -0600450 */
Chris Cain1be43372021-12-09 19:29:37 -0600451bool PowerMode::getIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
452 uint16_t& enterTime, uint8_t& exitUtil,
453 uint16_t& exitTime)
Chris Cain36f9cde2021-11-22 11:18:21 -0600454{
Chris Cain36f9cde2021-11-22 11:18:21 -0600455 // Defaults:
Chris Cain1be43372021-12-09 19:29:37 -0600456 ipsEnabled = true; // Enabled
457 enterUtil = 8; // Enter Utilization (8%)
458 enterTime = 240; // Enter Delay Time (240s)
459 exitUtil = 12; // Exit Utilization (12%)
460 exitTime = 10; // Exit Delay Time (10s)
Chris Cain36f9cde2021-11-22 11:18:21 -0600461
Chris Cain1be43372021-12-09 19:29:37 -0600462 if (!persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
463 exitTime))
464 {
465 // Persistent data not initialized, read defaults and update DBus
466 if (!initPersistentData())
467 {
468 // Unable to read defaults from entity manager yet
469 return false;
470 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600471
Chris Cain1be43372021-12-09 19:29:37 -0600472 persistedData.getIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
473 exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600474 }
475
476 if (enterUtil > exitUtil)
477 {
478 log<level::ERR>(
479 fmt::format(
480 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
481 enterUtil, exitUtil)
482 .c_str());
483 enterUtil = exitUtil;
484 }
485
Chris Cain1be43372021-12-09 19:29:37 -0600486 return true;
487}
488
489// Set the Idle Power Saver data on DBus
490bool PowerMode::updateDbusIPS(const bool enabled, const uint8_t enterUtil,
491 const uint16_t enterTime, const uint8_t exitUtil,
492 const uint16_t exitTime)
493{
494 // true = skip update signal
495 IpsInterface::setPropertyByName(IPS_ENABLED_PROP, enabled, true);
496 IpsInterface::setPropertyByName(IPS_ENTER_UTIL, enterUtil, true);
497 // Convert time from seconds to ms
498 uint64_t msTime = enterTime * 1000;
499 IpsInterface::setPropertyByName(IPS_ENTER_TIME, msTime, true);
500 IpsInterface::setPropertyByName(IPS_EXIT_UTIL, exitUtil, true);
501 msTime = exitTime * 1000;
502 IpsInterface::setPropertyByName(IPS_EXIT_TIME, msTime, true);
503
504 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600505}
506
507// Send Idle Power Saver config data to the master OCC
508CmdStatus PowerMode::sendIpsData()
509{
Chris Cain6fa848a2022-01-24 14:54:38 -0600510 if (!masterActive || !masterOccSet)
Chris Cain36f9cde2021-11-22 11:18:21 -0600511 {
512 // Nothing to do
513 return CmdStatus::SUCCESS;
514 }
515
516 if (!isPowerVM())
517 {
518 // Idle Power Saver data is only supported on PowerVM systems
519 log<level::DEBUG>(
520 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems");
521 return CmdStatus::SUCCESS;
522 }
523
Chris Cain1be43372021-12-09 19:29:37 -0600524 bool ipsEnabled;
Chris Cain36f9cde2021-11-22 11:18:21 -0600525 uint8_t enterUtil, exitUtil;
526 uint16_t enterTime, exitTime;
Chris Cain1be43372021-12-09 19:29:37 -0600527 getIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
Chris Cain36f9cde2021-11-22 11:18:21 -0600528
529 log<level::INFO>(
530 fmt::format(
531 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
532 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
533 .c_str());
534
535 std::vector<std::uint8_t> cmd, rsp;
536 cmd.reserve(12);
537 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA));
538 cmd.push_back(0x00); // Data Length (2 bytes)
539 cmd.push_back(0x09); //
540 cmd.push_back(0x11); // Config Format: IPS Settings
541 cmd.push_back(0x00); // Version
542 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable
543 cmd.push_back(enterTime >> 8); // Enter Delay Time
544 cmd.push_back(enterTime & 0xFF); //
545 cmd.push_back(enterUtil); // Enter Utilization
546 cmd.push_back(exitTime >> 8); // Exit Delay Time
547 cmd.push_back(exitTime & 0xFF); //
548 cmd.push_back(exitUtil); // Exit Utilization
549 log<level::INFO>(fmt::format("PowerMode::sendIpsData: SET_CFG_DATA[IPS] "
550 "command to OCC{} ({} bytes)",
551 occInstance, cmd.size())
552 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600553 CmdStatus status = occCmd->send(cmd, rsp);
Chris Cain36f9cde2021-11-22 11:18:21 -0600554 if (status == CmdStatus::SUCCESS)
555 {
556 if (rsp.size() == 5)
557 {
558 if (RspStatus::SUCCESS != RspStatus(rsp[2]))
559 {
560 log<level::ERR>(
561 fmt::format(
562 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}",
563 rsp[2])
564 .c_str());
565 dump_hex(rsp);
566 status = CmdStatus::FAILURE;
567 }
568 }
569 else
570 {
571 log<level::ERR>(
572 "PowerMode::sendIpsData: INVALID SET_CFG_DATA[IPS] response");
573 dump_hex(rsp);
574 status = CmdStatus::FAILURE;
575 }
576 }
577 else
578 {
Chris Cainc567dc82022-04-01 15:09:17 -0500579 log<level::ERR>(
580 fmt::format(
581 "PowerMode::sendIpsData: SET_CFG_DATA[IPS] with status={}",
582 status)
583 .c_str());
Chris Cain36f9cde2021-11-22 11:18:21 -0600584 }
585
586 return status;
587}
588
Chris Caincde7bea2022-01-28 15:54:24 -0600589// Print the current values
Chris Cain1be43372021-12-09 19:29:37 -0600590void OccPersistData::print()
Chris Cain36f9cde2021-11-22 11:18:21 -0600591{
Chris Cain1be43372021-12-09 19:29:37 -0600592 if (modeData.modeInitialized)
593 {
594 log<level::INFO>(
595 fmt::format(
596 "OccPersistData: Mode: 0x{:02X}, OEM Mode Data: {} (0x{:04X})",
597 modeData.mode, modeData.oemModeData, modeData.oemModeData)
598 .c_str());
599 }
600 if (modeData.ipsInitialized)
601 {
602 log<level::INFO>(
603 fmt::format(
604 "OccPersistData: IPS enabled:{}, enter:{}%/{}s, exit:{}%/{}s",
605 modeData.ipsEnabled, modeData.ipsEnterUtil,
606 modeData.ipsEnterTime, modeData.ipsExitUtil,
607 modeData.ipsExitTime)
608 .c_str());
609 }
Chris Cain36f9cde2021-11-22 11:18:21 -0600610}
611
612// Saves the OEM mode data in the filesystem using cereal.
613void OccPersistData::save()
614{
615 std::filesystem::path opath =
Chris Cain1be43372021-12-09 19:29:37 -0600616 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600617
618 if (!std::filesystem::exists(opath.parent_path()))
619 {
620 std::filesystem::create_directory(opath.parent_path());
621 }
622
623 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600624 fmt::format(
625 "OccPersistData::save: Writing Power Mode persisted data to {}",
626 opath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600627 .c_str());
Chris Caincde7bea2022-01-28 15:54:24 -0600628 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600629
630 std::ofstream stream{opath.c_str()};
631 cereal::JSONOutputArchive oarchive{stream};
632
Chris Cain1be43372021-12-09 19:29:37 -0600633 oarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600634}
635
636// Loads the OEM mode data in the filesystem using cereal.
637void OccPersistData::load()
638{
639
640 std::filesystem::path ipath =
Chris Cain1be43372021-12-09 19:29:37 -0600641 std::filesystem::path{OCC_CONTROL_PERSIST_PATH} / powerModeFilename;
Chris Cain36f9cde2021-11-22 11:18:21 -0600642
643 if (!std::filesystem::exists(ipath))
644 {
Chris Cain1be43372021-12-09 19:29:37 -0600645 modeData.modeInitialized = false;
646 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600647 return;
648 }
649
650 log<level::DEBUG>(
Chris Cain1be43372021-12-09 19:29:37 -0600651 fmt::format(
652 "OccPersistData::load: Reading Power Mode persisted data from {}",
653 ipath.c_str())
Chris Cain36f9cde2021-11-22 11:18:21 -0600654 .c_str());
655 try
656 {
657 std::ifstream stream{ipath.c_str()};
658 cereal::JSONInputArchive iarchive(stream);
Chris Cain1be43372021-12-09 19:29:37 -0600659 iarchive(modeData);
Chris Cain36f9cde2021-11-22 11:18:21 -0600660 }
661 catch (const std::exception& e)
662 {
663 auto error = errno;
664 log<level::ERR>(
665 fmt::format("OccPersistData::load: failed to read {}, errno={}",
666 ipath.c_str(), error)
667 .c_str());
Chris Cain1be43372021-12-09 19:29:37 -0600668 modeData.modeInitialized = false;
669 modeData.ipsInitialized = false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600670 }
671
Chris Caincde7bea2022-01-28 15:54:24 -0600672 // print();
Chris Cain36f9cde2021-11-22 11:18:21 -0600673}
674
Chris Cain1be43372021-12-09 19:29:37 -0600675// Called when PowerModeProperties defaults are available on DBus
676void PowerMode::defaultsReady(sdbusplus::message::message& msg)
Chris Cain36f9cde2021-11-22 11:18:21 -0600677{
Chris Cain1be43372021-12-09 19:29:37 -0600678 std::map<std::string, std::variant<std::string>> properties{};
679 std::string interface;
680 msg.read(interface, properties);
Chris Cain36f9cde2021-11-22 11:18:21 -0600681
Chris Cain1be43372021-12-09 19:29:37 -0600682 // If persistent data exists, then don't need to read defaults
683 if ((!persistedData.modeAvailable()) || (!persistedData.ipsAvailable()))
Chris Cain36f9cde2021-11-22 11:18:21 -0600684 {
Chris Cain1be43372021-12-09 19:29:37 -0600685 log<level::INFO>(
686 fmt::format(
687 "Default PowerModeProperties are now available (persistent modeAvail={}, ipsAvail={})",
688 persistedData.modeAvailable() ? 'y' : 'n',
689 persistedData.modeAvailable() ? 'y' : 'n')
690 .c_str());
691
692 // Read default power mode defaults and update DBus
693 initPersistentData();
694 }
695}
696
697// Get the default power mode from DBus and return true if success
698bool PowerMode::getDefaultMode(SysPwrMode& defaultMode)
699{
700 try
701 {
702 auto& bus = utils::getBus();
703 std::string path = "/";
704 std::string service =
705 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
706 auto method =
707 bus.new_method_call(service.c_str(), path.c_str(),
708 "org.freedesktop.DBus.Properties", "Get");
709 method.append(PMODE_DEFAULT_INTERFACE, "PowerMode");
710 auto reply = bus.call(method);
711
712 std::variant<std::string> stateEntryValue;
713 reply.read(stateEntryValue);
714 auto propVal = std::get<std::string>(stateEntryValue);
715
716 const std::string fullModeString =
717 PMODE_INTERFACE + ".PowerMode."s + propVal;
718 defaultMode = powermode::convertStringToMode(fullModeString);
719 if (!VALID_POWER_MODE_SETTING(defaultMode))
720 {
721 log<level::ERR>(
722 fmt::format(
723 "PowerMode::getDefaultMode: Invalid default power mode found: {}",
724 defaultMode)
725 .c_str());
726 // If default was read but not valid, use Max Performance
727 defaultMode = SysPwrMode::MAX_PERF;
728 return true;
729 }
730 }
731 catch (const sdbusplus::exception::exception& e)
732 {
733 log<level::ERR>(
734 fmt::format("Unable to read Default Power Mode: {}", e.what())
735 .c_str());
736 return false;
Chris Cain36f9cde2021-11-22 11:18:21 -0600737 }
738
Chris Cain1be43372021-12-09 19:29:37 -0600739 return true;
740}
Chris Cain36f9cde2021-11-22 11:18:21 -0600741
Chris Cain1be43372021-12-09 19:29:37 -0600742/* Get the default Idle Power Saver properties and return true if successful */
743bool PowerMode::getDefaultIPSParms(bool& ipsEnabled, uint8_t& enterUtil,
744 uint16_t& enterTime, uint8_t& exitUtil,
745 uint16_t& exitTime)
746{
747 // Defaults:
748 ipsEnabled = true; // Enabled
749 enterUtil = 8; // Enter Utilization (8%)
750 enterTime = 240; // Enter Delay Time (240s)
751 exitUtil = 12; // Exit Utilization (12%)
752 exitTime = 10; // Exit Delay Time (10s)
753
754 std::map<std::string, std::variant<bool, uint8_t, uint16_t, uint64_t>>
755 ipsProperties{};
756
757 // Get all IPS properties from DBus
758 try
759 {
760 auto& bus = utils::getBus();
761 std::string path = "/";
762 std::string service =
763 utils::getServiceUsingSubTree(PMODE_DEFAULT_INTERFACE, path);
764 auto method =
765 bus.new_method_call(service.c_str(), path.c_str(),
766 "org.freedesktop.DBus.Properties", "GetAll");
767 method.append(PMODE_DEFAULT_INTERFACE);
768 auto reply = bus.call(method);
769 reply.read(ipsProperties);
770 }
771 catch (const sdbusplus::exception::exception& e)
772 {
773 log<level::ERR>(
774 fmt::format(
775 "Unable to read Default Idle Power Saver parameters so it will be disabled: {}",
776 e.what())
777 .c_str());
778 return false;
779 }
780
781 auto ipsEntry = ipsProperties.find("IdlePowerSaverEnabled");
782 if (ipsEntry != ipsProperties.end())
783 {
784 ipsEnabled = std::get<bool>(ipsEntry->second);
785 }
786 else
787 {
788 log<level::ERR>(
789 "PowerMode::getDefaultIPSParms could not find property: IdlePowerSaverEnabled");
790 }
791
792 ipsEntry = ipsProperties.find("EnterUtilizationPercent");
793 if (ipsEntry != ipsProperties.end())
794 {
795 enterUtil = std::get<uint64_t>(ipsEntry->second);
796 }
797 else
798 {
799 log<level::ERR>(
800 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationPercent");
801 }
802
803 ipsEntry = ipsProperties.find("EnterUtilizationDwellTime");
804 if (ipsEntry != ipsProperties.end())
805 {
806 enterTime = std::get<uint64_t>(ipsEntry->second);
807 }
808 else
809 {
810 log<level::ERR>(
811 "PowerMode::getDefaultIPSParms could not find property: EnterUtilizationDwellTime");
812 }
813
814 ipsEntry = ipsProperties.find("ExitUtilizationPercent");
815 if (ipsEntry != ipsProperties.end())
816 {
817 exitUtil = std::get<uint64_t>(ipsEntry->second);
818 }
819 else
820 {
821 log<level::ERR>(
822 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationPercent");
823 }
824
825 ipsEntry = ipsProperties.find("ExitUtilizationDwellTime");
826 if (ipsEntry != ipsProperties.end())
827 {
828 exitTime = std::get<uint64_t>(ipsEntry->second);
829 }
830 else
831 {
832 log<level::ERR>(
833 "PowerMode::getDefaultIPSParms could not find property: ExitUtilizationDwellTime");
834 }
835
836 if (enterUtil > exitUtil)
837 {
838 log<level::ERR>(
839 fmt::format(
840 "ERROR: Default Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both",
841 enterUtil, exitUtil)
842 .c_str());
843 enterUtil = exitUtil;
844 }
845
846 return true;
Chris Cain36f9cde2021-11-22 11:18:21 -0600847}
848
Chris Caincde7bea2022-01-28 15:54:24 -0600849/* Read default IPS parameters, save them to the persistent file and update
850 DBus. Return true if successful */
851bool PowerMode::useDefaultIPSParms()
852{
853 // Read the default IPS parameters
854 bool ipsEnabled;
855 uint8_t enterUtil, exitUtil;
856 uint16_t enterTime, exitTime;
857 if (!getDefaultIPSParms(ipsEnabled, enterUtil, enterTime, exitUtil,
858 exitTime))
859 {
860 // Unable to read defaults
861 return false;
862 }
863 log<level::INFO>(
864 fmt::format(
865 "PowerMode::useDefaultIPSParms: Using default IPS parms: Enabled: {}, EnterUtil: {}%, EnterTime: {}s, ExitUtil: {}%, ExitTime: {}s",
866 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime)
867 .c_str());
868
869 // Save IPS parms to the persistent file
870 persistedData.updateIPS(ipsEnabled, enterUtil, enterTime, exitUtil,
871 exitTime);
872
873 // Write IPS parms to DBus
874 return updateDbusIPS(ipsEnabled, enterUtil, enterTime, exitUtil, exitTime);
875}
876
Sheldon Baileyea2b22e2022-04-04 12:24:46 -0500877#ifdef POWER10
878
879// Starts to watch for IPS active state changes.
880bool PowerMode::openIpsFile()
881{
882 bool rc = true;
883 fd = open(ipsStatusFile.c_str(), O_RDONLY | O_NONBLOCK);
884 const int open_errno = errno;
885 if (fd < 0)
886 {
887 log<level::ERR>(fmt::format("openIpsFile Error({})={} : File={}",
888 open_errno, strerror(open_errno),
889 ipsStatusFile.c_str())
890 .c_str());
891
892 close(fd);
893
894 using namespace sdbusplus::org::open_power::OCC::Device::Error;
895 report<OpenFailure>(
896 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
897 CALLOUT_ERRNO(open_errno),
898 phosphor::logging::org::open_power::OCC::Device::OpenFailure::
899 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
900
901 // We are no longer watching the error
902 active(false);
903
904 watching = false;
905 rc = false;
906 // NOTE: this will leave the system not reporting IPS active state to
907 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
908 }
909 return rc;
910}
911
912// Starts to watch for IPS active state changes.
913void PowerMode::addIpsWatch(bool poll)
914{
915 // open file and register callback on file if we are not currently watching,
916 // and if poll=true, and if we are the master.
917 if ((!watching) && poll)
918 {
919 // Open the file
920 if (openIpsFile())
921 {
922 // register the callback handler which sets 'watching'
923 registerIpsStatusCallBack();
924 }
925 }
926}
927
928// Stops watching for IPS active state changes.
929void PowerMode::removeIpsWatch()
930{
931 // NOTE: we want to remove event, close file, and IPS active false no
932 // matter what the 'watching' flags is set to.
933
934 // We are no longer watching the error
935 active(false);
936
937 watching = false;
938
939 // Close file
940 close(fd);
941
942 // clears sourcePtr in the event source.
943 eventSource.reset();
944}
945
946// Attaches the FD to event loop and registers the callback handler
947void PowerMode::registerIpsStatusCallBack()
948{
949 decltype(eventSource.get()) sourcePtr = nullptr;
950
951 auto r = sd_event_add_io(event.get(), &sourcePtr, fd, EPOLLPRI | EPOLLERR,
952 ipsStatusCallBack, this);
953 if (r < 0)
954 {
955 log<level::ERR>(fmt::format("sd_event_add_io: Error({})={} : File={}",
956 r, strerror(-r), ipsStatusFile.c_str())
957 .c_str());
958
959 using InternalFailure =
960 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
961 report<InternalFailure>();
962
963 removeIpsWatch();
964 // NOTE: this will leave the system not reporting IPS active state to
965 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
966 }
967 else
968 {
969 // puts sourcePtr in the event source.
970 eventSource.reset(sourcePtr);
971 // Set we are watching the error
972 watching = true;
973 }
974}
975
976// Static function to redirect to non static analyze event function to be
977// able to read file and push onto dBus.
978int PowerMode::ipsStatusCallBack(sd_event_source* /*es*/, int /*fd*/,
979 uint32_t /*revents*/, void* userData)
980{
981 auto pmode = static_cast<PowerMode*>(userData);
982 pmode->analyzeIpsEvent();
983 return 0;
984}
985
986// Function to Read SysFs file change on IPS state and push on dBus.
987void PowerMode::analyzeIpsEvent()
988{
989 // Need to seek to START, else the poll returns immediately telling
990 // there is data to be read. if not done this floods the system.
991 auto r = lseek(fd, 0, SEEK_SET);
992 const int open_errno = errno;
993 if (r < 0)
994 {
995 // NOTE: upon file access error we can not just re-open file, we have to
996 // remove and add to watch.
997 removeIpsWatch();
998 addIpsWatch(true);
999 }
1000
1001 // if we are 'watching' that is the file seek, or the re-open passed.. we
1002 // can read the data
1003 if (watching)
1004 {
1005 // This file gets created when polling OCCs. A value or length of 0 is
1006 // deemed success. That means we would disable IPS active on dbus.
1007 char data;
1008 bool ipsState = false;
1009 const auto len = read(fd, &data, sizeof(data));
1010 const int readErrno = errno;
1011 if (len <= 0)
1012 {
1013 removeIpsWatch();
1014
1015 log<level::ERR>(
1016 fmt::format("IPS state Read Error({})={} : File={} : len={}",
1017 readErrno, strerror(readErrno),
1018 ipsStatusFile.c_str(), len)
1019 .c_str());
1020
1021 report<ReadFailure>(
1022 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1023 CALLOUT_ERRNO(readErrno),
1024 phosphor::logging::org::open_power::OCC::Device::ReadFailure::
1025 CALLOUT_DEVICE_PATH(ipsStatusFile.c_str()));
1026
1027 // NOTE: this will leave the system not reporting IPS active state
1028 // to Fan Controls, Until an APP reload, or IPL and we will attempt
1029 // again.
1030 }
1031 else
1032 {
1033 // Data returned in ASCII.
1034 // convert to integer. atoi()
1035 // from OCC_P10_FW_Interfaces spec
1036 // Bit 6: IPS active 1 indicates enabled.
1037 // Bit 7: IPS enabled. 1 indicates enabled.
1038 // mask off bit 6 --> & 0x02
1039 // Shift left one bit and store as bool. >> 1
1040 ipsState = static_cast<bool>(((atoi(&data)) & 0x2) >> 1);
1041 }
1042
1043 // This will only set IPS active dbus if different than current.
1044 active(ipsState);
1045 }
1046 else
1047 {
1048 removeIpsWatch();
1049
1050 // If the Retry did not get to "watching = true" we already have an
1051 // error log, just post trace.
1052 log<level::ERR>(fmt::format("Retry on File seek Error({})={} : File={}",
1053 open_errno, strerror(open_errno),
1054 ipsStatusFile.c_str())
1055 .c_str());
1056
1057 // NOTE: this will leave the system not reporting IPS active state to
1058 // Fan Controls, Until an APP reload, or IPL and we will attempt again.
1059 }
1060
1061 return;
1062}
1063#endif
1064
Chris Cain78e86012021-03-04 16:15:31 -06001065} // namespace powermode
1066
1067} // namespace occ
1068
1069} // namespace open_power