1 #include "occ_status.hpp" 2 3 #include "occ_sensor.hpp" 4 #include "powermode.hpp" 5 #include "utils.hpp" 6 7 #include <fmt/core.h> 8 9 #ifdef POWER10 10 #include <com/ibm/Host/Target/server.hpp> 11 #endif 12 #include <phosphor-logging/log.hpp> 13 14 namespace open_power 15 { 16 namespace occ 17 { 18 19 using namespace phosphor::logging; 20 21 // Handles updates to occActive property 22 bool Status::occActive(bool value) 23 { 24 if (value != this->occActive()) 25 { 26 log<level::INFO>(fmt::format("Status::occActive OCC{} changed to {}", 27 instance, value) 28 .c_str()); 29 if (value) 30 { 31 // Bind the device 32 device.bind(); 33 34 // Start watching for errors 35 addErrorWatch(); 36 37 // Reset last OCC state 38 lastState = 0; 39 40 // Call into Manager to let know that we have bound 41 if (this->callBack) 42 { 43 this->callBack(value); 44 } 45 } 46 else 47 { 48 // Call into Manager to let know that we will unbind. 49 if (this->callBack) 50 { 51 this->callBack(value); 52 } 53 54 // Stop watching for errors 55 removeErrorWatch(); 56 57 // Do the unbind. 58 device.unBind(); 59 } 60 } 61 else if (value && !device.bound()) 62 { 63 // Existing error watch is on a dead file descriptor. 64 removeErrorWatch(); 65 66 /* 67 * In it's constructor, Status checks Device::bound() to see if OCC is 68 * active or not. 69 * Device::bound() checks for occX-dev0 directory. 70 * We will lose occX-dev0 directories during FSI rescan. 71 * So, if we start this application (and construct Status), and then 72 * later do FSI rescan, we will end up with occActive = true and device 73 * NOT bound. Lets correct that situation here. 74 */ 75 device.bind(); 76 77 // Add error watch again 78 addErrorWatch(); 79 } 80 else if (!value && device.bound()) 81 { 82 removeErrorWatch(); 83 84 // In the event that the application never receives the active signal 85 // even though the OCC is active (this can occur if the BMC is rebooted 86 // with the host on, since the initial OCC driver probe will discover 87 // the OCCs), this application needs to be able to unbind the device 88 // when we get the OCC inactive signal. 89 device.unBind(); 90 } 91 return Base::Status::occActive(value); 92 } 93 94 // Callback handler when a device error is reported. 95 void Status::deviceErrorHandler(bool error) 96 { 97 // Make sure we have an error 98 if (error) 99 { 100 // This would deem OCC inactive 101 this->occActive(false); 102 103 // Reset the OCC 104 this->resetOCC(); 105 } 106 } 107 108 // Sends message to host control command handler to reset OCC 109 void Status::resetOCC() 110 { 111 log<level::INFO>( 112 fmt::format(">>Status::resetOCC() - requesting reset for OCC{}", 113 instance) 114 .c_str()); 115 #ifdef PLDM 116 if (resetCallBack) 117 { 118 this->resetCallBack(instance); 119 } 120 #else 121 constexpr auto CONTROL_HOST_PATH = "/org/open_power/control/host0"; 122 constexpr auto CONTROL_HOST_INTF = "org.open_power.Control.Host"; 123 124 // This will throw exception on failure 125 auto service = utils::getService(CONTROL_HOST_PATH, CONTROL_HOST_INTF); 126 127 auto& bus = utils::getBus(); 128 auto method = bus.new_method_call(service.c_str(), CONTROL_HOST_PATH, 129 CONTROL_HOST_INTF, "Execute"); 130 // OCC Reset control command 131 method.append(convertForMessage(Control::Host::Command::OCCReset).c_str()); 132 133 // OCC Sensor ID for callout reasons 134 method.append(std::variant<uint8_t>(std::get<0>(sensorMap.at(instance)))); 135 bus.call_noreply(method); 136 return; 137 #endif 138 } 139 140 // Handler called by Host control command handler to convey the 141 // status of the executed command 142 void Status::hostControlEvent(sdbusplus::message::message& msg) 143 { 144 std::string cmdCompleted{}; 145 std::string cmdStatus{}; 146 147 msg.read(cmdCompleted, cmdStatus); 148 149 log<level::DEBUG>("Host control signal values", 150 entry("COMMAND=%s", cmdCompleted.c_str()), 151 entry("STATUS=%s", cmdStatus.c_str())); 152 153 if (Control::Host::convertResultFromString(cmdStatus) != 154 Control::Host::Result::Success) 155 { 156 if (Control::Host::convertCommandFromString(cmdCompleted) == 157 Control::Host::Command::OCCReset) 158 { 159 // Must be a Timeout. Log an Error trace 160 log<level::ERR>( 161 "Error resetting the OCC.", entry("PATH=%s", path.c_str()), 162 entry("SENSORID=0x%X", std::get<0>(sensorMap.at(instance)))); 163 } 164 } 165 return; 166 } 167 168 void Status::readOccState() 169 { 170 unsigned int state; 171 const fs::path filename = 172 fs::path(DEV_PATH) / 173 fs::path(sysfsName + "." + std::to_string(instance + 1)) / "occ_state"; 174 175 std::ifstream file(filename, std::ios::in); 176 const int open_errno = errno; 177 if (file) 178 { 179 file >> state; 180 if (state != lastState) 181 { 182 // Trace OCC state changes 183 log<level::INFO>( 184 fmt::format("Status::readOccState: OCC{} state 0x{:02X}", 185 instance, state) 186 .c_str()); 187 lastState = state; 188 189 #ifdef POWER10 190 if ((OccState(state) == OccState::ACTIVE) && (device.master())) 191 { 192 // Kernel detected that the master OCC went to active state 193 occsWentActive(); 194 } 195 #endif 196 } 197 file.close(); 198 } 199 else 200 { 201 // If not able to read, OCC may be offline 202 log<level::DEBUG>( 203 fmt::format("Status::readOccState: open failed (errno={})", 204 open_errno) 205 .c_str()); 206 lastState = 0; 207 } 208 } 209 210 #ifdef POWER10 211 // Check if Hypervisor target is PowerVM 212 bool Status::isPowerVM() 213 { 214 using namespace open_power::occ::powermode; 215 namespace Hyper = sdbusplus::com::ibm::Host::server; 216 constexpr auto HYPE_PATH = "/com/ibm/host0/hypervisor"; 217 constexpr auto HYPE_INTERFACE = "com.ibm.Host.Target"; 218 constexpr auto HYPE_PROP = "Target"; 219 220 bool powerVmTarget = false; 221 222 // This will throw exception on failure 223 auto& bus = utils::getBus(); 224 auto service = utils::getService(HYPE_PATH, HYPE_INTERFACE); 225 auto method = bus.new_method_call(service.c_str(), HYPE_PATH, 226 "org.freedesktop.DBus.Properties", "Get"); 227 method.append(HYPE_INTERFACE, HYPE_PROP); 228 auto reply = bus.call(method); 229 230 std::variant<std::string> hyperEntryValue; 231 reply.read(hyperEntryValue); 232 auto propVal = std::get<std::string>(hyperEntryValue); 233 if (Hyper::Target::convertHypervisorFromString(propVal) == 234 Hyper::Target::Hypervisor::PowerVM) 235 { 236 powerVmTarget = true; 237 } 238 239 log<level::DEBUG>( 240 fmt::format("Status::isPowerVM returning {}", powerVmTarget).c_str()); 241 242 return powerVmTarget; 243 } 244 245 // Get the requested power mode 246 SysPwrMode Status::getMode() 247 { 248 using namespace open_power::occ::powermode; 249 SysPwrMode pmode = SysPwrMode::NO_CHANGE; 250 251 // This will throw exception on failure 252 auto& bus = utils::getBus(); 253 auto service = utils::getService(PMODE_PATH, PMODE_INTERFACE); 254 auto method = bus.new_method_call(service.c_str(), PMODE_PATH, 255 "org.freedesktop.DBus.Properties", "Get"); 256 method.append(PMODE_INTERFACE, POWER_MODE_PROP); 257 auto reply = bus.call(method); 258 259 std::variant<std::string> stateEntryValue; 260 reply.read(stateEntryValue); 261 auto propVal = std::get<std::string>(stateEntryValue); 262 pmode = powermode::convertStringToMode(propVal); 263 264 log<level::DEBUG>( 265 fmt::format("Status::getMode returning {}", pmode).c_str()); 266 267 return pmode; 268 } 269 270 // Get the requested power mode 271 bool Status::getIPSParms(uint8_t& enterUtil, uint16_t& enterTime, 272 uint8_t& exitUtil, uint16_t& exitTime) 273 { 274 using namespace open_power::occ::powermode; 275 // Defaults: 276 bool ipsEnabled = false; // Disabled 277 enterUtil = 8; // Enter Utilization (8%) 278 enterTime = 240; // Enter Delay Time (240s) 279 exitUtil = 12; // Exit Utilization (12%) 280 exitTime = 10; // Exit Delay Time (10s) 281 282 std::map<std::string, std::variant<bool, uint8_t, uint64_t>> 283 ipsProperties{}; 284 285 // Get all IPS properties from DBus 286 try 287 { 288 auto& bus = utils::getBus(); 289 auto service = utils::getService(PIPS_PATH, PIPS_INTERFACE); 290 auto method = 291 bus.new_method_call(service.c_str(), PIPS_PATH, 292 "org.freedesktop.DBus.Properties", "GetAll"); 293 method.append(PIPS_INTERFACE); 294 auto reply = bus.call(method); 295 reply.read(ipsProperties); 296 } 297 catch (const sdbusplus::exception::exception& e) 298 { 299 log<level::ERR>( 300 fmt::format( 301 "Unable to read Idle Power Saver parameters so it will be disabled: {}", 302 e.what()) 303 .c_str()); 304 return ipsEnabled; 305 } 306 307 auto ipsEntry = ipsProperties.find(IPS_ENABLED_PROP); 308 if (ipsEntry != ipsProperties.end()) 309 { 310 ipsEnabled = std::get<bool>(ipsEntry->second); 311 } 312 else 313 { 314 log<level::ERR>( 315 fmt::format("Status::getIPSParms could not find property: {}", 316 IPS_ENABLED_PROP) 317 .c_str()); 318 } 319 320 ipsEntry = ipsProperties.find(IPS_ENTER_UTIL); 321 if (ipsEntry != ipsProperties.end()) 322 { 323 enterUtil = std::get<uint8_t>(ipsEntry->second); 324 } 325 else 326 { 327 log<level::ERR>( 328 fmt::format("Status::getIPSParms could not find property: {}", 329 IPS_ENTER_UTIL) 330 .c_str()); 331 } 332 333 ipsEntry = ipsProperties.find(IPS_ENTER_TIME); 334 if (ipsEntry != ipsProperties.end()) 335 { 336 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 337 enterTime = 338 std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 339 } 340 else 341 { 342 log<level::ERR>( 343 fmt::format("Status::getIPSParms could not find property: {}", 344 IPS_ENTER_TIME) 345 .c_str()); 346 } 347 348 ipsEntry = ipsProperties.find(IPS_EXIT_UTIL); 349 if (ipsEntry != ipsProperties.end()) 350 { 351 exitUtil = std::get<uint8_t>(ipsEntry->second); 352 } 353 else 354 { 355 log<level::ERR>( 356 fmt::format("Status::getIPSParms could not find property: {}", 357 IPS_EXIT_UTIL) 358 .c_str()); 359 } 360 361 ipsEntry = ipsProperties.find(IPS_EXIT_TIME); 362 if (ipsEntry != ipsProperties.end()) 363 { 364 std::chrono::milliseconds ms(std::get<uint64_t>(ipsEntry->second)); 365 exitTime = std::chrono::duration_cast<std::chrono::seconds>(ms).count(); 366 } 367 else 368 { 369 log<level::ERR>( 370 fmt::format("Status::getIPSParms could not find property: {}", 371 IPS_EXIT_TIME) 372 .c_str()); 373 } 374 375 if (enterUtil > exitUtil) 376 { 377 log<level::ERR>( 378 fmt::format( 379 "ERROR: Idle Power Saver Enter Utilization ({}%) is > Exit Utilization ({}%) - using Exit for both", 380 enterUtil, exitUtil) 381 .c_str()); 382 enterUtil = exitUtil; 383 } 384 385 return ipsEnabled; 386 } 387 388 // Special processing that needs to happen once the OCCs change to ACTIVE state 389 void Status::occsWentActive() 390 { 391 CmdStatus status = CmdStatus::SUCCESS; 392 393 status = sendModeChange(); 394 if (status != CmdStatus::SUCCESS) 395 { 396 log<level::ERR>( 397 fmt::format( 398 "Status::occsWentActive: OCC mode change failed with status {}", 399 status) 400 .c_str()); 401 } 402 403 status = sendIpsData(); 404 if (status != CmdStatus::SUCCESS) 405 { 406 log<level::ERR>( 407 fmt::format( 408 "Status::occsWentActive: Sending Idle Power Save Config data failed with status {}", 409 status) 410 .c_str()); 411 } 412 } 413 414 // Send mode change request to the master OCC 415 CmdStatus Status::sendModeChange() 416 { 417 CmdStatus status = CmdStatus::FAILURE; 418 419 if (!device.master()) 420 { 421 log<level::ERR>( 422 fmt::format( 423 "Status::sendModeChange: MODE CHANGE does not get sent to slave OCC{}", 424 instance) 425 .c_str()); 426 return status; 427 } 428 if (!isPowerVM()) 429 { 430 // Mode change is only supported on PowerVM systems 431 log<level::DEBUG>( 432 "Status::sendModeChange: MODE CHANGE does not get sent on non-PowerVM systems"); 433 return CmdStatus::SUCCESS; 434 } 435 436 const SysPwrMode newMode = getMode(); 437 438 if (VALID_POWER_MODE_SETTING(newMode)) 439 { 440 std::vector<std::uint8_t> cmd, rsp; 441 cmd.reserve(9); 442 cmd.push_back(uint8_t(CmdType::SET_MODE_AND_STATE)); 443 cmd.push_back(0x00); // Data Length (2 bytes) 444 cmd.push_back(0x06); 445 cmd.push_back(0x30); // Data (Version) 446 cmd.push_back(uint8_t(OccState::NO_CHANGE)); 447 cmd.push_back(uint8_t(newMode)); 448 cmd.push_back(0x00); // Mode Data (Freq Point) 449 cmd.push_back(0x00); // 450 cmd.push_back(0x00); // reserved 451 log<level::INFO>( 452 fmt::format( 453 "Status::sendModeChange: SET_MODE({}) command to OCC{} ({} bytes)", 454 newMode, instance, cmd.size()) 455 .c_str()); 456 status = occCmd.send(cmd, rsp); 457 if (status == CmdStatus::SUCCESS) 458 { 459 if (rsp.size() == 5) 460 { 461 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 462 { 463 log<level::ERR>( 464 fmt::format( 465 "Status::sendModeChange: SET MODE failed with status 0x{:02X}", 466 rsp[2]) 467 .c_str()); 468 dump_hex(rsp); 469 status = CmdStatus::FAILURE; 470 } 471 } 472 else 473 { 474 log<level::ERR>( 475 "Status::sendModeChange: INVALID SET MODE response"); 476 dump_hex(rsp); 477 status = CmdStatus::FAILURE; 478 } 479 } 480 else 481 { 482 if (status == CmdStatus::OPEN_FAILURE) 483 { 484 log<level::INFO>("Status::sendModeChange: OCC not active yet"); 485 status = CmdStatus::SUCCESS; 486 } 487 else 488 { 489 log<level::ERR>("Status::sendModeChange: SET_MODE FAILED!"); 490 } 491 } 492 } 493 else 494 { 495 log<level::ERR>( 496 fmt::format( 497 "Status::sendModeChange: Unable to set power mode to {}", 498 newMode) 499 .c_str()); 500 status = CmdStatus::FAILURE; 501 } 502 503 return status; 504 } 505 506 // Send Idle Power Saver config data to the master OCC 507 CmdStatus Status::sendIpsData() 508 { 509 CmdStatus status = CmdStatus::FAILURE; 510 511 if (!device.master()) 512 { 513 log<level::ERR>( 514 fmt::format( 515 "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent to slave OCC{}", 516 instance) 517 .c_str()); 518 return status; 519 } 520 if (!isPowerVM()) 521 { 522 // Idle Power Saver data is only supported on PowerVM systems 523 log<level::DEBUG>( 524 "Status::sendIpsData: SET_CFG_DATA[IPS] does not get sent on non-PowerVM systems"); 525 return CmdStatus::SUCCESS; 526 } 527 528 uint8_t enterUtil, exitUtil; 529 uint16_t enterTime, exitTime; 530 const bool ipsEnabled = 531 getIPSParms(enterUtil, enterTime, exitUtil, exitTime); 532 533 log<level::INFO>( 534 fmt::format( 535 "Idle Power Saver Parameters: enabled:{}, enter:{}%/{}s, exit:{}%/{}s", 536 ipsEnabled, enterUtil, enterTime, exitUtil, exitTime) 537 .c_str()); 538 539 std::vector<std::uint8_t> cmd, rsp; 540 cmd.reserve(12); 541 cmd.push_back(uint8_t(CmdType::SET_CONFIG_DATA)); 542 cmd.push_back(0x00); // Data Length (2 bytes) 543 cmd.push_back(0x09); // 544 cmd.push_back(0x11); // Config Format: IPS Settings 545 cmd.push_back(0x00); // Version 546 cmd.push_back(ipsEnabled ? 1 : 0); // IPS Enable 547 cmd.push_back(enterTime >> 8); // Enter Delay Time 548 cmd.push_back(enterTime & 0xFF); // 549 cmd.push_back(enterUtil); // Enter Utilization 550 cmd.push_back(exitTime >> 8); // Exit Delay Time 551 cmd.push_back(exitTime & 0xFF); // 552 cmd.push_back(exitUtil); // Exit Utilization 553 log<level::INFO>(fmt::format("Status::sendIpsData: SET_CFG_DATA[IPS] " 554 "command to OCC{} ({} bytes)", 555 instance, cmd.size()) 556 .c_str()); 557 status = occCmd.send(cmd, rsp); 558 if (status == CmdStatus::SUCCESS) 559 { 560 if (rsp.size() == 5) 561 { 562 if (RspStatus::SUCCESS != RspStatus(rsp[2])) 563 { 564 log<level::ERR>( 565 fmt::format( 566 "Status::sendIpsData: SET_CFG_DATA[IPS] failed with status 0x{:02X}", 567 rsp[2]) 568 .c_str()); 569 dump_hex(rsp); 570 status = CmdStatus::FAILURE; 571 } 572 } 573 else 574 { 575 log<level::ERR>( 576 "Status::sendIpsData: INVALID SET_CFG_DATA[IPS] response"); 577 dump_hex(rsp); 578 status = CmdStatus::FAILURE; 579 } 580 } 581 else 582 { 583 if (status == CmdStatus::OPEN_FAILURE) 584 { 585 log<level::INFO>("Status::sendIpsData: OCC not active yet"); 586 status = CmdStatus::SUCCESS; 587 } 588 else 589 { 590 log<level::ERR>("Status::sendIpsData: SET_CFG_DATA[IPS] FAILED!"); 591 } 592 } 593 594 return status; 595 } 596 597 #endif // POWER10 598 599 } // namespace occ 600 } // namespace open_power 601