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