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