blob: 9b102ab324a26b1b3ac2d674947238352b144764 [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>
Chris Caine2d0a432022-03-28 11:08:49 -05009#include <filesystem>
Andrew Geissler32016d12017-06-20 15:46:52 -050010
11namespace open_power
12{
13namespace occ
14{
15namespace powercap
16{
17
Gunnar Mills94df8c92018-09-14 14:50:03 -050018constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
Andrew Geissler52cf26a2017-07-06 12:56:32 -050019constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
20
Andrew Geissler52cf26a2017-07-06 12:56:32 -050021constexpr auto POWER_CAP_PROP = "PowerCap";
22constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
Chris Cain613dc902022-04-08 09:56:22 -050023constexpr auto POWER_CAP_SOFT_MIN = "MinSoftPowerCapValue";
Chris Cain5d66a0a2022-02-09 08:52:10 -060024constexpr auto POWER_CAP_HARD_MIN = "MinPowerCapValue";
25constexpr auto POWER_CAP_MAX = "MaxPowerCapValue";
Andrew Geissler52cf26a2017-07-06 12:56:32 -050026
Andrew Geissler32016d12017-06-20 15:46:52 -050027using namespace phosphor::logging;
George Liubcef3b42021-09-10 12:39:02 +080028namespace fs = std::filesystem;
Andrew Geissler32016d12017-06-20 15:46:52 -050029
Chris Cain5d66a0a2022-02-09 08:52:10 -060030void PowerCap::updatePcapBounds()
31{
32 // Build the hwmon string to write the power cap bounds
33 fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"});
Chris Cain613dc902022-04-08 09:56:22 -050034 fs::path softMinName =
35 getPcapFilename(std::regex{"power\\d+_cap_min_soft$"});
Chris Cain5d66a0a2022-02-09 08:52:10 -060036 fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"});
37
38 // Read the power cap bounds from sysfs files
39 uint64_t cap;
Chris Cain613dc902022-04-08 09:56:22 -050040 uint32_t capSoftMin = 0, capHardMin = 0, capMax = INT_MAX;
41
42 std::ifstream softMinFile(softMinName, std::ios::in);
43 if (softMinFile)
44 {
45 softMinFile >> cap;
46 softMinFile.close();
47 // Convert to Input Power in Watts (round up)
48 capSoftMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9);
49 }
50 else
51 {
52 log<level::ERR>(
53 fmt::format(
54 "updatePcapBounds: unable to find pcap_min_soft file: {} (errno={})",
55 pcapBasePathname.c_str(), errno)
56 .c_str());
57 }
58
Chris Cain5d66a0a2022-02-09 08:52:10 -060059 std::ifstream minFile(minName, std::ios::in);
60 if (minFile)
61 {
62 minFile >> cap;
63 minFile.close();
Chris Cain8676e032022-03-24 15:06:23 -050064 // Convert to Input Power in Watts (round up)
65 capHardMin = ((cap / (PS_DERATING_FACTOR / 100.0) / 1000000) + 0.9);
Chris Cain5d66a0a2022-02-09 08:52:10 -060066 }
67 else
68 {
69 log<level::ERR>(
70 fmt::format(
71 "updatePcapBounds: unable to find cap_min file: {} (errno={})",
72 pcapBasePathname.c_str(), errno)
73 .c_str());
74 }
Chris Cain613dc902022-04-08 09:56:22 -050075
Chris Cain5d66a0a2022-02-09 08:52:10 -060076 std::ifstream maxFile(maxName, std::ios::in);
77 if (maxFile)
78 {
79 maxFile >> cap;
80 maxFile.close();
Chris Cain8676e032022-03-24 15:06:23 -050081 // Convert to Input Power in Watts (truncate remainder)
82 capMax = cap / (PS_DERATING_FACTOR / 100.0) / 1000000;
Chris Cain5d66a0a2022-02-09 08:52:10 -060083 }
84 else
85 {
86 log<level::ERR>(
87 fmt::format(
88 "updatePcapBounds: unable to find cap_max file: {} (errno={})",
89 pcapBasePathname.c_str(), errno)
90 .c_str());
91 }
92
93 // Save the bounds to dbus
Chris Cain613dc902022-04-08 09:56:22 -050094 updateDbusPcap(capSoftMin, capHardMin, capMax);
Chris Cain40501a22022-03-14 17:33:27 -050095
96 // Validate dbus and hwmon user caps match
97 const uint32_t dbusUserCap = getPcap();
98 const uint32_t hwmonUserCap = readUserCapHwmon();
99 if ((hwmonUserCap != 0) && (dbusUserCap != hwmonUserCap))
100 {
101 // User power cap is enabled, but does not match dbus
102 log<level::ERR>(
103 fmt::format(
104 "updatePcapBounds: user powercap mismatch (hwmon:{}W, bdus:{}W) - using dbus",
105 hwmonUserCap, dbusUserCap)
106 .c_str());
107 writeOcc(dbusUserCap);
108 }
Chris Cain5d66a0a2022-02-09 08:52:10 -0600109}
110
Andrew Geissler4cea4d22017-07-10 15:13:33 -0500111uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled)
112{
113 if (!pcapEnabled)
114 {
115 // Pcap disabled, return 0 to indicate disabled
116 return 0;
117 }
118
119 // If pcap is not disabled then just return the pcap with the derating
120 // factor applied.
Gunnar Mills94df8c92018-09-14 14:50:03 -0500121 return ((static_cast<uint64_t>(pcap) * PS_DERATING_FACTOR) / 100);
Andrew Geissler4cea4d22017-07-10 15:13:33 -0500122}
123
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500124uint32_t PowerCap::getPcap()
125{
George Liuf3b75142021-06-10 11:22:50 +0800126 utils::PropertyValue pcap{};
127 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500128 {
George Liuf3b75142021-06-10 11:22:50 +0800129 pcap = utils::getProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_PROP);
130
131 return std::get<uint32_t>(pcap);
132 }
Patrick Williams25613622021-09-02 09:29:54 -0500133 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800134 {
135 log<level::ERR>("Failed to get PowerCap property",
136 entry("ERROR=%s", e.what()),
137 entry("PATH=%s", PCAP_PATH));
138
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500139 return 0;
140 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500141}
142
143bool PowerCap::getPcapEnabled()
144{
George Liuf3b75142021-06-10 11:22:50 +0800145 utils::PropertyValue pcapEnabled{};
146 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500147 {
George Liuf3b75142021-06-10 11:22:50 +0800148 pcapEnabled = utils::getProperty(PCAP_PATH, PCAP_INTERFACE,
149 POWER_CAP_ENABLE_PROP);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500150
George Liuf3b75142021-06-10 11:22:50 +0800151 return std::get<bool>(pcapEnabled);
152 }
Patrick Williams25613622021-09-02 09:29:54 -0500153 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800154 {
155 log<level::ERR>("Failed to get PowerCapEnable property",
156 entry("ERROR=%s", e.what()),
157 entry("PATH=%s", PCAP_PATH));
158
159 return false;
160 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500161}
Andrew Geissler32016d12017-06-20 15:46:52 -0500162
Chris Cain5d66a0a2022-02-09 08:52:10 -0600163fs::path PowerCap::getPcapFilename(const std::regex& expr)
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500164{
Chris Cain5d66a0a2022-02-09 08:52:10 -0600165 if (pcapBasePathname.empty())
166 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600167 pcapBasePathname = occStatus.getHwmonPath();
Chris Cain5d66a0a2022-02-09 08:52:10 -0600168 }
Chris Caine2d0a432022-03-28 11:08:49 -0500169
170 if (fs::exists(pcapBasePathname))
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500171 {
Chris Caine2d0a432022-03-28 11:08:49 -0500172 // Search for pcap file based on the supplied expr
173 for (auto& file : fs::directory_iterator(pcapBasePathname))
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500174 {
Chris Caine2d0a432022-03-28 11:08:49 -0500175 if (std::regex_search(file.path().string(), expr))
176 {
177 // Found match
178 return file;
179 }
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500180 }
181 }
Chris Caine2d0a432022-03-28 11:08:49 -0500182 else
183 {
184 log<level::ERR>(fmt::format("Power Cap base filename not found: {}",
185 pcapBasePathname.c_str())
186 .c_str());
187 }
188
Chris Cain5d66a0a2022-02-09 08:52:10 -0600189 // return empty path
190 return fs::path{};
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500191}
192
Chris Cain40501a22022-03-14 17:33:27 -0500193// Write the user power cap to sysfs.
194// This will trigger the driver to send the cap to the OCC
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500195void PowerCap::writeOcc(uint32_t pcapValue)
196{
Chris Caind4c19a02022-04-22 15:46:13 -0500197 if (!occStatus.occActive())
198 {
199 // OCC not running, skip update
200 return;
201 }
202
Chris Cain5d66a0a2022-02-09 08:52:10 -0600203 // Build the hwmon string to write the user power cap
204 fs::path fileName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
205 if (fileName.empty())
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500206 {
Chris Cainb6535e82022-01-21 08:24:13 -0600207 log<level::ERR>(
208 fmt::format("Could not find a power cap file to write to: {})",
Chris Cain5d66a0a2022-02-09 08:52:10 -0600209 pcapBasePathname.c_str())
Chris Cainb6535e82022-01-21 08:24:13 -0600210 .c_str());
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500211 return;
212 }
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500213
214 uint64_t microWatts = pcapValue * 1000000ull;
215
216 auto pcapString{std::to_string(microWatts)};
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500217
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500218 // Open the hwmon file and write the power cap
Chris Cain5d66a0a2022-02-09 08:52:10 -0600219 std::ofstream file(fileName, std::ios::out);
220 if (file)
221 {
222 log<level::INFO>(fmt::format("Writing {}uW to {}", pcapString.c_str(),
223 fileName.c_str())
224 .c_str());
225 file << pcapString;
226 file.close();
227 }
228 else
229 {
230 log<level::ERR>(fmt::format("Failed writing {}uW to {} (errno={})",
231 microWatts, fileName.c_str(), errno)
232 .c_str());
233 }
234
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500235 return;
236}
237
Chris Cain40501a22022-03-14 17:33:27 -0500238// Read the current user power cap from sysfs.
239uint32_t PowerCap::readUserCapHwmon()
240{
241 uint32_t userCap = 0;
242
243 // Get the sysfs filename for the user power cap
244 fs::path userCapName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
245 if (userCapName.empty())
246 {
247 log<level::ERR>(
248 fmt::format(
249 "readUserCapHwmon: Could not find a power cap file to read: {})",
250 pcapBasePathname.c_str())
251 .c_str());
252 return 0;
253 }
254
255 // Open the sysfs file and read the power cap
Chris Cain40501a22022-03-14 17:33:27 -0500256 std::ifstream file(userCapName, std::ios::in);
257 if (file)
258 {
Chris Caine2d0a432022-03-28 11:08:49 -0500259 uint64_t cap;
Chris Cain40501a22022-03-14 17:33:27 -0500260 file >> cap;
261 file.close();
262 // Convert to Watts
263 userCap = cap / 1000000;
264 }
265 else
266 {
267 log<level::ERR>(
268 fmt::format("readUserCapHwmon: Failed reading {} (errno={})",
269 userCapName.c_str(), errno)
270 .c_str());
271 }
272
273 return userCap;
274}
275
Andrew Geissler32016d12017-06-20 15:46:52 -0500276void PowerCap::pcapChanged(sdbusplus::message::message& msg)
277{
Andrew Geissler32016d12017-06-20 15:46:52 -0500278 if (!occStatus.occActive())
279 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600280 // Nothing to do
Andrew Geissler32016d12017-06-20 15:46:52 -0500281 return;
282 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500283
284 uint32_t pcap = 0;
285 bool pcapEnabled = false;
286
287 std::string msgSensor;
Patrick Williamse0962702020-05-13 17:50:22 -0500288 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500289 msg.read(msgSensor, msgData);
290
Chris Cain5d66a0a2022-02-09 08:52:10 -0600291 bool changeFound = false;
292 for (const auto& [prop, value] : msgData)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500293 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600294 if (prop == POWER_CAP_PROP)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500295 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600296 pcap = std::get<uint32_t>(value);
297 pcapEnabled = getPcapEnabled();
298 changeFound = true;
299 }
300 else if (prop == POWER_CAP_ENABLE_PROP)
301 {
302 pcapEnabled = std::get<bool>(value);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500303 pcap = getPcap();
Chris Cain5d66a0a2022-02-09 08:52:10 -0600304 changeFound = true;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500305 }
306 else
307 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600308 // Ignore other properties
309 log<level::DEBUG>(
310 fmt::format(
311 "pcapChanged: Unknown power cap property changed {} to {}",
312 prop.c_str(), std::get<uint32_t>(value))
313 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500314 }
315 }
316
Chris Cain5d66a0a2022-02-09 08:52:10 -0600317 if (changeFound)
318 {
319 log<level::INFO>(
320 fmt::format(
321 "Power Cap Property Change (cap={}W (input), enabled={})", pcap,
322 pcapEnabled ? 'y' : 'n')
323 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500324
Chris Cain5d66a0a2022-02-09 08:52:10 -0600325 // Determine desired action to write to occ
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500326
Chris Cain5d66a0a2022-02-09 08:52:10 -0600327 auto occInput = getOccInput(pcap, pcapEnabled);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500328
Chris Cain5d66a0a2022-02-09 08:52:10 -0600329 // Write action to occ
330 writeOcc(occInput);
331 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500332
333 return;
Andrew Geissler32016d12017-06-20 15:46:52 -0500334}
335
Chris Cain5d66a0a2022-02-09 08:52:10 -0600336// Update the Power Cap bounds on DBus
Chris Cain613dc902022-04-08 09:56:22 -0500337bool PowerCap::updateDbusPcap(uint32_t softMin, uint32_t hardMin, uint32_t max)
Chris Cain5d66a0a2022-02-09 08:52:10 -0600338{
339 bool complete = true;
340
341 try
342 {
Chris Cain613dc902022-04-08 09:56:22 -0500343 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_SOFT_MIN,
344 softMin);
345 }
346 catch (const sdbusplus::exception::exception& e)
347 {
348 log<level::ERR>(
349 fmt::format(
350 "updateDbusPcap: Failed to set SOFT PCAP to {}W due to {}",
351 softMin, e.what())
352 .c_str());
353 complete = false;
354 }
355
356 try
357 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600358 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_HARD_MIN,
359 hardMin);
360 }
361 catch (const sdbusplus::exception::exception& e)
362 {
363 log<level::ERR>(
364 fmt::format(
365 "updateDbusPcap: Failed to set HARD PCAP to {}W due to {}",
366 hardMin, e.what())
367 .c_str());
368 complete = false;
369 }
370
371 try
372 {
373 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_MAX, max);
374 }
375 catch (const sdbusplus::exception::exception& e)
376 {
377 log<level::ERR>(
378 fmt::format(
379 "updateDbusPcap: Failed to set MAX PCAP to {}W due to {}", max,
380 e.what())
381 .c_str());
382 complete = false;
383 }
384
385 return complete;
386}
Gunnar Mills94df8c92018-09-14 14:50:03 -0500387} // namespace powercap
Andrew Geissler32016d12017-06-20 15:46:52 -0500388
389} // namespace occ
390
Gunnar Mills94df8c92018-09-14 14:50:03 -0500391} // namespace open_power