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_t<
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_t& 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_t& msg, const EventData* eventData);
567 
568     /**
569      * @brief Add a signal to the list of signal based events
570      *
571      * @param[in] name - Event name
572      * @param[in] data - Event data for signal
573      * @param[in] match - Subscribed signal match
574      */
575     inline void addSignal(const std::string& name,
576                           std::unique_ptr<EventData>&& data,
577                           std::unique_ptr<sdbusplus::bus::match_t>&& match)
578     {
579         _signalEvents[name].emplace_back(std::move(data), std::move(match));
580     }
581 
582     /**
583      * @brief Set a property to be persisted
584      *
585      * @param[in] intf - Interface containing property
586      * @param[in] prop - Property to be persisted
587      */
588     inline void setPersisted(const std::string& intf, const std::string& prop)
589     {
590         _persisted[intf].emplace_back(prop);
591     }
592 
593     /**
594      * @brief Get persisted property
595      *
596      * @param[in] intf - Interface containing property
597      * @param[in] prop - Property persisted
598      *
599      * @return - True if property is to be persisted, false otherwise
600      */
601     auto getPersisted(const std::string& intf, const std::string& prop);
602 
603     /**
604      * @brief Get a property value from the zone object or the bus when
605      * the property requested is not on the zone object
606      *
607      * @param[in] path - Path of object
608      * @param[in] intf - Object interface
609      * @param[in] prop - Object property
610      *
611      * @return - Property's value
612      */
613     template <typename T>
614     auto getPropertyByName(const std::string& path, const std::string& intf,
615                            const std::string& prop)
616     {
617         T value;
618         auto pathIter = _objects.find(path);
619         if (pathIter != _objects.end())
620         {
621             auto intfIter = pathIter->second.find(intf);
622             if (intfIter != pathIter->second.end())
623             {
624                 if (intf == "xyz.openbmc_project.Control.ThermalMode")
625                 {
626                     auto var = ThermalMode::getPropertyByName(prop);
627                     // Use visitor to determine if requested property
628                     // type(T) is available on this interface and read it
629                     std::visit(
630                         [&value](auto&& val) {
631                             using V = std::decay_t<decltype(val)>;
632                             if constexpr (std::is_same_v<T, V>)
633                             {
634                                 value = val;
635                             }
636                         },
637                         var);
638 
639                     return value;
640                 }
641             }
642         }
643 
644         // Retrieve the property's value applying any visitors necessary
645         auto service = getService(path, intf);
646         auto variant = util::SDBusPlus::getPropertyVariant<PropertyVariantType>(
647             _bus, service, path, intf, prop);
648         value = getPropertyValueVisitor<T>(intf.c_str(), prop.c_str(), variant);
649 
650         return value;
651     }
652 
653     /**
654      * @brief Overridden thermal object's set 'Current' property function
655      *
656      * @param[in] value - Value to set 'Current' to
657      *
658      * @return - The updated value of the 'Current' property
659      */
660     virtual std::string current(std::string value);
661 
662   private:
663     /**
664      * The dbus object
665      */
666     sdbusplus::bus_t& _bus;
667 
668     /**
669      * Zone object path
670      */
671     const std::string _path;
672 
673     /**
674      * Zone supported interfaces
675      */
676     const std::vector<std::string> _ifaces;
677 
678     /**
679      * Full speed for the zone
680      */
681     const uint64_t _fullSpeed;
682 
683     /**
684      * The zone number
685      */
686     const size_t _zoneNum;
687 
688     /**
689      * The default floor speed for the zone
690      */
691     uint64_t _defFloorSpeed;
692 
693     /**
694      * The default ceiling speed for the zone
695      */
696     const uint64_t _defCeilingSpeed;
697 
698     /**
699      * The floor speed to not go below
700      */
701     uint64_t _floorSpeed = _defFloorSpeed;
702 
703     /**
704      * The ceiling speed to not go above
705      */
706     uint64_t _ceilingSpeed = _defCeilingSpeed;
707 
708     /**
709      * The previous sensor value for calculating the ceiling
710      */
711     int64_t _ceilingKeyValue = 0;
712 
713     /**
714      * Automatic fan control active state
715      */
716     bool _isActive = true;
717 
718     /**
719      * Target speed for this zone
720      */
721     uint64_t _targetSpeed = _fullSpeed;
722 
723     /**
724      * Speed increase delta
725      */
726     uint64_t _incSpeedDelta = 0;
727 
728     /**
729      * Speed decrease delta
730      */
731     uint64_t _decSpeedDelta = 0;
732 
733     /**
734      * Requested speed base
735      */
736     uint64_t _requestSpeedBase = 0;
737 
738     /**
739      * Speed increase delay in seconds
740      */
741     std::chrono::seconds _incDelay;
742 
743     /**
744      * Speed decrease interval in seconds
745      */
746     std::chrono::seconds _decInterval;
747 
748     /**
749      * The increase timer object
750      */
751     Timer _incTimer;
752 
753     /**
754      * The decrease timer object
755      */
756     Timer _decTimer;
757 
758     /**
759      * Event loop used on set speed event timers
760      */
761     sdeventplus::Event _eventLoop;
762 
763     /**
764      * The vector of fans in this zone
765      */
766     std::vector<std::unique_ptr<Fan>> _fans;
767 
768     /**
769      * @brief Map of object property values
770      */
771     std::map<std::string,
772              std::map<std::string, std::map<std::string, PropertyVariantType>>>
773         _properties;
774 
775     /**
776      * @brief Map of zone objects
777      */
778     std::map<std::string,
779              std::map<std::string, std::map<std::string, EventData*>>>
780         _objects;
781 
782     /**
783      * @brief Map of interfaces to persisted properties
784      */
785     std::map<std::string, std::vector<std::string>> _persisted;
786 
787     /**
788      * @brief Map of active fan control allowed by groups
789      */
790     std::map<const Group, bool> _active;
791 
792     /**
793      * @brief Map of floor change allowed by groups
794      */
795     std::map<const Group, bool> _floorChange;
796 
797     /**
798      * @brief Map of groups controlling decreases allowed
799      */
800     std::map<const Group, bool> _decAllowed;
801 
802     /**
803      * @brief Map of group service names
804      */
805     std::map<const Group, std::vector<Service>> _services;
806 
807     /**
808      * @brief Map tree of paths to services of interfaces
809      */
810     std::map<std::string, std::map<std::string, std::vector<std::string>>>
811         _servTree;
812 
813     /**
814      * @brief List of signal event arguments and Dbus matches
815      * for callbacks per event name
816      */
817     std::map<std::string, std::vector<SignalEvent>> _signalEvents;
818 
819     /**
820      * @brief List of timers per event name
821      */
822     std::map<std::string, std::vector<TimerEvent>> _timerEvents;
823 
824     /**
825      * @brief Save the thermal control current mode property
826      * to persisted storage
827      */
828     void saveCurrentMode();
829 
830     /**
831      * @brief Restore persisted thermal control current mode property
832      * value, setting the mode to "Default" otherwise
833      */
834     void restoreCurrentMode();
835 
836     /**
837      * @brief Get the request speed base if defined, otherwise the
838      * the current target speed is returned
839      *
840      * @return - The request speed base or current target speed
841      */
842     inline auto getRequestSpeedBase() const
843     {
844         return (_requestSpeedBase != 0) ? _requestSpeedBase : _targetSpeed;
845     }
846 };
847 
848 } // namespace control
849 } // namespace fan
850 } // namespace phosphor
851