xref: /openbmc/openpower-occ-control/powercap.cpp (revision 25613624cee19b8009f72cf30229d68e1fa7aa0a)
1 #include <cassert>
2 #include <phosphor-logging/log.hpp>
3 #include <powercap.hpp>
4 #include <regex>
5 
6 namespace open_power
7 {
8 namespace occ
9 {
10 namespace powercap
11 {
12 
13 constexpr auto PCAP_PATH = "/xyz/openbmc_project/control/host0/power_cap";
14 constexpr auto PCAP_INTERFACE = "xyz.openbmc_project.Control.Power.Cap";
15 
16 constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
17 constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
18 constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
19 
20 constexpr auto POWER_CAP_PROP = "PowerCap";
21 constexpr auto POWER_CAP_ENABLE_PROP = "PowerCapEnable";
22 
23 using namespace phosphor::logging;
24 namespace fs = std::experimental::filesystem;
25 
26 uint32_t PowerCap::getOccInput(uint32_t pcap, bool pcapEnabled)
27 {
28     if (!pcapEnabled)
29     {
30         // Pcap disabled, return 0 to indicate disabled
31         return 0;
32     }
33 
34     // If pcap is not disabled then just return the pcap with the derating
35     // factor applied.
36     return ((static_cast<uint64_t>(pcap) * PS_DERATING_FACTOR) / 100);
37 }
38 
39 uint32_t PowerCap::getPcap()
40 {
41     utils::PropertyValue pcap{};
42     try
43     {
44         pcap = utils::getProperty(PCAP_PATH, PCAP_INTERFACE, POWER_CAP_PROP);
45 
46         return std::get<uint32_t>(pcap);
47     }
48     catch (const sdbusplus::exception::exception& e)
49     {
50         log<level::ERR>("Failed to get PowerCap property",
51                         entry("ERROR=%s", e.what()),
52                         entry("PATH=%s", PCAP_PATH));
53 
54         return 0;
55     }
56 }
57 
58 bool PowerCap::getPcapEnabled()
59 {
60     utils::PropertyValue pcapEnabled{};
61     try
62     {
63         pcapEnabled = utils::getProperty(PCAP_PATH, PCAP_INTERFACE,
64                                          POWER_CAP_ENABLE_PROP);
65 
66         return std::get<bool>(pcapEnabled);
67     }
68     catch (const sdbusplus::exception::exception& e)
69     {
70         log<level::ERR>("Failed to get PowerCapEnable property",
71                         entry("ERROR=%s", e.what()),
72                         entry("PATH=%s", PCAP_PATH));
73 
74         return false;
75     }
76 }
77 
78 std::string PowerCap::getPcapFilename(const fs::path& path)
79 {
80     std::regex expr{"power\\d+_cap_user$"};
81     for (auto& file : fs::directory_iterator(path))
82     {
83         if (std::regex_search(file.path().string(), expr))
84         {
85             return file.path().filename();
86         }
87     }
88     return std::string{};
89 }
90 
91 void PowerCap::writeOcc(uint32_t pcapValue)
92 {
93     // Create path out to master occ hwmon entry
94     std::unique_ptr<fs::path> fileName =
95         std::make_unique<fs::path>(OCC_HWMON_PATH);
96     *fileName /= occMasterName;
97     *fileName /= "/hwmon/";
98 
99     // Need to get the hwmonXX directory name, there better only be 1 dir
100     assert(std::distance(fs::directory_iterator(*fileName),
101                          fs::directory_iterator{}) == 1);
102     // Now set our path to this full path, including this hwmonXX directory
103     fileName = std::make_unique<fs::path>(*fs::directory_iterator(*fileName));
104     // Append on the hwmon string where we write the user power cap
105 
106     auto baseName = getPcapFilename(*fileName);
107     if (baseName.empty())
108     {
109         log<level::ERR>("Could not find a power cap file to write to",
110                         entry("PATH=%s", *fileName->c_str()));
111         return;
112     }
113     *fileName /= baseName;
114 
115     uint64_t microWatts = pcapValue * 1000000ull;
116 
117     auto pcapString{std::to_string(microWatts)};
118 
119     log<level::INFO>("Writing pcap value to hwmon",
120                      entry("PCAP_PATH=%s", fileName->c_str()),
121                      entry("PCAP_VALUE=%s", pcapString.c_str()));
122     // Open the hwmon file and write the power cap
123     std::ofstream file(*fileName, std::ios::out);
124     file << pcapString;
125     file.close();
126     return;
127 }
128 
129 void PowerCap::pcapChanged(sdbusplus::message::message& msg)
130 {
131     if (!occStatus.occActive())
132     {
133         // Nothing to  do
134         return;
135     }
136 
137     uint32_t pcap = 0;
138     bool pcapEnabled = false;
139 
140     std::string msgSensor;
141     std::map<std::string, std::variant<uint32_t, bool>> msgData;
142     msg.read(msgSensor, msgData);
143 
144     // Retrieve which property changed via the msg and read the other one
145     auto valPropMap = msgData.find(POWER_CAP_PROP);
146     if (valPropMap != msgData.end())
147     {
148         pcap = std::get<uint32_t>(valPropMap->second);
149         pcapEnabled = getPcapEnabled();
150     }
151     else
152     {
153         valPropMap = msgData.find(POWER_CAP_ENABLE_PROP);
154         if (valPropMap != msgData.end())
155         {
156             pcapEnabled = std::get<bool>(valPropMap->second);
157             pcap = getPcap();
158         }
159         else
160         {
161             log<level::INFO>("Unknown power cap property changed");
162             return;
163         }
164     }
165 
166     log<level::INFO>("Power Cap Property Change", entry("PCAP=%u", pcap),
167                      entry("PCAP_ENABLED=%u", pcapEnabled));
168 
169     // Determine desired action to write to occ
170 
171     auto occInput = getOccInput(pcap, pcapEnabled);
172 
173     // Write action to occ
174     writeOcc(occInput);
175 
176     return;
177 }
178 
179 } // namespace powercap
180 
181 } // namespace occ
182 
183 } // namespace open_power
184