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