blob: db2c315b788940391af226f5ba32c054f62ac32a [file] [log] [blame]
/**
* Copyright © 2025 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "system.hpp"
#include <exception>
namespace phosphor::power::sequencer
{
void System::initializeMonitoring(Services& services)
{
// Initialize status monitoring in all the chassis
for (auto& curChassis : chassis)
{
curChassis->initializeMonitoring(services);
}
isMonitoringInitialized = true;
}
void System::setPowerState(PowerState newPowerState, Services& services)
{
verifyMonitoringInitialized();
verifyCanSetPowerState(newPowerState);
// Get chassis that can be set to the new power state
auto chassisToSet = getChassisForNewPowerState(newPowerState, services);
if (chassisToSet.empty())
{
throw std::runtime_error{std::format(
"Unable to set system to state {}: No chassis can be set to that state",
PowerInterface::toString(newPowerState))};
}
// Set new power state
powerState = newPowerState;
// Save list of chassis selected for current power on/off attempt
selectedChassis = chassisToSet;
// Set power state for selected chassis
for (auto& curChassis : chassis)
{
if (selectedChassis.contains(curChassis->getNumber()))
{
try
{
curChassis->setPowerState(newPowerState, services);
}
catch (const std::exception& e)
{
services.logErrorMsg(std::format(
"Unable to set chassis {} to state {}: {}",
curChassis->getNumber(),
PowerInterface::toString(newPowerState), e.what()));
}
}
}
}
void System::monitor(Services& services)
{
verifyMonitoringInitialized();
// Monitor the status of all chassis, including those not selected for
// current power on/off attempt. All chassis need to react to D-Bus status
// changes.
for (auto& curChassis : chassis)
{
try
{
curChassis->monitor(services);
}
catch (const std::exception& e)
{
services.logErrorMsg(
std::format("Unable to monitor chassis {}: {}",
curChassis->getNumber(), e.what()));
}
}
// Set initial set of chassis selected for power on/off if needed
setInitialSelectedChassisIfNeeded();
// Set the system power good based on the chassis power good values
setPowerGood();
// Set initial system power state based on system power good if needed
setInitialPowerStateIfNeeded();
}
std::set<size_t> System::getChassisForNewPowerState(PowerState newPowerState,
Services& services)
{
std::set<size_t> chassisForState;
for (auto& curChassis : chassis)
{
auto [canSet, reason] = curChassis->canSetPowerState(newPowerState);
if (canSet)
{
chassisForState.emplace(curChassis->getNumber());
}
else
{
services.logInfoMsg(
std::format("Unable to set chassis {} to state {}: {}",
curChassis->getNumber(),
PowerInterface::toString(newPowerState), reason));
}
}
return chassisForState;
}
void System::setInitialSelectedChassisIfNeeded()
{
if (!selectedChassis.empty())
{
// Selected set of chassis is already defined
return;
}
// Get the set of chassis that are powered on and off. Ignore chassis with
// an invalid status like not present.
std::set<size_t> chassisOn, chassisOff;
for (auto& curChassis : chassis)
{
try
{
size_t chassisNumber = curChassis->getNumber();
if (curChassis->isPresent() && curChassis->isAvailable() &&
curChassis->isInputPowerGood())
{
if (curChassis->getPowerGood() == PowerGood::on)
{
chassisOn.emplace(chassisNumber);
}
else
{
chassisOff.emplace(chassisNumber);
}
}
}
catch (...)
{
// Ignore errors; chassis status/power good might not be available
}
}
if (chassisOn.empty())
{
// No chassis with a valid status is powered on. Assume last requested
// power state was off. Use powered off chassis as initial selected set.
selectedChassis = chassisOff;
}
else
{
// At least one chassis with a valid status is powered on. Assume last
// requested power state was on. Use powered on chassis as initial
// selected set.
selectedChassis = chassisOn;
}
}
void System::setPowerGood()
{
// Return if there are no chassis selected for a power on/off attempt
if (selectedChassis.empty())
{
return;
}
// Count the number of selected chassis with power good on and off
size_t powerGoodOnCount{0}, powerGoodOffCount{0};
for (auto& curChassis : chassis)
{
if (selectedChassis.contains(curChassis->getNumber()))
{
try
{
if (curChassis->getPowerGood() == PowerGood::on)
{
++powerGoodOnCount;
}
else
{
++powerGoodOffCount;
}
}
catch (...)
{
// Chassis power good might not be available
}
}
}
if (powerGoodOnCount == selectedChassis.size())
{
// All selected chassis are on; set system power good to on
powerGood = PowerGood::on;
}
else if (powerGoodOffCount == selectedChassis.size())
{
// All selected chassis are off; set system power good to off
powerGood = PowerGood::off;
}
}
void System::setInitialPowerStateIfNeeded()
{
if (!powerState)
{
if (powerGood)
{
if (powerGood == PowerGood::off)
{
powerState = PowerState::off;
}
else
{
powerState = PowerState::on;
}
}
}
}
} // namespace phosphor::power::sequencer