xref: /openbmc/phosphor-fan-presence/control/zone.hpp (revision 5a80f3aec4cc817b0f20ce2ddd83d930b7df3af1)
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