1 #pragma once 2 #include "fan.hpp" 3 #include "sdbusplus.hpp" 4 #include "types.hpp" 5 #include "xyz/openbmc_project/Control/ThermalMode/server.hpp" 6 7 #include <sdbusplus/bus.hpp> 8 #include <sdeventplus/event.hpp> 9 10 #include <algorithm> 11 #include <cassert> 12 #include <chrono> 13 #include <cmath> 14 #include <optional> 15 #include <vector> 16 17 namespace phosphor 18 { 19 namespace fan 20 { 21 namespace control 22 { 23 24 using ThermalObject = sdbusplus::server::object_t< 25 sdbusplus::xyz::openbmc_project::Control::server::ThermalMode>; 26 27 /** 28 * The mode fan control will run in: 29 * - init - only do the initialization steps 30 * - control - run normal control algorithms 31 */ 32 enum class Mode 33 { 34 init, 35 control 36 }; 37 38 /** 39 * @class Represents a fan control zone, which is a group of fans 40 * that behave the same. 41 */ 42 class Zone : public ThermalObject 43 { 44 public: 45 Zone() = delete; 46 Zone(const Zone&) = delete; 47 Zone(Zone&&) = delete; 48 Zone& operator=(const Zone&) = delete; 49 Zone& operator=(Zone&&) = delete; 50 ~Zone() = default; 51 52 /** 53 * Constructor 54 * Creates the appropriate fan objects based on 55 * the zone definition data passed in. 56 * 57 * @param[in] mode - mode of fan control 58 * @param[in] bus - the dbus object 59 * @param[in] path - object instance path 60 * @param[in] event - Event loop reference 61 * @param[in] def - the fan zone definition data 62 */ 63 Zone(Mode mode, sdbusplus::bus_t& bus, const std::string& path, 64 const sdeventplus::Event& event, const ZoneDefinition& def); 65 66 /** 67 * @brief Get the zone's bus 68 * 69 * @return The bus used by the zone 70 */ getBus()71 inline auto& getBus() 72 { 73 return _bus; 74 } 75 76 /** 77 * @brief Get the zone's path 78 * 79 * @return The path of this zone 80 */ getPath()81 inline auto& getPath() 82 { 83 return _path; 84 } 85 86 /** 87 * @brief Get the zone's hosted interfaces 88 * 89 * @return The interfaces hosted by this zone 90 */ getIfaces()91 inline auto& getIfaces() 92 { 93 return _ifaces; 94 } 95 96 /** 97 * Sets all fans in the zone to the speed 98 * passed in when the zone is active 99 * 100 * @param[in] speed - the fan speed 101 */ 102 void setSpeed(uint64_t speed); 103 104 /** 105 * Sets the zone to full speed regardless of zone's active state 106 */ 107 void setFullSpeed(); 108 109 /** 110 * @brief Sets the automatic fan control allowed active state 111 * 112 * @param[in] group - A group that affects the active state 113 * @param[in] isActiveAllow - Active state according to group 114 */ 115 void setActiveAllow(const Group* group, bool isActiveAllow); 116 117 /** 118 * @brief Sets the floor change allowed state 119 * 120 * @param[in] group - A group that affects floor changes 121 * @param[in] isAllow - Allow state according to group 122 */ setFloorChangeAllow(const Group * group,bool isAllow)123 inline void setFloorChangeAllow(const Group* group, bool isAllow) 124 { 125 _floorChange[*(group)] = isAllow; 126 } 127 128 /** 129 * @brief Sets the decrease allowed state of a group 130 * 131 * @param[in] group - A group that affects speed decreases 132 * @param[in] isAllow - Allow state according to group 133 */ setDecreaseAllow(const Group * group,bool isAllow)134 inline void setDecreaseAllow(const Group* group, bool isAllow) 135 { 136 _decAllowed[*(group)] = isAllow; 137 } 138 139 /** 140 * @brief Sets a given object's event data for a property on this zone 141 * 142 * @param[in] object - Name of the object containing the property 143 * @param[in] interface - Interface name containing the property 144 * @param[in] property - Property name 145 * @param[in] data - Property value 146 */ setObjectData(const std::string & object,const std::string & interface,const std::string & property,EventData * data)147 inline void setObjectData(const std::string& object, 148 const std::string& interface, 149 const std::string& property, EventData* data) 150 { 151 _objects[object][interface][property] = data; 152 } 153 154 /** 155 * @brief Sets a given object's property value 156 * 157 * @param[in] object - Name of the object containing the property 158 * @param[in] interface - Interface name containing the property 159 * @param[in] property - Property name 160 * @param[in] value - Property value 161 */ 162 template <typename T> setPropertyValue(const char * object,const char * interface,const char * property,T value)163 void setPropertyValue(const char* object, const char* interface, 164 const char* property, T value) 165 { 166 _properties[object][interface][property] = value; 167 } 168 169 /** 170 * @brief Sets a given object's property value 171 * 172 * @param[in] object - Name of the object containing the property 173 * @param[in] interface - Interface name containing the property 174 * @param[in] property - Property name 175 * @param[in] value - Property value 176 */ 177 template <typename T> setPropertyValue(const std::string & object,const std::string & interface,const std::string & property,T value)178 void setPropertyValue(const std::string& object, 179 const std::string& interface, 180 const std::string& property, T value) 181 { 182 _properties[object][interface][property] = value; 183 } 184 185 /** 186 * @brief Get the value of an object's property 187 * 188 * @param[in] object - Name of the object containing the property 189 * @param[in] interface - Interface name containing the property 190 * @param[in] property - Property name 191 * 192 * @return - The property value 193 */ 194 template <typename T> getPropertyValue(const std::string & object,const std::string & interface,const std::string & property)195 inline auto getPropertyValue(const std::string& object, 196 const std::string& interface, 197 const std::string& property) 198 { 199 return std::get<T>(_properties.at(object).at(interface).at(property)); 200 } 201 202 /** 203 * @brief Get the object's property variant 204 * 205 * @param[in] object - Name of the object containing the property 206 * @param[in] interface - Interface name containing the property 207 * @param[in] property - Property name 208 * 209 * @return - The property variant 210 */ getPropValueVariant(const std::string & object,const std::string & interface,const std::string & property)211 inline auto getPropValueVariant(const std::string& object, 212 const std::string& interface, 213 const std::string& property) 214 { 215 return _properties.at(object).at(interface).at(property); 216 } 217 218 /** 219 * @brief Get a property's value after applying a set of visitors 220 * to translate the property value's type change to keep from 221 * affecting the configured use of the property. 222 * 223 * @param[in] intf = Interface name containing the property 224 * @param[in] prop = Property name 225 * @param[in] variant = Variant containing the property's value from 226 * the supported property types. 227 */ 228 template <typename T> getPropertyValueVisitor(const char * intf,const char * prop,PropertyVariantType & variant)229 inline auto getPropertyValueVisitor(const char* intf, const char* prop, 230 PropertyVariantType& variant) 231 { 232 // Handle the transition of the dbus sensor value type from 233 // int64 to double which also removed the scale property. 234 // https://gerrit.openbmc-project.xyz/11739 235 if (strcmp(intf, "xyz.openbmc_project.Sensor.Value") == 0 && 236 strcmp(prop, "Value") == 0) 237 { 238 // Use 'optional' variable to determine if the sensor value 239 // is set within the visitor based on the supported types. 240 // A non-supported type configured will assert. 241 std::optional<T> value; 242 std::visit( 243 [&value](auto&& val) { 244 // If the type configured is int64, but the sensor value 245 // property's type is double, scale it by 1000 and return 246 // the value as an int64 as configured. 247 using V = std::decay_t<decltype(val)>; 248 if constexpr (std::is_same_v<T, int64_t> && 249 std::is_same_v<V, double>) 250 { 251 val = val * 1000; 252 value = std::lround(val); 253 } 254 // If the type configured matches the sensor value 255 // property's type, just return the value as its 256 // given type. 257 else if constexpr (std::is_same_v<T, V>) 258 { 259 value = val; 260 } 261 }, 262 variant); 263 264 // Unable to return Sensor Value property 265 // as given type configured. 266 assert(value); 267 268 return value.value(); 269 } 270 271 // Default to return the property's value by the data type 272 // configured, applying no visitors to the variant. 273 return std::get<T>(variant); 274 } 275 276 /** 277 * @brief Remove an object's interface 278 * 279 * @param[in] object - Name of the object with the interface 280 * @param[in] interface - Interface name to remove 281 */ removeObjectInterface(const char * object,const char * interface)282 inline void removeObjectInterface(const char* object, const char* interface) 283 { 284 auto it = _properties.find(object); 285 if (it != std::end(_properties)) 286 { 287 _properties[object].erase(interface); 288 } 289 } 290 291 /** 292 * @brief Remove a service associated to a group 293 * 294 * @param[in] group - Group associated with service 295 * @param[in] name - Service name to remove 296 */ 297 void removeService(const Group* group, const std::string& name); 298 299 /** 300 * @brief Set or update a service name owner in use 301 * 302 * @param[in] group - Group associated with service 303 * @param[in] name - Service name 304 * @param[in] hasOwner - Whether the service is owned or not 305 */ 306 void setServiceOwner(const Group* group, const std::string& name, 307 const bool hasOwner); 308 309 /** 310 * @brief Set or update all services for a group 311 * 312 * @param[in] group - Group to get service names for 313 */ 314 void setServices(const Group* group); 315 316 /** 317 * @brief Get the group's list of service names 318 * 319 * @param[in] group - Group to get service names for 320 * 321 * @return - The list of service names 322 */ getGroupServices(const Group * group)323 inline auto getGroupServices(const Group* group) 324 { 325 return _services.at(*group); 326 } 327 328 /** 329 * @brief Initialize a set speed event properties and actions 330 * 331 * @param[in] event - Set speed event 332 */ 333 void initEvent(const SetSpeedEvent& event); 334 335 /** 336 * @brief Removes all the set speed event properties and actions 337 * 338 * @param[in] event - Set speed event 339 */ 340 void removeEvent(const SetSpeedEvent& event); 341 342 /** 343 * @brief Get the default floor speed 344 * 345 * @return - The defined default floor speed 346 */ getDefFloor()347 inline auto getDefFloor() 348 { 349 return _defFloorSpeed; 350 } 351 352 /** 353 * @brief Set the default floor 354 * 355 * @param[in] speed - Speed to set the default floor to 356 */ setDefFloor(uint64_t speed)357 inline void setDefFloor(uint64_t speed) 358 { 359 _defFloorSpeed = speed; 360 } 361 362 /** 363 * @brief Get the ceiling speed 364 * 365 * @return - The current ceiling speed 366 */ getCeiling() const367 inline auto& getCeiling() const 368 { 369 return _ceilingSpeed; 370 } 371 372 /** 373 * @brief Set the ceiling speed to the given speed 374 * 375 * @param[in] speed - Speed to set the ceiling to 376 */ setCeiling(uint64_t speed)377 inline void setCeiling(uint64_t speed) 378 { 379 _ceilingSpeed = speed; 380 } 381 382 /** 383 * @brief Swaps the ceiling key value with what's given and 384 * returns the value that was swapped. 385 * 386 * @param[in] keyValue - New ceiling key value 387 * 388 * @return - Ceiling key value prior to swapping 389 */ swapCeilingKeyValue(int64_t keyValue)390 inline auto swapCeilingKeyValue(int64_t keyValue) 391 { 392 std::swap(_ceilingKeyValue, keyValue); 393 return keyValue; 394 } 395 396 /** 397 * @brief Get the increase speed delta 398 * 399 * @return - The current increase speed delta 400 */ getIncSpeedDelta() const401 inline auto& getIncSpeedDelta() const 402 { 403 return _incSpeedDelta; 404 } 405 406 /** 407 * @brief Get the decrease speed delta 408 * 409 * @return - The current decrease speed delta 410 */ getDecSpeedDelta() const411 inline auto& getDecSpeedDelta() const 412 { 413 return _decSpeedDelta; 414 } 415 416 /** 417 * @brief Set the floor speed to the given speed and increase target 418 * speed to the floor when target is below floor where floor changes 419 * are allowed. 420 * 421 * @param[in] speed - Speed to set the floor to 422 */ 423 void setFloor(uint64_t speed); 424 425 /** 426 * @brief Set the requested speed base to be used as the speed to 427 * base a new requested speed target from 428 * 429 * @param[in] speedBase - Base speed value to use 430 */ setRequestSpeedBase(uint64_t speedBase)431 inline void setRequestSpeedBase(uint64_t speedBase) 432 { 433 _requestSpeedBase = speedBase; 434 } 435 436 /** 437 * @brief Calculate the requested target speed from the given delta 438 * and increase the fan speeds, not going above the ceiling. 439 * 440 * @param[in] targetDelta - The delta to increase the target speed by 441 */ 442 void requestSpeedIncrease(uint64_t targetDelta); 443 444 /** 445 * @brief Calculate the requested target speed from the given delta 446 * and increase the fan speeds, not going above the ceiling. 447 * 448 * @param[in] targetDelta - The delta to increase the target speed by 449 */ 450 void requestSpeedDecrease(uint64_t targetDelta); 451 452 /** 453 * @brief Callback function for the increase timer that delays 454 * processing of requested speed increases while fans are increasing 455 */ 456 void incTimerExpired(); 457 458 /** 459 * @brief Callback function for the decrease timer that processes any 460 * requested speed decreases if allowed 461 */ 462 void decTimerExpired(); 463 464 /** 465 * @brief Get the event loop used with this zone's timers 466 * 467 * @return - The event loop for timers 468 */ getEventLoop()469 inline auto& getEventLoop() 470 { 471 return _eventLoop; 472 } 473 474 /** 475 * @brief Remove the given signal event 476 * 477 * @param[in] seIter - Iterator pointing to the signal event to remove 478 */ removeSignal(std::vector<SignalEvent>::iterator & seIter)479 inline void removeSignal(std::vector<SignalEvent>::iterator& seIter) 480 { 481 std::get<signalEventDataPos>(*seIter).reset(); 482 if (std::get<signalMatchPos>(*seIter) != nullptr) 483 { 484 std::get<signalMatchPos>(*seIter).reset(); 485 } 486 } 487 488 /** 489 * @brief Get the list of timer events 490 * 491 * @return - List of timer events 492 */ getTimerEvents()493 inline auto& getTimerEvents() 494 { 495 return _timerEvents; 496 } 497 498 /** 499 * @brief Find the first instance of a timer event 500 * 501 * @param[in] eventGroup - Group associated with a timer 502 * @param[in] eventActions - List of actions associated with a timer 503 * @param[in] eventTimers - List of timers to find the timer in 504 * 505 * @return - Iterator to the timer event 506 */ 507 std::vector<TimerEvent>::iterator findTimer( 508 const Group& eventGroup, const std::vector<Action>& eventActions, 509 std::vector<TimerEvent>& eventTimers); 510 511 /** 512 * @brief Add a timer to the list of timer based events 513 * 514 * @param[in] name - Event name associated with timer 515 * @param[in] group - Group associated with a timer 516 * @param[in] actions - List of actions associated with a timer 517 * @param[in] tConf - Configuration for the new timer 518 */ 519 void addTimer(const std::string& name, const Group& group, 520 const std::vector<Action>& actions, const TimerConf& tConf); 521 522 /** 523 * @brief Callback function for event timers that processes the given 524 * actions for a group 525 * 526 * @param[in] eventGroup - Group to process actions on 527 * @param[in] eventActions - List of event actions to run 528 */ 529 void timerExpired(const Group& eventGroup, 530 const std::vector<Action>& eventActions); 531 532 /** 533 * @brief Get the service for a given path and interface from cached 534 * dataset and add a service that's not found 535 * 536 * @param[in] path - Path to get service for 537 * @param[in] intf - Interface to get service for 538 * 539 * @return - The service name 540 */ 541 const std::string& getService(const std::string& path, 542 const std::string& intf); 543 544 /** 545 * @brief Add a set of services for a path and interface 546 * by retrieving all the path subtrees to the given depth 547 * from root for the interface 548 * 549 * @param[in] path - Path to add services for 550 * @param[in] intf - Interface to add services for 551 * @param[in] depth - Depth of tree traversal from root path 552 * 553 * @return - The associated service to the given path and interface 554 * or empty string for no service found 555 */ 556 const std::string& addServices(const std::string& path, 557 const std::string& intf, int32_t depth); 558 559 /** 560 * @brief Dbus signal change callback handler 561 * 562 * @param[in] msg - Expanded sdbusplus message data 563 * @param[in] eventData - The single event's data 564 */ 565 void handleEvent(sdbusplus::message_t& msg, const EventData* eventData); 566 567 /** 568 * @brief Add a signal to the list of signal based events 569 * 570 * @param[in] name - Event name 571 * @param[in] data - Event data for signal 572 * @param[in] match - Subscribed signal match 573 */ addSignal(const std::string & name,std::unique_ptr<EventData> && data,std::unique_ptr<sdbusplus::bus::match_t> && match)574 inline void addSignal(const std::string& name, 575 std::unique_ptr<EventData>&& data, 576 std::unique_ptr<sdbusplus::bus::match_t>&& match) 577 { 578 _signalEvents[name].emplace_back(std::move(data), std::move(match)); 579 } 580 581 /** 582 * @brief Set a property to be persisted 583 * 584 * @param[in] intf - Interface containing property 585 * @param[in] prop - Property to be persisted 586 */ setPersisted(const std::string & intf,const std::string & prop)587 inline void setPersisted(const std::string& intf, const std::string& prop) 588 { 589 _persisted[intf].emplace_back(prop); 590 } 591 592 /** 593 * @brief Get persisted property 594 * 595 * @param[in] intf - Interface containing property 596 * @param[in] prop - Property persisted 597 * 598 * @return - True if property is to be persisted, false otherwise 599 */ 600 auto getPersisted(const std::string& intf, const std::string& prop); 601 602 /** 603 * @brief Get a property value from the zone object or the bus when 604 * the property requested is not on the zone object 605 * 606 * @param[in] path - Path of object 607 * @param[in] intf - Object interface 608 * @param[in] prop - Object property 609 * 610 * @return - Property's value 611 */ 612 template <typename T> getPropertyByName(const std::string & path,const std::string & intf,const std::string & prop)613 auto getPropertyByName(const std::string& path, const std::string& intf, 614 const std::string& prop) 615 { 616 T value; 617 auto pathIter = _objects.find(path); 618 if (pathIter != _objects.end()) 619 { 620 auto intfIter = pathIter->second.find(intf); 621 if (intfIter != pathIter->second.end()) 622 { 623 if (intf == "xyz.openbmc_project.Control.ThermalMode") 624 { 625 auto var = ThermalMode::getPropertyByName(prop); 626 // Use visitor to determine if requested property 627 // type(T) is available on this interface and read it 628 std::visit( 629 [&value](auto&& val) { 630 using V = std::decay_t<decltype(val)>; 631 if constexpr (std::is_same_v<T, V>) 632 { 633 value = val; 634 } 635 }, 636 var); 637 638 return value; 639 } 640 } 641 } 642 643 // Retrieve the property's value applying any visitors necessary 644 auto service = getService(path, intf); 645 auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>( 646 _bus, service, path, intf, prop); 647 value = getPropertyValueVisitor<T>(intf.c_str(), prop.c_str(), variant); 648 649 return value; 650 } 651 652 /** 653 * @brief Overridden thermal object's set 'Current' property function 654 * 655 * @param[in] value - Value to set 'Current' to 656 * 657 * @return - The updated value of the 'Current' property 658 */ 659 virtual std::string current(std::string value); 660 661 private: 662 /** 663 * The dbus object 664 */ 665 sdbusplus::bus_t& _bus; 666 667 /** 668 * Zone object path 669 */ 670 const std::string _path; 671 672 /** 673 * Zone supported interfaces 674 */ 675 const std::vector<std::string> _ifaces; 676 677 /** 678 * Full speed for the zone 679 */ 680 const uint64_t _fullSpeed; 681 682 /** 683 * The zone number 684 */ 685 const size_t _zoneNum; 686 687 /** 688 * The default floor speed for the zone 689 */ 690 uint64_t _defFloorSpeed; 691 692 /** 693 * The default ceiling speed for the zone 694 */ 695 const uint64_t _defCeilingSpeed; 696 697 /** 698 * The floor speed to not go below 699 */ 700 uint64_t _floorSpeed = _defFloorSpeed; 701 702 /** 703 * The ceiling speed to not go above 704 */ 705 uint64_t _ceilingSpeed = _defCeilingSpeed; 706 707 /** 708 * The previous sensor value for calculating the ceiling 709 */ 710 int64_t _ceilingKeyValue = 0; 711 712 /** 713 * Automatic fan control active state 714 */ 715 bool _isActive = true; 716 717 /** 718 * Target speed for this zone 719 */ 720 uint64_t _targetSpeed = _fullSpeed; 721 722 /** 723 * Speed increase delta 724 */ 725 uint64_t _incSpeedDelta = 0; 726 727 /** 728 * Speed decrease delta 729 */ 730 uint64_t _decSpeedDelta = 0; 731 732 /** 733 * Requested speed base 734 */ 735 uint64_t _requestSpeedBase = 0; 736 737 /** 738 * Speed increase delay in seconds 739 */ 740 std::chrono::seconds _incDelay; 741 742 /** 743 * Speed decrease interval in seconds 744 */ 745 std::chrono::seconds _decInterval; 746 747 /** 748 * The increase timer object 749 */ 750 Timer _incTimer; 751 752 /** 753 * The decrease timer object 754 */ 755 Timer _decTimer; 756 757 /** 758 * Event loop used on set speed event timers 759 */ 760 sdeventplus::Event _eventLoop; 761 762 /** 763 * The vector of fans in this zone 764 */ 765 std::vector<std::unique_ptr<Fan>> _fans; 766 767 /** 768 * @brief Map of object property values 769 */ 770 std::map<std::string, 771 std::map<std::string, std::map<std::string, PropertyVariantType>>> 772 _properties; 773 774 /** 775 * @brief Map of zone objects 776 */ 777 std::map<std::string, 778 std::map<std::string, std::map<std::string, EventData*>>> 779 _objects; 780 781 /** 782 * @brief Map of interfaces to persisted properties 783 */ 784 std::map<std::string, std::vector<std::string>> _persisted; 785 786 /** 787 * @brief Map of active fan control allowed by groups 788 */ 789 std::map<const Group, bool> _active; 790 791 /** 792 * @brief Map of floor change allowed by groups 793 */ 794 std::map<const Group, bool> _floorChange; 795 796 /** 797 * @brief Map of groups controlling decreases allowed 798 */ 799 std::map<const Group, bool> _decAllowed; 800 801 /** 802 * @brief Map of group service names 803 */ 804 std::map<const Group, std::vector<Service>> _services; 805 806 /** 807 * @brief Map tree of paths to services of interfaces 808 */ 809 std::map<std::string, std::map<std::string, std::vector<std::string>>> 810 _servTree; 811 812 /** 813 * @brief List of signal event arguments and Dbus matches 814 * for callbacks per event name 815 */ 816 std::map<std::string, std::vector<SignalEvent>> _signalEvents; 817 818 /** 819 * @brief List of timers per event name 820 */ 821 std::map<std::string, std::vector<TimerEvent>> _timerEvents; 822 823 /** 824 * @brief Save the thermal control current mode property 825 * to persisted storage 826 */ 827 void saveCurrentMode(); 828 829 /** 830 * @brief Restore persisted thermal control current mode property 831 * value, setting the mode to "Default" otherwise 832 */ 833 void restoreCurrentMode(); 834 835 /** 836 * @brief Get the request speed base if defined, otherwise the 837 * the current target speed is returned 838 * 839 * @return - The request speed base or current target speed 840 */ getRequestSpeedBase() const841 inline auto getRequestSpeedBase() const 842 { 843 return (_requestSpeedBase != 0) ? _requestSpeedBase : _targetSpeed; 844 } 845 }; 846 847 } // namespace control 848 } // namespace fan 849 } // namespace phosphor 850