1 /** 2 * Copyright © 2022 IBM Corporation 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #pragma once 17 18 #include "config_base.hpp" 19 #include "dbus_zone.hpp" 20 #include "fan.hpp" 21 22 #include <nlohmann/json.hpp> 23 #include <sdeventplus/event.hpp> 24 #include <sdeventplus/utility/timer.hpp> 25 26 #include <any> 27 #include <chrono> 28 #include <functional> 29 #include <map> 30 #include <memory> 31 #include <tuple> 32 33 namespace phosphor::fan::control::json 34 { 35 36 class Manager; 37 38 using json = nlohmann::json; 39 40 /* Dbus event timer */ 41 using Timer = sdeventplus::utility::Timer<sdeventplus::ClockId::Monotonic>; 42 43 /** 44 * @class Zone - Represents a configured fan control zone 45 * 46 * A zone object contains the configured attributes for a zone that groups 47 * a number of fans together to be under the same target control. These 48 * configuration attributes include, but are not limited to, the default ceiling 49 * of the fans within the zone, a default floor, the delay between increases, a 50 * decrease interval, and any profiles(OPTIONAL) the zone should be included in. 51 * 52 * (When no profile for a zone is given, the zone defaults to always exist) 53 * 54 */ 55 class Zone : public ConfigBase 56 { 57 public: 58 /* JSON file name for zones */ 59 static constexpr auto confFileName = "zones.json"; 60 61 Zone() = delete; 62 Zone(const Zone&) = delete; 63 Zone(Zone&&) = delete; 64 Zone& operator=(const Zone&) = delete; 65 Zone& operator=(Zone&&) = delete; 66 ~Zone() = default; 67 68 /** 69 * Constructor 70 * Parses and populates a zone from JSON object data 71 * 72 * @param[in] jsonObj - JSON object 73 * @param[in] event - sdeventplus event loop 74 * @param[in] mgr - Manager of this zone 75 */ 76 Zone(const json& jsonObj, const sdeventplus::Event& event, Manager* mgr); 77 78 /** 79 * @brief Get the poweron target 80 * 81 * Poweron target is the target the fans within this zone should be set to 82 * when the system is powered on. 83 * 84 * @return Poweron target of this zone 85 */ 86 inline const auto& getPoweronTarget() const 87 { 88 return _poweronTarget; 89 } 90 91 /** 92 * @brief Get the default ceiling 93 * 94 * Default ceiling is the highest target the fans within this zone is 95 * allowed to increase to. The zone's ceiling defaults to this unless 96 * changed by some configured event. 97 * 98 * @return Default ceiling of this zone 99 */ 100 inline const auto& getDefaultCeiling() const 101 { 102 return _defaultCeiling; 103 } 104 105 /** 106 * @brief Get the default floor 107 * 108 * The default floor is the lowest target the fans within this zone 109 * are allowed to decrease to. The zone's floor defaults to this 110 * unless changed by some configured event. 111 * 112 * @return Default floor 113 */ 114 inline const auto& getDefaultFloor() const 115 { 116 return _defaultFloor; 117 } 118 119 /** 120 * @brief Get the increase delay(OPTIONAL) 121 * 122 * The increase delay is the amount of time(in seconds) increases 123 * to a target are delayed before being made. The default is 0, which 124 * results in immediate increase requests when any events result in 125 * a change to the target. 126 * 127 * It is recommend a value other than 0 is configured, but that inherently 128 * depends on the fan controller and configured increases. 129 * 130 * @return Increase delay(in seconds) 131 */ 132 inline const auto& getIncDelay() const 133 { 134 return _incDelay; 135 } 136 137 /** 138 * @brief Get the decrease interval 139 * 140 * Decreases happen on a set interval when no requests for an increase 141 * in fan targets exists. This is the interval(in seconds) at which the fans 142 * within the zone are decreased if events exist that result in a target 143 * decrease. 144 * 145 * @return Decrease interval(in seconds) 146 */ 147 inline const auto& getDecInterval() const 148 { 149 return _decInterval; 150 } 151 152 /** 153 * @brief Get the current target of the zone 154 * 155 * @return - The current target of the zone 156 */ 157 inline const auto& getTarget() const 158 { 159 return _target; 160 } 161 162 /** 163 * @brief Get the target increase delta 164 * 165 * @return - The current target increase delta 166 */ 167 inline auto& getIncDelta() const 168 { 169 return _incDelta; 170 }; 171 172 /** 173 * @brief Get the target decrease delta 174 * 175 * @return - The current target decrease delta 176 */ 177 inline auto& getDecDelta() const 178 { 179 return _decDelta; 180 }; 181 182 /** 183 * @brief Get the manager of the zone 184 * 185 * @return - The manager of the zone 186 */ 187 inline auto* getManager() const 188 { 189 return _manager; 190 } 191 192 /** 193 * @brief Enable the zone 194 * 195 * Performs the necessary tasks to enable the zone such as restoring any 196 * dbus property states(if persisted), starting the decrement timer, etc... 197 */ 198 void enable(); 199 200 /** 201 * @brief Add a fan object to the zone 202 * 203 * @param[in] fan - Unique pointer to a fan object that will be moved into 204 * the zone 205 * 206 * Adds a fan object to the list of fans that make up the zone by moving the 207 * fan object into the list. 208 */ 209 void addFan(std::unique_ptr<Fan> fan); 210 211 /** 212 * Sets all fans in the zone to the target given when the zone is active 213 * 214 * @param[in] target - Target for fans 215 */ 216 void setTarget(uint64_t target); 217 218 /** 219 * Add a target lock for the specified fan. 220 * 221 * @param[in] fname - Fan to request/add the target lock 222 * @param[in] target - Target to register 223 */ 224 void lockFanTarget(const std::string& fname, uint64_t target); 225 226 /** 227 * Remove target lock for specific fan. 228 * 229 * @param[in] fname - Fan to remove lock from 230 * @param[in] target- Target to de-register 231 */ 232 void unlockFanTarget(const std::string& fname, uint64_t target); 233 234 /** 235 * Sets and holds all fans in the zone to the target given or releases a 236 * target hold resulting in the fans being held at the highest remaining 237 * hold target if other hold targets had been requested. When no hold 238 * targets exist, the zone returns to being active. 239 * 240 * @param[in] ident - Unique identifier for a target hold 241 * @param[in] target - Target to hold fans at 242 * @param[in] hold - Whether to hold(true) or release(false) a target hold 243 */ 244 void setTargetHold(const std::string& ident, uint64_t target, bool hold); 245 246 /** 247 * @brief Set the floor to the given target and increase target to the floor 248 * when the target is below the floor value when floor changes are allowed. 249 * 250 * @param[in] target - Target to set the floor to 251 */ 252 void setFloor(uint64_t target); 253 254 /** 255 * Sets and holds the floor of the zone to the target given or releases a 256 * floor hold resulting in the fans being held at the highest remaining 257 * hold target if other floor hold targets had been requested. When no hold 258 * targets exist, the floor gets set to the default floor value. 259 * 260 * @param[in] ident - Unique identifier for a floor hold 261 * @param[in] target - Floor value 262 * @param[in] hold - Whether to hold(true) or release(false) a hold 263 */ 264 void setFloorHold(const std::string& ident, uint64_t target, bool hold); 265 266 /** 267 * @brief Says if the passed in identity has a floor hold 268 * 269 * @param ident - The identity key to check 270 * @return bool - If it has a floor hold or not 271 */ 272 inline bool hasFloorHold(const std::string& ident) const 273 { 274 return _floorHolds.contains(ident); 275 } 276 277 /** 278 * @brief Set the default floor to the given value 279 * 280 * @param[in] value - Value to set the default floor to 281 */ 282 inline void setDefaultFloor(uint64_t value) 283 { 284 _defaultFloor = value; 285 } 286 287 /** 288 * @brief Sets the floor change allowed state 289 * 290 * @param[in] ident - An identifier that affects floor changes 291 * @param[in] isAllow - Allow state according to the identifier 292 */ 293 inline void setFloorChangeAllow(const std::string& ident, bool isAllow) 294 { 295 _floorChange[ident] = isAllow; 296 } 297 298 /** 299 * @brief Sets the decrease allowed state of a group 300 * 301 * @param[in] ident - An identifier that affects speed decreases 302 * @param[in] isAllow - Allow state according to the identifier 303 */ 304 inline void setDecreaseAllow(const std::string& ident, bool isAllow) 305 { 306 _decAllowed[ident] = isAllow; 307 } 308 309 /** 310 * @brief Calculate the requested target from the given delta and increases 311 * the fans, not going above the ceiling. 312 * 313 * @param[in] targetDelta - The delta to increase the target by 314 */ 315 void requestIncrease(uint64_t targetDelta); 316 317 /** 318 * @brief Callback function for the increase timer that delays 319 * processing of requested target increases while fans are increasing 320 */ 321 void incTimerExpired(); 322 323 /** 324 * @brief Calculate the lowest requested decrease target from the given 325 * delta within a decrease interval. 326 * 327 * @param[in] targetDelta - The delta to decrease the target by 328 */ 329 void requestDecrease(uint64_t targetDelta); 330 331 /** 332 * @brief Callback function for the decrease timer that processes any 333 * requested target decreases if allowed 334 */ 335 void decTimerExpired(); 336 337 /** 338 * @brief Set the requested target base to be used as the target to base a 339 * new requested target from 340 * 341 * @param[in] targetBase - Base target value to use 342 */ 343 inline void setRequestTargetBase(uint64_t targetBase) 344 { 345 _requestTargetBase = targetBase; 346 }; 347 348 /** 349 * @brief Set a property to be persisted 350 * 351 * @param[in] intf - Interface containing property 352 * @param[in] prop - Property to be persisted 353 */ 354 void setPersisted(const std::string& intf, const std::string& prop); 355 356 /** 357 * @brief Is the property persisted 358 * 359 * @param[in] intf - Interface containing property 360 * @param[in] prop - Property to check if persisted 361 * 362 * @return - True if property is to be persisted, false otherwise 363 */ 364 bool isPersisted(const std::string& intf, const std::string& prop) const; 365 366 /** 367 * @brief A handler function to set/update a property on a zone 368 * @details Sets or updates a zone's dbus property to the given value using 369 * the provided base dbus object's set property function 370 * 371 * @param[in] intf - Interface on zone object 372 * @param[in] prop - Property on interface 373 * @param[in] func - Zone dbus object's set property function pointer 374 * @param[in] value - Value to set property to 375 * @param[in] persist - Persist property value or not 376 * 377 * @return Lambda function 378 * A lambda function to set/update the zone dbus object's property 379 */ 380 template <typename T> 381 static auto setProperty(const char* intf, const char* prop, 382 T (DBusZone::*func)(T), T&& value, bool persist) 383 { 384 return [=, value = std::forward<T>(value)](DBusZone& dbusZone, 385 Zone& zone) { 386 (dbusZone.*func)(value); 387 if (persist) 388 { 389 zone.setPersisted(intf, prop); 390 } 391 }; 392 } 393 394 /** 395 * @brief A handler function to set/update a zone's dbus property's persist 396 * state 397 * @details Sets or updates a zone's dbus property's persist state where the 398 * value of the property is to be left unchanged 399 * 400 * @param[in] intf - Interface on zone object 401 * @param[in] prop - Property on interface 402 * @param[in] persist - Persist property value or not 403 * 404 * @return Lambda function 405 * A lambda function to set/update the zone's dbus object's property's 406 * persist state 407 */ 408 static auto setPropertyPersist(const char* intf, const char* prop, 409 bool persist) 410 { 411 return [=](DBusZone&, Zone& zone) { 412 if (persist) 413 { 414 zone.setPersisted(intf, prop); 415 } 416 }; 417 } 418 419 /** 420 * @brief Dump the attributes into JSON 421 * 422 * @return json - JSON object with the attributes 423 */ 424 json dump() const; 425 426 private: 427 /* The zone's associated dbus object */ 428 std::unique_ptr<DBusZone> _dbusZone; 429 430 /* The zone's manager */ 431 Manager* _manager; 432 433 /* The zone's poweron target value for fans */ 434 uint64_t _poweronTarget; 435 436 /* The zone's default ceiling value for fans */ 437 uint64_t _defaultCeiling; 438 439 /* The zone's default floor value for fans */ 440 uint64_t _defaultFloor; 441 442 /* Zone's increase delay(in seconds) (OPTIONAL) */ 443 std::chrono::seconds _incDelay; 444 445 /* Zone's decrease interval(in seconds) (OPTIONAL) */ 446 std::chrono::seconds _decInterval; 447 448 /* The floor target to not go below */ 449 uint64_t _floor; 450 451 /* Target for this zone */ 452 uint64_t _target; 453 454 /* Zone increase delta */ 455 uint64_t _incDelta; 456 457 /* Zone decrease delta */ 458 uint64_t _decDelta; 459 460 /* The ceiling target to not go above */ 461 uint64_t _ceiling; 462 463 /* Requested target base */ 464 uint64_t _requestTargetBase; 465 466 /* Map of whether floor changes are allowed by a string identifier */ 467 std::map<std::string, bool> _floorChange; 468 469 /* Map of controlling decreases allowed by a string identifer */ 470 std::map<std::string, bool> _decAllowed; 471 472 /* Map of interfaces to persisted properties the zone hosts*/ 473 std::map<std::string, std::vector<std::string>> _propsPersisted; 474 475 /* Automatic fan control active state */ 476 bool _isActive; 477 478 /* The target increase timer object */ 479 Timer _incTimer; 480 481 /* The target decrease timer object */ 482 Timer _decTimer; 483 484 /* Map of target holds by a string identifier */ 485 std::unordered_map<std::string, uint64_t> _targetHolds; 486 487 /* Map of floor holds by a string identifier */ 488 std::unordered_map<std::string, uint64_t> _floorHolds; 489 490 /* Interface to property mapping of their associated set property handler 491 * function */ 492 static const std::map< 493 std::string, 494 std::map<std::string, std::function<std::function<void( 495 DBusZone&, Zone&)>(const json&, bool)>>> 496 _intfPropHandlers; 497 498 /* List of fans included in this zone */ 499 std::vector<std::unique_ptr<Fan>> _fans; 500 501 /* List of configured interface set property functions */ 502 std::vector<std::function<void(DBusZone&, Zone&)>> _propInitFunctions; 503 504 /** 505 * @brief Parse and set the zone's poweron target value 506 * 507 * @param[in] jsonObj - JSON object for the zone 508 * 509 * Sets the poweron target value for the zone from the JSON configuration 510 * object 511 */ 512 void setPowerOnTarget(const json& jsonObj); 513 514 /** 515 * @brief Parse and set the interfaces served by the zone(OPTIONAL) 516 * 517 * @param[in] jsonObj - JSON object for the zone 518 * 519 * Constructs any zone interface handler functions for interfaces that the 520 * zone serves which contains the interface's property's value and 521 * persistency state (OPTIONAL). A property's "persist" state is defaulted 522 * to not be persisted when not given. 523 */ 524 void setInterfaces(const json& jsonObj); 525 526 /** 527 * @brief Get the request target base if defined, otherwise the the current 528 * target is returned 529 * 530 * @return - The request target base or current target 531 */ 532 inline auto getRequestTargetBase() const 533 { 534 return (_requestTargetBase != 0) ? _requestTargetBase : _target; 535 }; 536 }; 537 538 /** 539 * Properties of interfaces supported by the zone configuration 540 */ 541 namespace zone::property 542 { 543 544 /** 545 * @brief "Supported" property on the "xyz.openbmc_project.Control.ThermalMode" 546 * interface parser. Also creates the handler function for the Zone dbus object 547 * to initialize the property according to what's parsed from the configuration. 548 * 549 * @param[in] jsonObj - JSON object for the "Supported" property 550 * @param[in] persist - Whether to persist the value or not 551 * 552 * @return Zone dbus object's set property function for the "Supported" property 553 */ 554 std::function<void(DBusZone&, Zone&)> supported(const json& jsonObj, 555 bool persist); 556 557 /** 558 * @brief "Current" property on the "xyz.openbmc_project.Control.ThermalMode" 559 * interface parser. Also creates the handler function for the Zone dbus object 560 * to initialize the property according to what's parsed from the configuration. 561 * 562 * @param[in] jsonObj - JSON object for the "Current" property 563 * @param[in] persist - Whether to persist the value or not 564 * 565 * @return Zone dbus object's set property function for the "Current" property 566 */ 567 std::function<void(DBusZone&, Zone&)> current(const json& jsonObj, 568 bool persist); 569 570 } // namespace zone::property 571 572 } // namespace phosphor::fan::control::json 573