blob: cd1105984eab5acb7e31a57e72324e45b5be71af [file] [log] [blame]
Chris Cain5d66a0a2022-02-09 08:52:10 -06001#include "occ_status.hpp"
2
Chris Cainb6535e82022-01-21 08:24:13 -06003#include <fmt/core.h>
4
Andrew Geissler32016d12017-06-20 15:46:52 -05005#include <phosphor-logging/log.hpp>
Gunnar Mills94df8c92018-09-14 14:50:03 -05006#include <powercap.hpp>
George Liub5ca1012021-09-10 12:53:11 +08007
8#include <cassert>
Andrew Geissler32016d12017-06-20 15:46:52 -05009
10namespace open_power
11{
12namespace occ
13{
14namespace powercap
15{
16
Gunnar Mills94df8c92018-09-14 14:50:03 -050017constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
Andrew Geissler52cf26a2017-07-06 12:56:32 -050018constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
19
Andrew Geissler52cf26a2017-07-06 12:56:32 -050020constexpr auto POWER_CAP_PROP = "PowerCap";
21constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
Chris Cain5d66a0a2022-02-09 08:52:10 -060022constexpr auto POWER_CAP_SOFT_MIN = "MinSoftPowerCapValue";
23constexpr auto POWER_CAP_HARD_MIN = "MinPowerCapValue";
24constexpr auto POWER_CAP_MAX = "MaxPowerCapValue";
Andrew Geissler52cf26a2017-07-06 12:56:32 -050025
Andrew Geissler32016d12017-06-20 15:46:52 -050026using namespace phosphor::logging;
George Liubcef3b42021-09-10 12:39:02 +080027namespace fs = std::filesystem;
Andrew Geissler32016d12017-06-20 15:46:52 -050028
Chris Cain5d66a0a2022-02-09 08:52:10 -060029void PowerCap::updatePcapBounds()
30{
31 // Build the hwmon string to write the power cap bounds
32 fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"});
33 fs::path softMinName =
34 getPcapFilename(std::regex{"power\\d+_cap_min_soft$"});
35 fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"});
36
37 // Read the power cap bounds from sysfs files
38 uint64_t cap;
39 uint32_t capSoftMin = 0, capHardMin = 0, capMax = INT_MAX;
40 std::ifstream softMinFile(softMinName, std::ios::in);
41 if (softMinFile)
42 {
43 softMinFile >> cap;
44 softMinFile.close();
45 // Convert to Watts
46 capSoftMin = cap / 1000000;
47 }
48 else
49 {
50 log<level::ERR>(
51 fmt::format(
52 "updatePcapBounds: unable to find pcap_min_soft file: {} (errno={})",
53 pcapBasePathname.c_str(), errno)
54 .c_str());
55 }
56 std::ifstream minFile(minName, std::ios::in);
57 if (minFile)
58 {
59 minFile >> cap;
60 minFile.close();
61 // Convert to Watts
62 capHardMin = cap / 1000000;
63 }
64 else
65 {
66 log<level::ERR>(
67 fmt::format(
68 "updatePcapBounds: unable to find cap_min file: {} (errno={})",
69 pcapBasePathname.c_str(), errno)
70 .c_str());
71 }
72 std::ifstream maxFile(maxName, std::ios::in);
73 if (maxFile)
74 {
75 maxFile >> cap;
76 maxFile.close();
77 // Convert to Watts
78 capMax = cap / 1000000;
79 }
80 else
81 {
82 log<level::ERR>(
83 fmt::format(
84 "updatePcapBounds: unable to find cap_max file: {} (errno={})",
85 pcapBasePathname.c_str(), errno)
86 .c_str());
87 }
88
89 // Save the bounds to dbus
90 updateDbusPcap(capSoftMin, capHardMin, capMax);
91}
92
Andrew Geissler4cea4d22017-07-10 15:13:33 -050093uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled)
94{
95 if (!pcapEnabled)
96 {
97 // Pcap disabled, return 0 to indicate disabled
98 return 0;
99 }
100
101 // If pcap is not disabled then just return the pcap with the derating
102 // factor applied.
Gunnar Mills94df8c92018-09-14 14:50:03 -0500103 return ((static_cast<uint64_t>(pcap) * PS_DERATING_FACTOR) / 100);
Andrew Geissler4cea4d22017-07-10 15:13:33 -0500104}
105
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500106uint32_t PowerCap::getPcap()
107{
George Liuf3b75142021-06-10 11:22:50 +0800108 utils::PropertyValue pcap{};
109 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500110 {
George Liuf3b75142021-06-10 11:22:50 +0800111 pcap = utils::getProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_PROP);
112
113 return std::get<uint32_t>(pcap);
114 }
Patrick Williams25613622021-09-02 09:29:54 -0500115 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800116 {
117 log<level::ERR>("Failed to get PowerCap property",
118 entry("ERROR=%s", e.what()),
119 entry("PATH=%s", PCAP_PATH));
120
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500121 return 0;
122 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500123}
124
125bool PowerCap::getPcapEnabled()
126{
George Liuf3b75142021-06-10 11:22:50 +0800127 utils::PropertyValue pcapEnabled{};
128 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500129 {
George Liuf3b75142021-06-10 11:22:50 +0800130 pcapEnabled = utils::getProperty(PCAP_PATH, PCAP_INTERFACE,
131 POWER_CAP_ENABLE_PROP);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500132
George Liuf3b75142021-06-10 11:22:50 +0800133 return std::get<bool>(pcapEnabled);
134 }
Patrick Williams25613622021-09-02 09:29:54 -0500135 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800136 {
137 log<level::ERR>("Failed to get PowerCapEnable property",
138 entry("ERROR=%s", e.what()),
139 entry("PATH=%s", PCAP_PATH));
140
141 return false;
142 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500143}
Andrew Geissler32016d12017-06-20 15:46:52 -0500144
Chris Cain5d66a0a2022-02-09 08:52:10 -0600145fs::path PowerCap::getPcapFilename(const std::regex& expr)
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500146{
Chris Cain5d66a0a2022-02-09 08:52:10 -0600147 if (pcapBasePathname.empty())
148 {
149 static bool tracedError = false;
150 pcapBasePathname = occStatus.getHwmonPath();
151 if (!fs::exists(pcapBasePathname) && !tracedError)
152 {
153 log<level::ERR>(fmt::format("Power Cap base filename not found: {}",
154 pcapBasePathname.c_str())
155 .c_str());
156 tracedError = true;
157 }
158 }
159 for (auto& file : fs::directory_iterator(pcapBasePathname))
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500160 {
161 if (std::regex_search(file.path().string(), expr))
162 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600163 return file;
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500164 }
165 }
Chris Cain5d66a0a2022-02-09 08:52:10 -0600166 // return empty path
167 return fs::path{};
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500168}
169
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500170void PowerCap::writeOcc(uint32_t pcapValue)
171{
Chris Cain5d66a0a2022-02-09 08:52:10 -0600172 // Build the hwmon string to write the user power cap
173 fs::path fileName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
174 if (fileName.empty())
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500175 {
Chris Cainb6535e82022-01-21 08:24:13 -0600176 log<level::ERR>(
177 fmt::format("Could not find a power cap file to write to: {})",
Chris Cain5d66a0a2022-02-09 08:52:10 -0600178 pcapBasePathname.c_str())
Chris Cainb6535e82022-01-21 08:24:13 -0600179 .c_str());
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500180 return;
181 }
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500182
183 uint64_t microWatts = pcapValue * 1000000ull;
184
185 auto pcapString{std::to_string(microWatts)};
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500186
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500187 // Open the hwmon file and write the power cap
Chris Cain5d66a0a2022-02-09 08:52:10 -0600188 std::ofstream file(fileName, std::ios::out);
189 if (file)
190 {
191 log<level::INFO>(fmt::format("Writing {}uW to {}", pcapString.c_str(),
192 fileName.c_str())
193 .c_str());
194 file << pcapString;
195 file.close();
196 }
197 else
198 {
199 log<level::ERR>(fmt::format("Failed writing {}uW to {} (errno={})",
200 microWatts, fileName.c_str(), errno)
201 .c_str());
202 }
203
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500204 return;
205}
206
Andrew Geissler32016d12017-06-20 15:46:52 -0500207void PowerCap::pcapChanged(sdbusplus::message::message& msg)
208{
Andrew Geissler32016d12017-06-20 15:46:52 -0500209 if (!occStatus.occActive())
210 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600211 // Nothing to do
Andrew Geissler32016d12017-06-20 15:46:52 -0500212 return;
213 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500214
215 uint32_t pcap = 0;
216 bool pcapEnabled = false;
217
218 std::string msgSensor;
Patrick Williamse0962702020-05-13 17:50:22 -0500219 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500220 msg.read(msgSensor, msgData);
221
Chris Cain5d66a0a2022-02-09 08:52:10 -0600222 bool changeFound = false;
223 for (const auto& [prop, value] : msgData)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500224 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600225 if (prop == POWER_CAP_PROP)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500226 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600227 pcap = std::get<uint32_t>(value);
228 pcapEnabled = getPcapEnabled();
229 changeFound = true;
230 }
231 else if (prop == POWER_CAP_ENABLE_PROP)
232 {
233 pcapEnabled = std::get<bool>(value);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500234 pcap = getPcap();
Chris Cain5d66a0a2022-02-09 08:52:10 -0600235 changeFound = true;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500236 }
237 else
238 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600239 // Ignore other properties
240 log<level::DEBUG>(
241 fmt::format(
242 "pcapChanged: Unknown power cap property changed {} to {}",
243 prop.c_str(), std::get<uint32_t>(value))
244 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500245 }
246 }
247
Chris Cain5d66a0a2022-02-09 08:52:10 -0600248 if (changeFound)
249 {
250 log<level::INFO>(
251 fmt::format(
252 "Power Cap Property Change (cap={}W (input), enabled={})", pcap,
253 pcapEnabled ? 'y' : 'n')
254 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500255
Chris Cain5d66a0a2022-02-09 08:52:10 -0600256 // Determine desired action to write to occ
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500257
Chris Cain5d66a0a2022-02-09 08:52:10 -0600258 auto occInput = getOccInput(pcap, pcapEnabled);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500259
Chris Cain5d66a0a2022-02-09 08:52:10 -0600260 // Write action to occ
261 writeOcc(occInput);
262 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500263
264 return;
Andrew Geissler32016d12017-06-20 15:46:52 -0500265}
266
Chris Cain5d66a0a2022-02-09 08:52:10 -0600267// Update the Power Cap bounds on DBus
268bool PowerCap::updateDbusPcap(uint32_t softMin, uint32_t hardMin, uint32_t max)
269{
270 bool complete = true;
271
272 try
273 {
274 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_SOFT_MIN,
275 softMin);
276 }
277 catch (const sdbusplus::exception::exception& e)
278 {
279 log<level::ERR>(
280 fmt::format(
281 "updateDbusPcap: Failed to set SOFT PCAP to {}W due to {}",
282 softMin, e.what())
283 .c_str());
284 complete = false;
285 }
286
287 try
288 {
289 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_HARD_MIN,
290 hardMin);
291 }
292 catch (const sdbusplus::exception::exception& e)
293 {
294 log<level::ERR>(
295 fmt::format(
296 "updateDbusPcap: Failed to set HARD PCAP to {}W due to {}",
297 hardMin, e.what())
298 .c_str());
299 complete = false;
300 }
301
302 try
303 {
304 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_MAX, max);
305 }
306 catch (const sdbusplus::exception::exception& e)
307 {
308 log<level::ERR>(
309 fmt::format(
310 "updateDbusPcap: Failed to set MAX PCAP to {}W due to {}", max,
311 e.what())
312 .c_str());
313 complete = false;
314 }
315
316 return complete;
317}
Gunnar Mills94df8c92018-09-14 14:50:03 -0500318} // namespace powercap
Andrew Geissler32016d12017-06-20 15:46:52 -0500319
320} // namespace occ
321
Gunnar Mills94df8c92018-09-14 14:50:03 -0500322} // namespace open_power