1 #include "config.h" 2 3 #include "occ_manager.hpp" 4 5 #include "i2c_occ.hpp" 6 #include "occ_dbus.hpp" 7 #include "utils.hpp" 8 9 #include <cmath> 10 #include <experimental/filesystem> 11 #include <phosphor-logging/elog-errors.hpp> 12 #include <phosphor-logging/log.hpp> 13 #include <regex> 14 #include <xyz/openbmc_project/Common/error.hpp> 15 16 namespace open_power 17 { 18 namespace occ 19 { 20 21 constexpr uint32_t fruTypeNotAvailable = 0xFF; 22 23 using namespace phosphor::logging; 24 25 void Manager::findAndCreateObjects() 26 { 27 for (auto id = 0; id < MAX_CPUS; ++id) 28 { 29 // Create one occ per cpu 30 auto occ = std::string(OCC_NAME) + std::to_string(id); 31 createObjects(occ); 32 } 33 } 34 35 int Manager::cpuCreated(sdbusplus::message::message& msg) 36 { 37 namespace fs = std::experimental::filesystem; 38 39 sdbusplus::message::object_path o; 40 msg.read(o); 41 fs::path cpuPath(std::string(std::move(o))); 42 43 auto name = cpuPath.filename().string(); 44 auto index = name.find(CPU_NAME); 45 name.replace(index, std::strlen(CPU_NAME), OCC_NAME); 46 47 createObjects(name); 48 49 return 0; 50 } 51 52 void Manager::createObjects(const std::string& occ) 53 { 54 auto path = fs::path(OCC_CONTROL_ROOT) / occ; 55 56 passThroughObjects.emplace_back( 57 std::make_unique<PassThrough>(path.c_str())); 58 59 statusObjects.emplace_back(std::make_unique<Status>( 60 event, path.c_str(), *this, 61 std::bind(std::mem_fn(&Manager::statusCallBack), this, 62 std::placeholders::_1) 63 #ifdef PLDM 64 , 65 std::bind(std::mem_fn(&pldm::Interface::resetOCC), pldmHandle.get(), 66 std::placeholders::_1) 67 #endif 68 )); 69 70 // Create the power cap monitor object for master occ (0) 71 if (!pcap) 72 { 73 pcap = std::make_unique<open_power::occ::powercap::PowerCap>( 74 *statusObjects.front()); 75 } 76 77 #ifdef POWER10 78 // Create the power mode monitor object for master occ (0) 79 if (!pmode) 80 { 81 pmode = std::make_unique<open_power::occ::powermode::PowerMode>( 82 *statusObjects.front()); 83 } 84 #endif 85 } 86 87 void Manager::statusCallBack(bool status) 88 { 89 using InternalFailure = 90 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 91 92 // At this time, it won't happen but keeping it 93 // here just in case something changes in the future 94 if ((activeCount == 0) && (!status)) 95 { 96 log<level::ERR>("Invalid update on OCCActive"); 97 elog<InternalFailure>(); 98 } 99 100 activeCount += status ? 1 : -1; 101 102 // Only start presence detection if all the OCCs are bound 103 if (activeCount == statusObjects.size()) 104 { 105 for (auto& obj : statusObjects) 106 { 107 obj->addPresenceWatchMaster(); 108 } 109 } 110 111 if ((!_pollTimer->isEnabled()) && (activeCount > 0)) 112 { 113 log<level::INFO>(fmt::format("Manager::statusCallBack(): {} OCCs will " 114 "be polled every {} seconds", 115 activeCount, pollInterval) 116 .c_str()); 117 118 // Send poll and start OCC poll timer 119 pollerTimerExpired(); 120 } 121 else if ((_pollTimer->isEnabled()) && (activeCount == 0)) 122 { 123 // Stop OCC poll timer 124 log<level::INFO>("Manager::statusCallBack(): OCCs are not running, " 125 "stopping poll timer"); 126 _pollTimer->setEnabled(false); 127 } 128 } 129 130 #ifdef I2C_OCC 131 void Manager::initStatusObjects() 132 { 133 // Make sure we have a valid path string 134 static_assert(sizeof(DEV_PATH) != 0); 135 136 auto deviceNames = i2c_occ::getOccHwmonDevices(DEV_PATH); 137 auto occMasterName = deviceNames.front(); 138 for (auto& name : deviceNames) 139 { 140 i2c_occ::i2cToDbus(name); 141 name = std::string(OCC_NAME) + '_' + name; 142 auto path = fs::path(OCC_CONTROL_ROOT) / name; 143 statusObjects.emplace_back( 144 std::make_unique<Status>(event, path.c_str(), *this)); 145 } 146 // The first device is master occ 147 pcap = std::make_unique<open_power::occ::powercap::PowerCap>( 148 *statusObjects.front(), occMasterName); 149 #ifdef POWER10 150 pmode = std::make_unique<open_power::occ::powermode::PowerMode>( 151 *statusObjects.front()); 152 #endif 153 } 154 #endif 155 156 #ifdef PLDM 157 bool Manager::updateOCCActive(instanceID instance, bool status) 158 { 159 return (statusObjects[instance])->occActive(status); 160 } 161 #endif 162 163 void Manager::pollerTimerExpired() 164 { 165 if (activeCount == 0) 166 { 167 // No OCCs running, so poll timer will not be restarted 168 log<level::INFO>("Manager::pollerTimerExpire(): No OCCs running, poll " 169 "timer not restarted"); 170 } 171 172 if (!_pollTimer) 173 { 174 log<level::ERR>( 175 "Manager::pollerTimerExpired() ERROR: Timer not defined"); 176 return; 177 } 178 179 for (auto& obj : statusObjects) 180 { 181 // Read sysfs to force kernel to poll OCC 182 obj->readOccState(); 183 184 #ifdef READ_OCC_SENSORS 185 // Read occ sensor values 186 auto id = obj->getOccInstanceID(); 187 if (!obj->occActive()) 188 { 189 // Occ not activated 190 setSensorValueToNaN(id); 191 continue; 192 } 193 getSensorValues(id, obj->isMasterOcc()); 194 #endif 195 } 196 197 // Restart OCC poll timer 198 _pollTimer->restartOnce(std::chrono::seconds(pollInterval)); 199 } 200 201 #ifdef READ_OCC_SENSORS 202 void Manager::readTempSensors(const fs::path& path, uint32_t id) 203 { 204 const int open_errno = errno; 205 std::regex expr{"temp\\d+_label$"}; // Example: temp5_label 206 for (auto& file : fs::directory_iterator(path)) 207 { 208 if (!std::regex_search(file.path().string(), expr)) 209 { 210 continue; 211 } 212 std::ifstream fileOpen(file.path(), std::ios::in); 213 if (!fileOpen) 214 { 215 // If not able to read, OCC may be offline 216 log<level::DEBUG>( 217 fmt::format("readTempSensors: open failed(errno = {}) ", 218 open_errno) 219 .c_str()); 220 221 continue; 222 } 223 uint32_t labelValue{0}; 224 fileOpen >> labelValue; 225 fileOpen.close(); 226 227 const std::string& tempLabel = "label"; 228 const std::string filePathString = file.path().string().substr( 229 0, file.path().string().length() - tempLabel.length()); 230 std::ifstream fruTypeFile(filePathString + "fru_type", std::ios::in); 231 if (!fruTypeFile) 232 { 233 // If not able to read, OCC may be offline 234 log<level::DEBUG>( 235 fmt::format("readTempSensors: open failed(errno = {}) ", 236 open_errno) 237 .c_str()); 238 continue; 239 } 240 uint32_t fruTypeValue{0}; 241 fruTypeFile >> fruTypeValue; 242 fruTypeFile.close(); 243 244 std::string sensorPath = 245 OCC_SENSORS_ROOT + std::string("/temperature/"); 246 247 if (fruTypeValue == VRMVdd) 248 { 249 sensorPath.append("vrm_vdd" + std::to_string(id) + "_temp"); 250 } 251 else 252 { 253 uint16_t type = (labelValue & 0xFF000000) >> 24; 254 uint16_t instanceID = labelValue & 0x0000FFFF; 255 256 if (type == OCC_DIMM_TEMP_SENSOR_TYPE) 257 { 258 if (fruTypeValue == fruTypeNotAvailable) 259 { 260 // Not all DIMM related temps are available to read 261 // (no _input file in this case) 262 continue; 263 } 264 auto iter = dimmTempSensorName.find(fruTypeValue); 265 if (iter == dimmTempSensorName.end()) 266 { 267 log<level::ERR>(fmt::format("readTempSensors: Fru type " 268 "error! fruTypeValue = {}) ", 269 fruTypeValue) 270 .c_str()); 271 continue; 272 } 273 274 sensorPath.append("dimm" + std::to_string(instanceID) + 275 iter->second); 276 } 277 else if (type == OCC_CPU_TEMP_SENSOR_TYPE) 278 { 279 if (fruTypeValue != processorCore) 280 { 281 // TODO: support IO ring temp 282 continue; 283 } 284 285 sensorPath.append("proc" + std::to_string(id) + "_core" + 286 std::to_string(instanceID) + "_temp"); 287 } 288 else 289 { 290 continue; 291 } 292 } 293 294 std::ifstream faultPathFile(filePathString + "fault", std::ios::in); 295 if (faultPathFile) 296 { 297 uint32_t faultValue; 298 faultPathFile >> faultValue; 299 faultPathFile.close(); 300 301 if (faultValue != 0) 302 { 303 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue( 304 sensorPath, std::numeric_limits<double>::quiet_NaN()); 305 306 open_power::occ::dbus::OccDBusSensors::getOccDBus() 307 .setOperationalStatus(sensorPath, false); 308 309 continue; 310 } 311 } 312 313 std::ifstream inputFile(filePathString + "input", std::ios::in); 314 if (inputFile) 315 { 316 double tempValue; 317 inputFile >> tempValue; 318 319 inputFile.close(); 320 321 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue( 322 sensorPath, tempValue * std::pow(10, -3)); 323 324 open_power::occ::dbus::OccDBusSensors::getOccDBus() 325 .setOperationalStatus(sensorPath, true); 326 327 existingSensors[sensorPath] = id; 328 } 329 else 330 { 331 // If not able to read, OCC may be offline 332 log<level::DEBUG>( 333 fmt::format("readTempSensors: open failed(errno = {}) ", 334 open_errno) 335 .c_str()); 336 } 337 } 338 return; 339 } 340 341 std::optional<std::string> 342 Manager::getPowerLabelFunctionID(const std::string& value) 343 { 344 // If the value is "system", then the FunctionID is "system". 345 if (value == "system") 346 { 347 return value; 348 } 349 350 // If the value is not "system", then the label value have 3 numbers, of 351 // which we only care about the middle one: 352 // <sensor id>_<function id>_<apss channel> 353 // eg: The value is "0_10_5" , then the FunctionID is "10". 354 if (value.find("_") == std::string::npos) 355 { 356 return std::nullopt; 357 } 358 359 auto powerLabelValue = value.substr((value.find("_") + 1)); 360 361 if (powerLabelValue.find("_") == std::string::npos) 362 { 363 return std::nullopt; 364 } 365 366 return powerLabelValue.substr(0, powerLabelValue.find("_")); 367 } 368 369 void Manager::readPowerSensors(const fs::path& path, uint32_t id) 370 { 371 const int open_errno = errno; 372 std::regex expr{"power\\d+_label$"}; // Example: power5_label 373 for (auto& file : fs::directory_iterator(path)) 374 { 375 if (!std::regex_search(file.path().string(), expr)) 376 { 377 continue; 378 } 379 std::ifstream fileOpen(file.path(), std::ios::in); 380 if (!fileOpen) 381 { 382 // If not able to read, OCC may be offline 383 log<level::DEBUG>( 384 fmt::format("readPowerSensors: open failed(errno = {}) ", 385 open_errno) 386 .c_str()); 387 388 continue; 389 } 390 std::string labelValue; 391 fileOpen >> labelValue; 392 fileOpen.close(); 393 394 auto functionID = getPowerLabelFunctionID(labelValue); 395 if (functionID == std::nullopt) 396 { 397 continue; 398 } 399 400 const std::string& tempLabel = "label"; 401 const std::string filePathString = file.path().string().substr( 402 0, file.path().string().length() - tempLabel.length()); 403 404 std::string sensorPath = OCC_SENSORS_ROOT + std::string("/power/"); 405 406 auto iter = powerSensorName.find(*functionID); 407 if (iter == powerSensorName.end()) 408 { 409 continue; 410 } 411 sensorPath.append(iter->second); 412 413 std::ifstream faultPathFile(filePathString + "fault", std::ios::in); 414 if (faultPathFile) 415 { 416 uint32_t faultValue{0}; 417 faultPathFile >> faultValue; 418 faultPathFile.close(); 419 420 if (faultValue != 0) 421 { 422 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue( 423 sensorPath, std::numeric_limits<double>::quiet_NaN()); 424 425 open_power::occ::dbus::OccDBusSensors::getOccDBus() 426 .setOperationalStatus(sensorPath, false); 427 428 continue; 429 } 430 } 431 432 std::ifstream inputFile(filePathString + "input", std::ios::in); 433 if (inputFile) 434 { 435 double tempValue; 436 inputFile >> tempValue; 437 inputFile.close(); 438 439 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue( 440 sensorPath, tempValue * std::pow(10, -3) * std::pow(10, -3)); 441 442 open_power::occ::dbus::OccDBusSensors::getOccDBus() 443 .setOperationalStatus(sensorPath, true); 444 445 existingSensors[sensorPath] = id; 446 } 447 else 448 { 449 // If not able to read, OCC may be offline 450 log<level::DEBUG>( 451 fmt::format("readPowerSensors: open failed(errno = {}) ", 452 open_errno) 453 .c_str()); 454 } 455 } 456 return; 457 } 458 459 void Manager::setSensorValueToNaN(uint32_t id) 460 { 461 for (const auto& [sensorPath, occId] : existingSensors) 462 { 463 if (occId == id) 464 { 465 open_power::occ::dbus::OccDBusSensors::getOccDBus().setValue( 466 sensorPath, std::numeric_limits<double>::quiet_NaN()); 467 } 468 } 469 return; 470 } 471 472 void Manager::getSensorValues(uint32_t id, bool masterOcc) 473 { 474 const auto occ = std::string("occ-hwmon.") + std::to_string(id + 1); 475 476 fs::path fileName{OCC_HWMON_PATH + occ + "/hwmon/"}; 477 478 // Need to get the hwmonXX directory name, there better only be 1 dir 479 assert(std::distance(fs::directory_iterator(fileName), 480 fs::directory_iterator{}) == 1); 481 // Now set our path to this full path, including this hwmonXX directory 482 fileName = fs::path(*fs::directory_iterator(fileName)); 483 484 // Read temperature sensors 485 readTempSensors(fileName, id); 486 487 if (masterOcc) 488 { 489 // Read power sensors 490 readPowerSensors(fileName, id); 491 } 492 493 return; 494 } 495 #endif 496 497 } // namespace occ 498 } // namespace open_power 499