1 #pragma once 2 3 #include <cstring> 4 #include <vector> 5 #include <experimental/filesystem> 6 #include <functional> 7 #include <sdbusplus/bus.hpp> 8 #include <phosphor-logging/log.hpp> 9 #include <phosphor-logging/elog.hpp> 10 #include <powercap.hpp> 11 #include "occ_pass_through.hpp" 12 #include "occ_status.hpp" 13 #include "occ_finder.hpp" 14 #include "config.h" 15 #include "i2c_occ.hpp" 16 17 namespace sdbusRule = sdbusplus::bus::match::rules; 18 namespace open_power 19 { 20 namespace occ 21 { 22 23 /** @class Manager 24 * @brief Builds and manages OCC objects 25 */ 26 struct Manager 27 { 28 public: 29 Manager() = delete; 30 Manager(const Manager&) = delete; 31 Manager& operator=(const Manager&) = delete; 32 Manager(Manager&&) = default; 33 Manager& operator=(Manager&&) = default; 34 ~Manager() = default; 35 36 /** @brief Adds OCC pass-through and status objects on the bus 37 * when corresponding CPU inventory is created. 38 * 39 * @param[in] bus - handle to the bus 40 * @param[in] event - Unique ptr reference to sd_event 41 */ 42 Manager(sdbusplus::bus::bus& bus, 43 EventPtr& event) : 44 bus(bus), 45 event(event) 46 { 47 #ifdef I2C_OCC 48 // I2C OCC status objects are initialized directly 49 initStatusObjects(); 50 #else 51 // Check if CPU inventory exists already. 52 auto occs = open_power::occ::finder::get(bus); 53 if (occs.empty()) 54 { 55 // Need to watch for CPU inventory creation. 56 for (auto id = 0; id < MAX_CPUS; ++id) 57 { 58 auto path = std::string(CPU_PATH) + std::to_string(id); 59 cpuMatches.emplace_back( 60 bus, 61 sdbusRule::interfacesAdded() + 62 sdbusRule::argNpath(0, path), 63 std::bind(std::mem_fn(&Manager::cpuCreated), 64 this, std::placeholders::_1)); 65 } 66 } 67 else 68 { 69 for (const auto& occ : occs) 70 { 71 // CPU inventory exists already, OCC objects can be created. 72 createObjects(occ); 73 } 74 } 75 #endif 76 } 77 78 private: 79 /** @brief Callback that responds to cpu creation in the inventory - 80 * by creating the needed objects. 81 * 82 * @param[in] msg - bus message 83 * 84 * @returns 0 to indicate success 85 */ 86 int cpuCreated(sdbusplus::message::message& msg) 87 { 88 namespace fs = std::experimental::filesystem; 89 90 sdbusplus::message::object_path o; 91 msg.read(o); 92 fs::path cpuPath(std::string(std::move(o))); 93 94 auto name = cpuPath.filename().string(); 95 auto index = name.find(CPU_NAME); 96 name.replace(index, std::strlen(CPU_NAME), OCC_NAME); 97 98 createObjects(name); 99 100 return 0; 101 } 102 103 /** @brief Create child OCC objects. 104 * 105 * @param[in] occ - the occ name, such as occ0. 106 */ 107 void createObjects(const std::string& occ) 108 { 109 auto path = fs::path(OCC_CONTROL_ROOT) / occ; 110 111 passThroughObjects.emplace_back( 112 std::make_unique<PassThrough>( 113 bus, 114 path.c_str())); 115 116 statusObjects.emplace_back( 117 std::make_unique<Status>( 118 bus, 119 event, 120 path.c_str(), 121 std::bind(std::mem_fn(&Manager::statusCallBack), 122 this, std::placeholders::_1))); 123 124 // Create the power cap monitor object for master occ (0) 125 if (!pcap) 126 { 127 pcap = std::make_unique<open_power::occ::powercap::PowerCap>( 128 bus, 129 *statusObjects.front()); 130 } 131 } 132 133 /** @brief Callback handler invoked by Status object when the OccActive 134 * property is changed. This is needed to make sure that the 135 * error detection is started only after all the OCCs are bound. 136 * Similarly, when one of the OCC gets its OccActive property 137 * un-set, then the OCC error detection needs to be stopped on 138 * all the OCCs 139 * 140 * @param[in] status - OccActive status 141 */ 142 void statusCallBack(bool status) 143 { 144 using namespace phosphor::logging; 145 using InternalFailure = sdbusplus::xyz::openbmc_project::Common:: 146 Error::InternalFailure; 147 148 // At this time, it won't happen but keeping it 149 // here just in case something changes in the future 150 if ((activeCount == 0) && (!status)) 151 { 152 log<level::ERR>("Invalid update on OCCActive"); 153 elog<InternalFailure>(); 154 } 155 156 activeCount += status ? 1 : -1; 157 158 // If all the OCCs are bound, then start error detection 159 if (activeCount == statusObjects.size()) 160 { 161 for (const auto& occ: statusObjects) 162 { 163 occ->addErrorWatch(); 164 } 165 } 166 else if (!status) 167 { 168 // If some OCCs are not bound yet, those will be a NO-OP 169 for (const auto& occ: statusObjects) 170 { 171 occ->removeErrorWatch(); 172 } 173 } 174 } 175 176 /** @brief reference to the bus */ 177 sdbusplus::bus::bus& bus; 178 179 /** @brief reference to sd_event wrapped in unique_ptr */ 180 EventPtr& event; 181 182 /** @brief OCC pass-through objects */ 183 std::vector<std::unique_ptr<PassThrough>> passThroughObjects; 184 185 /** @brief OCC Status objects */ 186 std::vector<std::unique_ptr<Status>> statusObjects; 187 188 /** @brief Power cap monitor and occ notification object */ 189 std::unique_ptr<open_power::occ::powercap::PowerCap> pcap; 190 191 /** @brief sbdbusplus match objects */ 192 std::vector<sdbusplus::bus::match_t> cpuMatches; 193 194 /** @brief Number of OCCs that are bound */ 195 uint8_t activeCount = 0; 196 197 #ifdef I2C_OCC 198 /** @brief Init Status objects for I2C OCC devices 199 * 200 * It iterates in /sys/bus/i2c/devices, finds all occ hwmon devices 201 * and creates status objects. 202 */ 203 void initStatusObjects() 204 { 205 // Make sure we have a valid path string 206 static_assert(sizeof(DEV_PATH) != 0); 207 208 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH); 209 for (auto& name : deviceNames) 210 { 211 i2c_occ::i2cToDbus(name); 212 auto path = fs::path(OCC_CONTROL_ROOT) / name; 213 statusObjects.emplace_back( 214 std::make_unique<Status>( 215 bus, 216 event, 217 path.c_str())); 218 } 219 } 220 #endif 221 }; 222 223 } // namespace occ 224 } // namespace open_power 225