xref: /openbmc/phosphor-fan-presence/docs/control/events.md (revision 9f2b5056da97b2d64bbee0e41327cf977f533bc7)
1# events.json
2
3This file defines the events that dictate how fan control operates. Each event
4can contain groups, triggers, and actions.
5
6Actions are where fan targets are calculated and set, among other things.
7Triggers specify when an action should run. Groups specify which D-Bus objects
8the triggers and actions should operate on.
9
10Some actions have modifiers, which help calculate a value.
11
12- [Groups](#groups)
13- [Triggers](#triggers)
14- [Actions](#actions)
15- [Modifiers](#modifiers)
16
17## Example
18
19```json
20[
21  {
22    "name": "fan(s) missing",
23    "groups": [
24      {
25        "name": "fan inventory",
26        "interface": "xyz.openbmc_project.Inventory.Item",
27        "property": { "name": "Present" }
28      }
29    ],
30    "triggers": [
31      {
32        "class": "init",
33        "method": "get_properties"
34      },
35      {
36        "class": "signal",
37        "signal": "properties_changed"
38      }
39    ],
40    "actions": [
41      {
42        "name": "count_state_before_target",
43        "count": 1,
44        "state": false,
45        "target": 15000
46      }
47    ]
48  }
49]
50```
51
52The above event is an example of a method to set the fan target to 15000 when
53one or more fans is missing. The basic behavior with this config is:
54
55The trigger `init.get_properties` will, on fan control startup, read the Present
56property on the `xyz.openbmc_project.Inventory.Item` interface from every D-Bus
57object in the 'fan inventory' group, update those values in the object cache,
58and then call the `count_state_before_target` action.
59
60The trigger `signal.properties_changed` will watch for changes on the Present
61property on each D-Bus object in the group, update the new value in the object
62cache, and call the `count_state_before_target` action when the value changes.
63
64The `count_state_before_target` action will look at the object cache value of
65the Present property on each member of the group and set the fan target hold to
6615000 when one or more of them is false. Otherwise, it will clear its fan target
67hold.
68
69## Groups
70
71```json
72"groups": [
73    {
74        "name": "<name>",
75        "interface": "<interface>",
76        "property": { "name": "<name>" }
77    }
78    ...
79]
80```
81
82### name
83
84The name of a group that must be also be defined in [groups.json](groups.md).
85
86### interface
87
88The actions and triggers defined with this group will look at this D-Bus
89interface on the members of this group.
90
91### property: name
92
93The actions and triggers defined with this group will look at this D-Bus
94property on the members of this group.
95
96## Triggers
97
98There are several classes of triggers, and the JSON configuration is different
99for each.
100
101### init
102
103Init triggers run when fan control events are enabled on fan control startup.
104After invoking the configured method, any actions configured for this trigger
105will run.
106
107```json
108{
109  "class": "init",
110  "method": "<method>"
111}
112```
113
114#### methods
115
1161. `get_properties` - Read the property configured for the group from every
117   member of the group, and store it in fan control's object cache.
118
1192. `name_has_owner` - Populates the service owned state from D-Bus for each
120   group member in fan control's D-Bus service cache map.
121
122### signal
123
124Signal triggers subscribe to certain D-Bus signals for each member of its
125configured group. After handling the signal, any configured actions are run.
126
127```json
128{
129  "class": "signal",
130  "signal": "<signal>"
131}
132```
133
134#### signal types
135
1361. `properties_changed` - Subscribes to the PropertiesChanged signal for the
137   D-Bus interface and property specified in the group definition for each group
138   member. When the signal occurs, the new property value will be added to or
139   updated in the object cache.
140
1412. `interfaces_added` - Subscribes to the InterfacesAdded signal for the D-Bus
142   interface specified in the group definition for each group member. When the
143   signal occurs, the interface and its properties will be added to the object
144   cache.
145
1463. `interfaces_removed` - Subscribes to the InterfacesRemoved signal for the
147   D-Bus interface specified in the group definition for each group member. When
148   the signal occurs, the interface and properties will be removed from the
149   object cache.
150
1514. `name_owner_changed` - Subscribes to the NameOwnerChanged signal for the
152   services that host the D-bus interface specified in the group definition for
153   each group member. When the signal occurs, the service owned state will be
154   updated in the service cache map.
155
1565. `member` - Subscribes to the signal listed on each group member. No caches
157   are updated when the signal occurs.
158
159### timer
160
161Timer triggers run actions after the configured type of timer expires.
162
163```json
164{
165  "class": "timer",
166  "type": "<type>",
167  "interval": "<interval>",
168  "preload_groups": "<true/false>"
169}
170```
171
172#### type
173
1741. `oneshot` - Starts a timer that runs once.
175
1762. `repeating` - Starts a repeating timer.
177
178#### interval
179
180The timer length in microseconds
181
182#### preload_groups
183
184Optional, if set to true, will update the D-Bus properties from the configured
185groups in the object cache after the timer expires but before any actions run.
186
187### parameter
188
189Parameter triggers run actions after a parameter changes.
190
191```json
192{
193  "class": "parameter",
194  "parameter": "<parameter>"
195}
196```
197
198#### parameter
199
200The parameter value to watch.
201
202### poweron
203
204PowerOn triggers run when the power turns on. Functionally, they behave like an
205init trigger.
206
207```json
208{
209  "class": "poweron",
210  "method": "<method>"
211}
212```
213
214#### method
215
216The methods are the same as with the init trigger.
217
218### poweroff
219
220PowerOff triggers run when the power turns off. Functionally, they behave like
221an init trigger.
222
223```json
224{
225  "class": "poweroff",
226  "method": "<method>"
227}
228```
229
230#### method
231
232The methods are the same as with the init trigger.
233
234## Actions
235
236Actions can either operate on the groups listed with the event, or on the groups
237listed within the action's JSON config.
238
239Most actions set fan targets or floors. This can be done by requesting a target
240value explicitly, or by requesting an increase or decrease delta. Targets and
241floors can also be set with a hold, meaning another action can't set a
242floor/target below the one that is held.
243
244Some actions set or read a key/value pair called a parameter. These can be
245created, updated, and deleted as necessary. For example, one action may
246calculate and set a `floor_index` parameter, and another action may then read
247that parameter to help choose a fan floor.
248
249The available actions are:
250
251- [set_net_increase_target](#set_net_increase_target)
252- [set_net_decrease_target](#set_net_decrease_target)
253- [count_state_floor](#count_state_floor)
254- [count_state_before_target](#count_state_before_target)
255- [default_floor_on_missing_owner](#default_floor_on_missing_owner)
256- [mapped_floor](#mapped_floor)
257- [set_target_on_missing_owner](#set_target_on_missing_owner)
258- [override_fan_target](#override_fan_target)
259- [pcie_card_floors](#pcie_card_floors)
260- [set_request_target_base_with_max](#set_request_target_base_with_max)
261- [set_parameter_from_group_max](#set_parameter_from_group_max)
262- [target_from_group_max](#target_from_group_max)
263- [call_actions_based_on_timer](#call_actions_based_on_timer)
264- [get_managed_objects](#get_managed_objects)
265
266### set_net_increase_target
267
268Calculates the net target increase to be requested based on the value of each
269property given within a group. The net target increase is based on the maximum
270difference between the `delta` JSON value and all of the properties of the
271group. The final result is the increase change that's requested to the current
272target of a zone.
273
274The group values can be compared to either a value hardcoded in the JSON, or a
275parameter value.
276
277```json
278{
279  "name": "set_net_increase_target",
280  "groups": [
281    {
282      "name": "pcie temps",
283      "interface": "xyz.openbmc_project.Sensor.Value",
284      "property": { "name": "Value" }
285    }
286  ],
287  "state": 70.0,
288  "delta": 255
289}
290```
291
292The above config uses a hardcoded state value:
293
294- For each member of the 'pcie temps' group:
295  - Read its 'Value' D-Bus property.
296  - If that property value is greater than the 'state' value of 70.0:
297  - Subtracts 70.0 from the property value.
298  - Multiplies that difference by the 'delta' value of 255.
299
300- Requests an increase of the largest calculated delta value, if there is one.
301
302  ```json
303  {
304    "name": "set_net_increase_target",
305    "groups": [
306      {
307        "name": "proc0 core temps",
308        "interface": "xyz.openbmc_project.Sensor.Value",
309        "property": { "name": "Value" }
310      }
311    ],
312    "state_parameter_name": "proc_0_core_dvfs_increase_temp",
313    "delta": 300
314  }
315  ```
316
317The above config uses a parameter as the state value:
318
319- For each member of the 'proc 0 core temps' group:
320  - Read its 'Value' D-Bus property.
321  - If that property value is greater than the value of the parameter listed in
322    the 'state_parameter_name' field, in this case
323    'proc_0_core_dvfs_increase_temp':
324  - Subtracts that parameter value from the property value.
325  - Multiplies that difference by the 'delta' value of 300.
326
327- Requests an increase of the largest calculated delta value, if there is one.
328
329### set_net_decrease_target
330
331Calculates the net target decrease to be requested based on the value of each
332property given within a group. The net target decrease is based on the minimum
333difference between the `delta` JSON value and all properties in the group. The
334final result is the decrease change that's requested to the current target of a
335zone.
336
337The group values can be compared to either a value hardcoded in the JSON, or a
338parameter value.
339
340```json
341{
342  "name": "set_net_decrease_target",
343  "groups": [
344    {
345      "name": "pcie temps",
346      "interface": "xyz.openbmc_project.Sensor.Value",
347      "property": { "name": "Value" }
348    }
349  ],
350  "state": 65.0,
351  "delta": 80
352}
353```
354
355The above config uses a hardcoded state value:
356
357- For each member of the 'pcie temps' group:
358  - Read its 'Value' D-Bus property.
359  - If that property value is less than the 'state' value of 65.0:
360  - Subtracts the property value from 65.0.
361  - Multiplies that difference by the 'delta' value of 80.
362
363- Requests a decrease of the smallest calculated delta value, if there is one.
364
365  ```json
366  {
367    "name": "set_net_decrease_target",
368    "groups": [
369      {
370        "name": "proc 0 core temps",
371        "interface": "xyz.openbmc_project.Sensor.Value",
372        "property": { "name": "Value" }
373      }
374    ],
375    "state_parameter_name": "proc_0_core_dvfs_decrease_temp",
376    "delta": 50
377  }
378  ```
379
380The above config uses a parameter as the state value:
381
382- For each member of the 'proc 0 core temps' group:
383  - Read its 'Value' D-Bus property.
384  - If that property value is less than the value of the parameter listed the
385    'state_parameter_name' field, in this case 'proc_0_core_dvfs_decrease_temp':
386  - Subtracts the property value from the parameter value.
387  - Multiplies that difference by the 'delta' value of 50.
388
389- Requests a decrease of the smallest calculated delta value, if there is one.
390
391### count_state_floor
392
393Sets the fans to a configured floor when a number of members within the group
394are at a configured state. Once the number of members at the given state falls
395below the configured count, the floor hold is released.
396
397```json
398{
399  "name": "count_state_floor",
400  "count": 2,
401  "state": false,
402  "floor": 18000,
403  "delay": 3
404}
405```
406
407The above config reads the configured D-Bus property on each group member
408configured for the action. If two or more members have a property value of false
409for 3 seconds, a floor hold will be requested with a value of 18000. Otherwise,
410the floor hold will be released (if it was previously requested).
411
412### count_state_before_target
413
414Sets the fans to a configured target when a number of members within the group
415are at a configured state. Once the number of members at the given state falls
416below the configured count, active fan target changes are allowed.
417
418```json
419{
420  "name": "count_state_before_target",
421  "count": 1,
422  "state": false,
423  "target": 18000
424}
425```
426
427The above config reads the configured D-Bus property on each group member
428configured for the action. If one or more members have a property value of
429false, a target hold will be requested with a value of 18000. Otherwise, the
430hold will be released (if it was previously requested).
431
432### default_floor_on_missing_owner
433
434Sets the fan floor to the defined zone's default fan floor when a service
435associated to a given group has terminated. Once all services are functional and
436providing the sensors, the fan floor is allowed to be set normally again.
437
438There is no additional JSON config for this action.
439
440### mapped_floor
441
442This action can be used to set a floor value based on 2 or more groups having
443values within certain ranges, where the key group chooses the set of tables in
444which to check the remaining group values.
445
446```json
447{
448  "name": "mapped_floor",
449  "key_group": "ambient temp",
450  "default_floor": 5555,
451  "fan_floors": [
452    {
453      "key": 25,
454      "default_floor": 4444,
455      "floor_offset_parameter": "ambient_25_altitude_offset",
456      "floors": [
457        {
458          "parameter": "pcie_floor_index",
459          "floors": [
460            { "value": 1, "floor": 2000 },
461            { "value": 2, "floor": 3000 },
462            { "value": 3, "floor": 4000 },
463            { "value": 4, "floor": 5000 },
464            { "value": 5, "floor": 6000 }
465          ]
466        },
467        {
468          "group": "power save",
469          "floors": [{ "value": true, "floor": 1000 }]
470        }
471      ]
472    }
473  ]
474}
475```
476
477The above config will use the maximum value of the 'ambient temp' group as the
478key into the 'fan_floors' tables. There is one of those tables listed, and it
479will be used when the key group has a max value of less than 25.
480
481It will then traverse the contained floors arrays, keeping track of the highest
482valid floor value it finds. If it doesn't find any valid floor values, it will
483use the `default_floor` value of 4444, though that value is optional.
484
485If no valid tables were found given the key value, the `default_floor` value of
4865555 would be used, though that is optional and if not supplied the code would
487default to the default floor of the zone.
488
489At the end of the analysis, a floor hold will be set with the final floor value.
490
491This action can also have a condition specified where a group property must
492either match or not match a given value to determine if the action should run or
493not. This requires the following in the JSON:
494
495- "condition_group": The group name
496  - For now, this group must just have a single member.
497- "condition_op": Either "equal" or "not_equal"
498- "condition_value": The value to check against
499
500For example, the following says the single member of the 'cpu 0' group must have
501its Model property be equal to "1234" for the action to run:
502
503```json
504    "groups": [{
505        "name": "cpu 0",
506        "interface": "xyz.openbmc_project.Inventory.Decorator.Asset",
507        "property": { "name": "Model" }
508      }
509      ...
510    ],
511    ...
512    "name": "mapped_floor",
513    "key_group": "ambient temp",
514    "condition_group": "cpu 0",
515    "condition_value": "1234",
516    "condition_op": "equal",
517    ...
518```
519
520### set_target_on_missing_owner
521
522Sets the fans to a configured target when any service owner associated to the
523group is missing. Once all services are functional and providing all the group
524data again, active fan target changes are allowed.
525
526```json
527{
528  "name": "set_target_on_missing_owner",
529  "groups": [
530    {
531      "name": "fan inventory",
532      "interface": "xyz.openbmc_project.Inventory.Item",
533      "property": { "name": "Present" }
534    }
535  ],
536  "target": 18000
537}
538```
539
540The above config will set a target hold of 18000 when the service associated
541with the 'fan inventory' group is lost.
542
543### override_fan_target
544
545This action locks fans at configured targets when the configured `count` amount
546of fans meet criterion for the particular condition. A locked fan maintains its
547override target until unlocked (or locked at a higher target). Upon unlocking,
548it will either revert to temperature control or activate the next-highest target
549remaining in its list of locks.
550
551```json
552{
553  "name": "override_fan_target",
554  "count": 1,
555  "state": false,
556  "fans": ["fan0", "fan1", "fan2", "fan3"],
557  "target": 10000
558}
559```
560
561The above config will lock all fans in the fans array at a target of 10000 when
562one or more members in its configured group have a group property value of
563false.
564
565This could be used for example, to lock the rotors of a multirotor fan to a high
566target when one of its rotors has a functional property equal to false.
567
568### pcie_card_floors
569
570Sets the `pcie_floor_index` parameter based on the current configuration of
571plugged and powered on PCIe cards, using data from the `pcie_cards.json` file.
572
573It chooses the highest index from the active PCIe cards to set in the parameter.
574
575It must be configured with the following groups and properties:
576
577- The PCIe slots with the PowerState property
578- The PCIe cards with the following properties: Function0DeviceId,
579  Function0VendorId, Function0SubsystemId, Function0SubsystemVendorId
580
581```json
582{
583  "name": "pcie_card_floors",
584  "use_config_specific_files": true,
585  "settle_time": 2
586}
587```
588
589The `use_config_specific_files` field tells the code to look for the
590'pcie_cards.json' files in the same system specific directories as
591'events.json'. If missing or false, looks in the base fan control directory.
592
593The `settle_time` field is the amount of time in seconds that needs to pass
594without a call to run() from a group property value changing. As the PCIeDevice
595attributes are written close together by the host, this allows the action to
596wait until the writes are done before selecting the index.
597
598Additional details are in the
599[header file](../../control/json/actions/pcie_card_floors.hpp)
600
601### set_request_target_base_with_max
602
603Determines the maximum value from the properties of the group of D-Bus objects
604and sets the requested target base to this value. Only positive integer or
605floating point types are supported as these are the only valid types for a fan
606target to be based off of.
607
608The `requested target base` value is the base value to apply a target delta to.
609By default, it's the current zone target unless modified by this action.
610
611```json
612{
613  "name": "set_request_target_base_with_max",
614  "groups": [
615    {
616      "name": "fan targets",
617      "interface": "xyz.openbmc_project.Fan.Target",
618      "property": { "name": "Target" }
619    }
620  ]
621}
622```
623
624The above config will set the requested target base to the maximum Target
625property value of all members of the 'fan targets' group.
626
627### set_parameter_from_group_max
628
629Sets a parameter value based on the maximum group property value. The property
630value can be modified before storing it if the JSON specifies a valid modifier
631expression.
632
633```json
634{
635  "name": "set_parameter_from_group_max",
636  "parameter_name": "proc_0_throttle_temp",
637  "modifier": {
638    "expression": "minus",
639    "value": 4
640  }
641}
642```
643
644The above config will first find the max of its groups property values, subtract
6454, and then store the resulting value in the `proc_0_throttle_temp` parameter.
646
647### target_from_group_max
648
649The action sets target of Zone to a value corresponding to the maximum value
650from maximum group property value. The mapping is based on a provided table. If
651there are more than one event using this action, the maximum speed derived from
652the mapping of all groups will be set to the zone's target.
653
654```json
655{
656  "name": "target_from_group_max",
657  "groups": [
658    {
659      "name": "zone0_ambient",
660      "interface": "xyz.openbmc_project.Sensor.Value",
661      "property": {
662        "name": "Value"
663      }
664    }
665  ],
666  "neg_hysteresis": 1,
667  "pos_hysteresis": 0,
668  "map": [
669    {
670      "value": 10.0,
671      "target": 38.0
672    }
673  ]
674}
675```
676
677The above JSON will cause the action to read the property specified in the group
678"zone0_ambient" from all members of the group. The change in the group's members
679value will be checked against "neg_hysteresis" and "pos_hysteresis" to decide if
680it is worth taking action. "pos_hysteresis" is for the increasing case and
681"neg_hysteresis" is for the decreasing case. The maximum property value in the
682group will be mapped to the "map" to get the output "target". Each configured
683event using this action will be provided with a key in a static map to store its
684mapping result. The static map will be shared across the events of this action.
685Therefore, the updated "target" value derived from "zone0_ambient" will be
686stored in that static map with its own key. Each time it calls this action
687running for each event, after the new value is updated to the static map, the
688maximum value from it will be used to set to the Zone's target.
689
690### call_actions_based_on_timer
691
692This action starts and stops a timer that runs a list of actions whenever the
693timer expires. A timer can be either `oneshot` or `repeating`.
694
695When all groups have a configured value to compare against, that will be
696compared against all members within each group to start/stop the timer. When all
697group members have a given value and it matches what's in the cache, the timer
698is started and if any do not match, the timer is stopped.
699
700When any group does not have a configured value to be compared against, the
701groups' service owned state is used to start/stop the timer. When any service
702providing a group member is not owned, the timer is started and if all members'
703services are owned, the timer is stopped.
704
705Consider the following action config:
706
707```json
708{
709  "name": "call_actions_based_on_timer",
710  "timer": {
711    "interval": 5000000,
712    "type": "oneshot"
713  },
714  "actions": [
715    {
716      "name": "test"
717    }
718  ]
719}
720```
721
722If its group configuration has a property value listed, like:
723
724```json
725{
726  "name": "fan inventory",
727  "interface": "xyz.openbmc_project.Inventory.Item",
728  "property": { "name": "Present", "value": true }
729}
730```
731
732Then a oneshot timer of 5000000us will be started when every member of the fan
733inventory group has a value of true. Otherwise, the timer will be stopped if
734it's running.
735
736If the group configuration has no property value listed, like:
737
738```json
739{
740  "name": "fan inventory",
741  "interface": "xyz.openbmc_project.Inventory.Item",
742  "property": { "name": "Present" }
743}
744```
745
746Then the timer will be started when any service providing a group member isn't
747owned (on D-Bus). Otherwise, it will stop the timer if it's running.
748
749### get_managed_objects
750
751This action adds the members of its groups to the object cache by using the
752GetManagedObjects D-Bus method to find and add the results. When that is done,
753it then runs any actions listed in the JSON.
754
755This allows an action to run with the latest values in the cache without having
756to subscribe to propertiesChanged for them all.
757
758```json
759{
760  "name": "get_managed_objects",
761  "groups": [
762    {
763      "name": "proc temps",
764      "interface": "xyz.openbmc_project.Sensor.Value",
765      "property": { "name": "Value" }
766    }
767  ],
768  "actions": [
769    {
770      "name": "set_net_increase_target",
771      "state": 30,
772      "delta": 100
773    }
774  ]
775}
776```
777
778The above config will make the GetManagedObjects call on all services that own
779the configured groups and then add all resulting property values to the object
780cache. After that, it will call the `set_net_increase_target` action using the
781same groups.
782
783## Modifiers
784
785Modifiers are used by some actions to help calculate values.
786
787### minus
788
789Subtract the `value` field from the passed in value.
790
791```json
792"modifier": {
793    "expression": "minus",
794    "value": 4
795}
796```
797
798The above config subtracts 4 from what is passed to it.
799
800### less_than
801
802Returns a value from a data table that is selected when the argument passed in
803is less than the `arg_value` entry in the table row. If there is a
804`default_value` field supplied, then that will be returned if the argument is
805greater than the `arg_value` of the last row.
806
807```json
808"modifier": {
809  "operator": "less_than",
810  "default_value": 10000,
811  "value": [
812      { "arg_value": 500, "parameter_value": 1 },
813      { "arg_value": 1000, "parameter_value": 2 },
814      { "arg_value": 1500, "parameter_value": 3 }
815  ]
816}
817```
818
819The above config returns 1 if the arg passed is less than 500, 2 if less than
8201000, and 3 if less than 1500. Otherwise returns 10000, the default value.
821