blob: c95c27924bd0fdff4bac866d7296623b5c1fad65 [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_HARD_MIN = "MinPowerCapValue";
23constexpr auto POWER_CAP_MAX = "MaxPowerCapValue";
Andrew Geissler52cf26a2017-07-06 12:56:32 -050024
Andrew Geissler32016d12017-06-20 15:46:52 -050025using namespace phosphor::logging;
George Liubcef3b42021-09-10 12:39:02 +080026namespace fs = std::filesystem;
Andrew Geissler32016d12017-06-20 15:46:52 -050027
Chris Cain5d66a0a2022-02-09 08:52:10 -060028void PowerCap::updatePcapBounds()
29{
30 // Build the hwmon string to write the power cap bounds
31 fs::path minName = getPcapFilename(std::regex{"power\\d+_cap_min$"});
Chris Cain5d66a0a2022-02-09 08:52:10 -060032 fs::path maxName = getPcapFilename(std::regex{"power\\d+_cap_max$"});
33
34 // Read the power cap bounds from sysfs files
35 uint64_t cap;
Chris Cain40501a22022-03-14 17:33:27 -050036 uint32_t capHardMin = 0, capMax = INT_MAX;
Chris Cain5d66a0a2022-02-09 08:52:10 -060037 std::ifstream minFile(minName, std::ios::in);
38 if (minFile)
39 {
40 minFile >> cap;
41 minFile.close();
42 // Convert to Watts
43 capHardMin = cap / 1000000;
44 }
45 else
46 {
47 log<level::ERR>(
48 fmt::format(
49 "updatePcapBounds: unable to find cap_min file: {} (errno={})",
50 pcapBasePathname.c_str(), errno)
51 .c_str());
52 }
53 std::ifstream maxFile(maxName, std::ios::in);
54 if (maxFile)
55 {
56 maxFile >> cap;
57 maxFile.close();
58 // Convert to Watts
59 capMax = cap / 1000000;
60 }
61 else
62 {
63 log<level::ERR>(
64 fmt::format(
65 "updatePcapBounds: unable to find cap_max file: {} (errno={})",
66 pcapBasePathname.c_str(), errno)
67 .c_str());
68 }
69
70 // Save the bounds to dbus
Chris Cain40501a22022-03-14 17:33:27 -050071 updateDbusPcap(capHardMin, capMax);
72
73 // Validate dbus and hwmon user caps match
74 const uint32_t dbusUserCap = getPcap();
75 const uint32_t hwmonUserCap = readUserCapHwmon();
76 if ((hwmonUserCap != 0) && (dbusUserCap != hwmonUserCap))
77 {
78 // User power cap is enabled, but does not match dbus
79 log<level::ERR>(
80 fmt::format(
81 "updatePcapBounds: user powercap mismatch (hwmon:{}W, bdus:{}W) - using dbus",
82 hwmonUserCap, dbusUserCap)
83 .c_str());
84 writeOcc(dbusUserCap);
85 }
Chris Cain5d66a0a2022-02-09 08:52:10 -060086}
87
Andrew Geissler4cea4d22017-07-10 15:13:33 -050088uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled)
89{
90 if (!pcapEnabled)
91 {
92 // Pcap disabled, return 0 to indicate disabled
93 return 0;
94 }
95
96 // If pcap is not disabled then just return the pcap with the derating
97 // factor applied.
Gunnar Mills94df8c92018-09-14 14:50:03 -050098 return ((static_cast<uint64_t>(pcap) * PS_DERATING_FACTOR) / 100);
Andrew Geissler4cea4d22017-07-10 15:13:33 -050099}
100
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500101uint32_t PowerCap::getPcap()
102{
George Liuf3b75142021-06-10 11:22:50 +0800103 utils::PropertyValue pcap{};
104 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500105 {
George Liuf3b75142021-06-10 11:22:50 +0800106 pcap = utils::getProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_PROP);
107
108 return std::get<uint32_t>(pcap);
109 }
Patrick Williams25613622021-09-02 09:29:54 -0500110 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800111 {
112 log<level::ERR>("Failed to get PowerCap property",
113 entry("ERROR=%s", e.what()),
114 entry("PATH=%s", PCAP_PATH));
115
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500116 return 0;
117 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500118}
119
120bool PowerCap::getPcapEnabled()
121{
George Liuf3b75142021-06-10 11:22:50 +0800122 utils::PropertyValue pcapEnabled{};
123 try
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500124 {
George Liuf3b75142021-06-10 11:22:50 +0800125 pcapEnabled = utils::getProperty(PCAP_PATH, PCAP_INTERFACE,
126 POWER_CAP_ENABLE_PROP);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500127
George Liuf3b75142021-06-10 11:22:50 +0800128 return std::get<bool>(pcapEnabled);
129 }
Patrick Williams25613622021-09-02 09:29:54 -0500130 catch (const sdbusplus::exception::exception& e)
George Liuf3b75142021-06-10 11:22:50 +0800131 {
132 log<level::ERR>("Failed to get PowerCapEnable property",
133 entry("ERROR=%s", e.what()),
134 entry("PATH=%s", PCAP_PATH));
135
136 return false;
137 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500138}
Andrew Geissler32016d12017-06-20 15:46:52 -0500139
Chris Cain5d66a0a2022-02-09 08:52:10 -0600140fs::path PowerCap::getPcapFilename(const std::regex& expr)
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500141{
Chris Cain5d66a0a2022-02-09 08:52:10 -0600142 if (pcapBasePathname.empty())
143 {
144 static bool tracedError = false;
145 pcapBasePathname = occStatus.getHwmonPath();
146 if (!fs::exists(pcapBasePathname) && !tracedError)
147 {
148 log<level::ERR>(fmt::format("Power Cap base filename not found: {}",
149 pcapBasePathname.c_str())
150 .c_str());
151 tracedError = true;
152 }
153 }
154 for (auto& file : fs::directory_iterator(pcapBasePathname))
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500155 {
156 if (std::regex_search(file.path().string(), expr))
157 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600158 return file;
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500159 }
160 }
Chris Cain5d66a0a2022-02-09 08:52:10 -0600161 // return empty path
162 return fs::path{};
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500163}
164
Chris Cain40501a22022-03-14 17:33:27 -0500165// Write the user power cap to sysfs.
166// This will trigger the driver to send the cap to the OCC
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500167void PowerCap::writeOcc(uint32_t pcapValue)
168{
Chris Cain5d66a0a2022-02-09 08:52:10 -0600169 // Build the hwmon string to write the user power cap
170 fs::path fileName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
171 if (fileName.empty())
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500172 {
Chris Cainb6535e82022-01-21 08:24:13 -0600173 log<level::ERR>(
174 fmt::format("Could not find a power cap file to write to: {})",
Chris Cain5d66a0a2022-02-09 08:52:10 -0600175 pcapBasePathname.c_str())
Chris Cainb6535e82022-01-21 08:24:13 -0600176 .c_str());
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500177 return;
178 }
Matt Spinlereaaf3b22019-07-16 10:29:27 -0500179
180 uint64_t microWatts = pcapValue * 1000000ull;
181
182 auto pcapString{std::to_string(microWatts)};
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500183
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500184 // Open the hwmon file and write the power cap
Chris Cain5d66a0a2022-02-09 08:52:10 -0600185 std::ofstream file(fileName, std::ios::out);
186 if (file)
187 {
188 log<level::INFO>(fmt::format("Writing {}uW to {}", pcapString.c_str(),
189 fileName.c_str())
190 .c_str());
191 file << pcapString;
192 file.close();
193 }
194 else
195 {
196 log<level::ERR>(fmt::format("Failed writing {}uW to {} (errno={})",
197 microWatts, fileName.c_str(), errno)
198 .c_str());
199 }
200
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500201 return;
202}
203
Chris Cain40501a22022-03-14 17:33:27 -0500204// Read the current user power cap from sysfs.
205uint32_t PowerCap::readUserCapHwmon()
206{
207 uint32_t userCap = 0;
208
209 // Get the sysfs filename for the user power cap
210 fs::path userCapName = getPcapFilename(std::regex{"power\\d+_cap_user$"});
211 if (userCapName.empty())
212 {
213 log<level::ERR>(
214 fmt::format(
215 "readUserCapHwmon: Could not find a power cap file to read: {})",
216 pcapBasePathname.c_str())
217 .c_str());
218 return 0;
219 }
220
221 // Open the sysfs file and read the power cap
222 uint64_t cap;
223 std::ifstream file(userCapName, std::ios::in);
224 if (file)
225 {
226 file >> cap;
227 file.close();
228 // Convert to Watts
229 userCap = cap / 1000000;
230 }
231 else
232 {
233 log<level::ERR>(
234 fmt::format("readUserCapHwmon: Failed reading {} (errno={})",
235 userCapName.c_str(), errno)
236 .c_str());
237 }
238
239 return userCap;
240}
241
Andrew Geissler32016d12017-06-20 15:46:52 -0500242void PowerCap::pcapChanged(sdbusplus::message::message& msg)
243{
Andrew Geissler32016d12017-06-20 15:46:52 -0500244 if (!occStatus.occActive())
245 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600246 // Nothing to do
Andrew Geissler32016d12017-06-20 15:46:52 -0500247 return;
248 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500249
250 uint32_t pcap = 0;
251 bool pcapEnabled = false;
252
253 std::string msgSensor;
Patrick Williamse0962702020-05-13 17:50:22 -0500254 std::map<std::string, std::variant<uint32_t, bool>> msgData;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500255 msg.read(msgSensor, msgData);
256
Chris Cain5d66a0a2022-02-09 08:52:10 -0600257 bool changeFound = false;
258 for (const auto& [prop, value] : msgData)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500259 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600260 if (prop == POWER_CAP_PROP)
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500261 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600262 pcap = std::get<uint32_t>(value);
263 pcapEnabled = getPcapEnabled();
264 changeFound = true;
265 }
266 else if (prop == POWER_CAP_ENABLE_PROP)
267 {
268 pcapEnabled = std::get<bool>(value);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500269 pcap = getPcap();
Chris Cain5d66a0a2022-02-09 08:52:10 -0600270 changeFound = true;
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500271 }
272 else
273 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600274 // Ignore other properties
275 log<level::DEBUG>(
276 fmt::format(
277 "pcapChanged: Unknown power cap property changed {} to {}",
278 prop.c_str(), std::get<uint32_t>(value))
279 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500280 }
281 }
282
Chris Cain5d66a0a2022-02-09 08:52:10 -0600283 if (changeFound)
284 {
285 log<level::INFO>(
286 fmt::format(
287 "Power Cap Property Change (cap={}W (input), enabled={})", pcap,
288 pcapEnabled ? 'y' : 'n')
289 .c_str());
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500290
Chris Cain5d66a0a2022-02-09 08:52:10 -0600291 // Determine desired action to write to occ
Andrew Geissler6ac874e2017-07-10 15:54:58 -0500292
Chris Cain5d66a0a2022-02-09 08:52:10 -0600293 auto occInput = getOccInput(pcap, pcapEnabled);
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500294
Chris Cain5d66a0a2022-02-09 08:52:10 -0600295 // Write action to occ
296 writeOcc(occInput);
297 }
Andrew Geissler52cf26a2017-07-06 12:56:32 -0500298
299 return;
Andrew Geissler32016d12017-06-20 15:46:52 -0500300}
301
Chris Cain5d66a0a2022-02-09 08:52:10 -0600302// Update the Power Cap bounds on DBus
Chris Cain40501a22022-03-14 17:33:27 -0500303bool PowerCap::updateDbusPcap(uint32_t hardMin, uint32_t max)
Chris Cain5d66a0a2022-02-09 08:52:10 -0600304{
305 bool complete = true;
306
307 try
308 {
Chris Cain5d66a0a2022-02-09 08:52:10 -0600309 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_HARD_MIN,
310 hardMin);
311 }
312 catch (const sdbusplus::exception::exception& e)
313 {
314 log<level::ERR>(
315 fmt::format(
316 "updateDbusPcap: Failed to set HARD PCAP to {}W due to {}",
317 hardMin, e.what())
318 .c_str());
319 complete = false;
320 }
321
322 try
323 {
324 utils::setProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_MAX, max);
325 }
326 catch (const sdbusplus::exception::exception& e)
327 {
328 log<level::ERR>(
329 fmt::format(
330 "updateDbusPcap: Failed to set MAX PCAP to {}W due to {}", max,
331 e.what())
332 .c_str());
333 complete = false;
334 }
335
336 return complete;
337}
Gunnar Mills94df8c92018-09-14 14:50:03 -0500338} // namespace powercap
Andrew Geissler32016d12017-06-20 15:46:52 -0500339
340} // namespace occ
341
Gunnar Mills94df8c92018-09-14 14:50:03 -0500342} // namespace open_power