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