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