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