blob: dfa6efb97783fedab8696f82f1a7de69b3a5c32d [file] [log] [blame]
#pragma once
#include "fan.hpp"
#include "sdbusplus.hpp"
#include "types.hpp"
#include "xyz/openbmc_project/Control/ThermalMode/server.hpp"
#include <sdbusplus/bus.hpp>
#include <sdeventplus/event.hpp>
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cmath>
#include <optional>
#include <vector>
namespace phosphor
{
namespace fan
{
namespace control
{
using ThermalObject = sdbusplus::server::object_t<
sdbusplus::xyz::openbmc_project::Control::server::ThermalMode>;
/**
* The mode fan control will run in:
* - init - only do the initialization steps
* - control - run normal control algorithms
*/
enum class Mode
{
init,
control
};
/**
* @class Represents a fan control zone, which is a group of fans
* that behave the same.
*/
class Zone : public ThermalObject
{
public:
Zone() = delete;
Zone(const Zone&) = delete;
Zone(Zone&&) = delete;
Zone& operator=(const Zone&) = delete;
Zone& operator=(Zone&&) = delete;
~Zone() = default;
/**
* Constructor
* Creates the appropriate fan objects based on
* the zone definition data passed in.
*
* @param[in] mode - mode of fan control
* @param[in] bus - the dbus object
* @param[in] path - object instance path
* @param[in] event - Event loop reference
* @param[in] def - the fan zone definition data
*/
Zone(Mode mode, sdbusplus::bus_t& bus, const std::string& path,
const sdeventplus::Event& event, const ZoneDefinition& def);
/**
* @brief Get the zone's bus
*
* @return The bus used by the zone
*/
inline auto& getBus()
{
return _bus;
}
/**
* @brief Get the zone's path
*
* @return The path of this zone
*/
inline auto& getPath()
{
return _path;
}
/**
* @brief Get the zone's hosted interfaces
*
* @return The interfaces hosted by this zone
*/
inline auto& getIfaces()
{
return _ifaces;
}
/**
* Sets all fans in the zone to the speed
* passed in when the zone is active
*
* @param[in] speed - the fan speed
*/
void setSpeed(uint64_t speed);
/**
* Sets the zone to full speed regardless of zone's active state
*/
void setFullSpeed();
/**
* @brief Sets the automatic fan control allowed active state
*
* @param[in] group - A group that affects the active state
* @param[in] isActiveAllow - Active state according to group
*/
void setActiveAllow(const Group* group, bool isActiveAllow);
/**
* @brief Sets the floor change allowed state
*
* @param[in] group - A group that affects floor changes
* @param[in] isAllow - Allow state according to group
*/
inline void setFloorChangeAllow(const Group* group, bool isAllow)
{
_floorChange[*(group)] = isAllow;
}
/**
* @brief Sets the decrease allowed state of a group
*
* @param[in] group - A group that affects speed decreases
* @param[in] isAllow - Allow state according to group
*/
inline void setDecreaseAllow(const Group* group, bool isAllow)
{
_decAllowed[*(group)] = isAllow;
}
/**
* @brief Sets a given object's event data for a property on this zone
*
* @param[in] object - Name of the object containing the property
* @param[in] interface - Interface name containing the property
* @param[in] property - Property name
* @param[in] data - Property value
*/
inline void setObjectData(const std::string& object,
const std::string& interface,
const std::string& property, EventData* data)
{
_objects[object][interface][property] = data;
}
/**
* @brief Sets a given object's property value
*
* @param[in] object - Name of the object containing the property
* @param[in] interface - Interface name containing the property
* @param[in] property - Property name
* @param[in] value - Property value
*/
template <typename T>
void setPropertyValue(const char* object, const char* interface,
const char* property, T value)
{
_properties[object][interface][property] = value;
}
/**
* @brief Sets a given object's property value
*
* @param[in] object - Name of the object containing the property
* @param[in] interface - Interface name containing the property
* @param[in] property - Property name
* @param[in] value - Property value
*/
template <typename T>
void setPropertyValue(const std::string& object,
const std::string& interface,
const std::string& property, T value)
{
_properties[object][interface][property] = value;
}
/**
* @brief Get the value of an object's property
*
* @param[in] object - Name of the object containing the property
* @param[in] interface - Interface name containing the property
* @param[in] property - Property name
*
* @return - The property value
*/
template <typename T>
inline auto getPropertyValue(const std::string& object,
const std::string& interface,
const std::string& property)
{
return std::get<T>(_properties.at(object).at(interface).at(property));
}
/**
* @brief Get the object's property variant
*
* @param[in] object - Name of the object containing the property
* @param[in] interface - Interface name containing the property
* @param[in] property - Property name
*
* @return - The property variant
*/
inline auto getPropValueVariant(const std::string& object,
const std::string& interface,
const std::string& property)
{
return _properties.at(object).at(interface).at(property);
}
/**
* @brief Get a property's value after applying a set of visitors
* to translate the property value's type change to keep from
* affecting the configured use of the property.
*
* @param[in] intf = Interface name containing the property
* @param[in] prop = Property name
* @param[in] variant = Variant containing the property's value from
* the supported property types.
*/
template <typename T>
inline auto getPropertyValueVisitor(const char* intf, const char* prop,
PropertyVariantType& variant)
{
// Handle the transition of the dbus sensor value type from
// int64 to double which also removed the scale property.
// https://gerrit.openbmc-project.xyz/11739
if (strcmp(intf, "xyz.openbmc_project.Sensor.Value") == 0 &&
strcmp(prop, "Value") == 0)
{
// Use 'optional' variable to determine if the sensor value
// is set within the visitor based on the supported types.
// A non-supported type configured will assert.
std::optional<T> value;
std::visit(
[&value](auto&& val) {
// If the type configured is int64, but the sensor value
// property's type is double, scale it by 1000 and return
// the value as an int64 as configured.
using V = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, int64_t> &&
std::is_same_v<V, double>)
{
val = val * 1000;
value = std::lround(val);
}
// If the type configured matches the sensor value
// property's type, just return the value as its
// given type.
else if constexpr (std::is_same_v<T, V>)
{
value = val;
}
},
variant);
// Unable to return Sensor Value property
// as given type configured.
assert(value);
return value.value();
}
// Default to return the property's value by the data type
// configured, applying no visitors to the variant.
return std::get<T>(variant);
}
/**
* @brief Remove an object's interface
*
* @param[in] object - Name of the object with the interface
* @param[in] interface - Interface name to remove
*/
inline void removeObjectInterface(const char* object, const char* interface)
{
auto it = _properties.find(object);
if (it != std::end(_properties))
{
_properties[object].erase(interface);
}
}
/**
* @brief Remove a service associated to a group
*
* @param[in] group - Group associated with service
* @param[in] name - Service name to remove
*/
void removeService(const Group* group, const std::string& name);
/**
* @brief Set or update a service name owner in use
*
* @param[in] group - Group associated with service
* @param[in] name - Service name
* @param[in] hasOwner - Whether the service is owned or not
*/
void setServiceOwner(const Group* group, const std::string& name,
const bool hasOwner);
/**
* @brief Set or update all services for a group
*
* @param[in] group - Group to get service names for
*/
void setServices(const Group* group);
/**
* @brief Get the group's list of service names
*
* @param[in] group - Group to get service names for
*
* @return - The list of service names
*/
inline auto getGroupServices(const Group* group)
{
return _services.at(*group);
}
/**
* @brief Initialize a set speed event properties and actions
*
* @param[in] event - Set speed event
*/
void initEvent(const SetSpeedEvent& event);
/**
* @brief Removes all the set speed event properties and actions
*
* @param[in] event - Set speed event
*/
void removeEvent(const SetSpeedEvent& event);
/**
* @brief Get the default floor speed
*
* @return - The defined default floor speed
*/
inline auto getDefFloor()
{
return _defFloorSpeed;
}
/**
* @brief Set the default floor
*
* @param[in] speed - Speed to set the default floor to
*/
inline void setDefFloor(uint64_t speed)
{
_defFloorSpeed = speed;
}
/**
* @brief Get the ceiling speed
*
* @return - The current ceiling speed
*/
inline auto& getCeiling() const
{
return _ceilingSpeed;
}
/**
* @brief Set the ceiling speed to the given speed
*
* @param[in] speed - Speed to set the ceiling to
*/
inline void setCeiling(uint64_t speed)
{
_ceilingSpeed = speed;
}
/**
* @brief Swaps the ceiling key value with what's given and
* returns the value that was swapped.
*
* @param[in] keyValue - New ceiling key value
*
* @return - Ceiling key value prior to swapping
*/
inline auto swapCeilingKeyValue(int64_t keyValue)
{
std::swap(_ceilingKeyValue, keyValue);
return keyValue;
}
/**
* @brief Get the increase speed delta
*
* @return - The current increase speed delta
*/
inline auto& getIncSpeedDelta() const
{
return _incSpeedDelta;
}
/**
* @brief Get the decrease speed delta
*
* @return - The current decrease speed delta
*/
inline auto& getDecSpeedDelta() const
{
return _decSpeedDelta;
}
/**
* @brief Set the floor speed to the given speed and increase target
* speed to the floor when target is below floor where floor changes
* are allowed.
*
* @param[in] speed - Speed to set the floor to
*/
void setFloor(uint64_t speed);
/**
* @brief Set the requested speed base to be used as the speed to
* base a new requested speed target from
*
* @param[in] speedBase - Base speed value to use
*/
inline void setRequestSpeedBase(uint64_t speedBase)
{
_requestSpeedBase = speedBase;
}
/**
* @brief Calculate the requested target speed from the given delta
* and increase the fan speeds, not going above the ceiling.
*
* @param[in] targetDelta - The delta to increase the target speed by
*/
void requestSpeedIncrease(uint64_t targetDelta);
/**
* @brief Calculate the requested target speed from the given delta
* and increase the fan speeds, not going above the ceiling.
*
* @param[in] targetDelta - The delta to increase the target speed by
*/
void requestSpeedDecrease(uint64_t targetDelta);
/**
* @brief Callback function for the increase timer that delays
* processing of requested speed increases while fans are increasing
*/
void incTimerExpired();
/**
* @brief Callback function for the decrease timer that processes any
* requested speed decreases if allowed
*/
void decTimerExpired();
/**
* @brief Get the event loop used with this zone's timers
*
* @return - The event loop for timers
*/
inline auto& getEventLoop()
{
return _eventLoop;
}
/**
* @brief Remove the given signal event
*
* @param[in] seIter - Iterator pointing to the signal event to remove
*/
inline void removeSignal(std::vector<SignalEvent>::iterator& seIter)
{
std::get<signalEventDataPos>(*seIter).reset();
if (std::get<signalMatchPos>(*seIter) != nullptr)
{
std::get<signalMatchPos>(*seIter).reset();
}
}
/**
* @brief Get the list of timer events
*
* @return - List of timer events
*/
inline auto& getTimerEvents()
{
return _timerEvents;
}
/**
* @brief Find the first instance of a timer event
*
* @param[in] eventGroup - Group associated with a timer
* @param[in] eventActions - List of actions associated with a timer
* @param[in] eventTimers - List of timers to find the timer in
*
* @return - Iterator to the timer event
*/
std::vector<TimerEvent>::iterator findTimer(
const Group& eventGroup, const std::vector<Action>& eventActions,
std::vector<TimerEvent>& eventTimers);
/**
* @brief Add a timer to the list of timer based events
*
* @param[in] name - Event name associated with timer
* @param[in] group - Group associated with a timer
* @param[in] actions - List of actions associated with a timer
* @param[in] tConf - Configuration for the new timer
*/
void addTimer(const std::string& name, const Group& group,
const std::vector<Action>& actions, const TimerConf& tConf);
/**
* @brief Callback function for event timers that processes the given
* actions for a group
*
* @param[in] eventGroup - Group to process actions on
* @param[in] eventActions - List of event actions to run
*/
void timerExpired(const Group& eventGroup,
const std::vector<Action>& eventActions);
/**
* @brief Get the service for a given path and interface from cached
* dataset and add a service that's not found
*
* @param[in] path - Path to get service for
* @param[in] intf - Interface to get service for
*
* @return - The service name
*/
const std::string& getService(const std::string& path,
const std::string& intf);
/**
* @brief Add a set of services for a path and interface
* by retrieving all the path subtrees to the given depth
* from root for the interface
*
* @param[in] path - Path to add services for
* @param[in] intf - Interface to add services for
* @param[in] depth - Depth of tree traversal from root path
*
* @return - The associated service to the given path and interface
* or empty string for no service found
*/
const std::string& addServices(const std::string& path,
const std::string& intf, int32_t depth);
/**
* @brief Dbus signal change callback handler
*
* @param[in] msg - Expanded sdbusplus message data
* @param[in] eventData - The single event's data
*/
void handleEvent(sdbusplus::message_t& msg, const EventData* eventData);
/**
* @brief Add a signal to the list of signal based events
*
* @param[in] name - Event name
* @param[in] data - Event data for signal
* @param[in] match - Subscribed signal match
*/
inline void addSignal(const std::string& name,
std::unique_ptr<EventData>&& data,
std::unique_ptr<sdbusplus::bus::match_t>&& match)
{
_signalEvents[name].emplace_back(std::move(data), std::move(match));
}
/**
* @brief Set a property to be persisted
*
* @param[in] intf - Interface containing property
* @param[in] prop - Property to be persisted
*/
inline void setPersisted(const std::string& intf, const std::string& prop)
{
_persisted[intf].emplace_back(prop);
}
/**
* @brief Get persisted property
*
* @param[in] intf - Interface containing property
* @param[in] prop - Property persisted
*
* @return - True if property is to be persisted, false otherwise
*/
auto getPersisted(const std::string& intf, const std::string& prop);
/**
* @brief Get a property value from the zone object or the bus when
* the property requested is not on the zone object
*
* @param[in] path - Path of object
* @param[in] intf - Object interface
* @param[in] prop - Object property
*
* @return - Property's value
*/
template <typename T>
auto getPropertyByName(const std::string& path, const std::string& intf,
const std::string& prop)
{
T value;
auto pathIter = _objects.find(path);
if (pathIter != _objects.end())
{
auto intfIter = pathIter->second.find(intf);
if (intfIter != pathIter->second.end())
{
if (intf == "xyz.openbmc_project.Control.ThermalMode")
{
auto var = ThermalMode::getPropertyByName(prop);
// Use visitor to determine if requested property
// type(T) is available on this interface and read it
std::visit(
[&value](auto&& val) {
using V = std::decay_t<decltype(val)>;
if constexpr (std::is_same_v<T, V>)
{
value = val;
}
},
var);
return value;
}
}
}
// Retrieve the property's value applying any visitors necessary
auto service = getService(path, intf);
auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>(
_bus, service, path, intf, prop);
value = getPropertyValueVisitor<T>(intf.c_str(), prop.c_str(), variant);
return value;
}
/**
* @brief Overridden thermal object's set 'Current' property function
*
* @param[in] value - Value to set 'Current' to
*
* @return - The updated value of the 'Current' property
*/
virtual std::string current(std::string value);
private:
/**
* The dbus object
*/
sdbusplus::bus_t& _bus;
/**
* Zone object path
*/
const std::string _path;
/**
* Zone supported interfaces
*/
const std::vector<std::string> _ifaces;
/**
* Full speed for the zone
*/
const uint64_t _fullSpeed;
/**
* The zone number
*/
const size_t _zoneNum;
/**
* The default floor speed for the zone
*/
uint64_t _defFloorSpeed;
/**
* The default ceiling speed for the zone
*/
const uint64_t _defCeilingSpeed;
/**
* The floor speed to not go below
*/
uint64_t _floorSpeed = _defFloorSpeed;
/**
* The ceiling speed to not go above
*/
uint64_t _ceilingSpeed = _defCeilingSpeed;
/**
* The previous sensor value for calculating the ceiling
*/
int64_t _ceilingKeyValue = 0;
/**
* Automatic fan control active state
*/
bool _isActive = true;
/**
* Target speed for this zone
*/
uint64_t _targetSpeed = _fullSpeed;
/**
* Speed increase delta
*/
uint64_t _incSpeedDelta = 0;
/**
* Speed decrease delta
*/
uint64_t _decSpeedDelta = 0;
/**
* Requested speed base
*/
uint64_t _requestSpeedBase = 0;
/**
* Speed increase delay in seconds
*/
std::chrono::seconds _incDelay;
/**
* Speed decrease interval in seconds
*/
std::chrono::seconds _decInterval;
/**
* The increase timer object
*/
Timer _incTimer;
/**
* The decrease timer object
*/
Timer _decTimer;
/**
* Event loop used on set speed event timers
*/
sdeventplus::Event _eventLoop;
/**
* The vector of fans in this zone
*/
std::vector<std::unique_ptr<Fan>> _fans;
/**
* @brief Map of object property values
*/
std::map<std::string,
std::map<std::string, std::map<std::string, PropertyVariantType>>>
_properties;
/**
* @brief Map of zone objects
*/
std::map<std::string,
std::map<std::string, std::map<std::string, EventData*>>>
_objects;
/**
* @brief Map of interfaces to persisted properties
*/
std::map<std::string, std::vector<std::string>> _persisted;
/**
* @brief Map of active fan control allowed by groups
*/
std::map<const Group, bool> _active;
/**
* @brief Map of floor change allowed by groups
*/
std::map<const Group, bool> _floorChange;
/**
* @brief Map of groups controlling decreases allowed
*/
std::map<const Group, bool> _decAllowed;
/**
* @brief Map of group service names
*/
std::map<const Group, std::vector<Service>> _services;
/**
* @brief Map tree of paths to services of interfaces
*/
std::map<std::string, std::map<std::string, std::vector<std::string>>>
_servTree;
/**
* @brief List of signal event arguments and Dbus matches
* for callbacks per event name
*/
std::map<std::string, std::vector<SignalEvent>> _signalEvents;
/**
* @brief List of timers per event name
*/
std::map<std::string, std::vector<TimerEvent>> _timerEvents;
/**
* @brief Save the thermal control current mode property
* to persisted storage
*/
void saveCurrentMode();
/**
* @brief Restore persisted thermal control current mode property
* value, setting the mode to "Default" otherwise
*/
void restoreCurrentMode();
/**
* @brief Get the request speed base if defined, otherwise the
* the current target speed is returned
*
* @return - The request speed base or current target speed
*/
inline auto getRequestSpeedBase() const
{
return (_requestSpeedBase != 0) ? _requestSpeedBase : _targetSpeed;
}
};
} // namespace control
} // namespace fan
} // namespace phosphor