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