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