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