1\ingroup GroupModules Modules 2\defgroup GroupSCMI_PERF SCMI Performance Domain Management Protocol 3 4# Performance Plugins Handler Architecture 5 6Copyright (c) 2021-2022, Arm Limited and Contributors. All rights reserved. 7 8## Overview 9The performance of a platform with respect to voltage and frequency can be 10managed via the SCMI performance protocol. 11 12The ability of the platform to run at the request from the agent may be 13subjected to hardware or software limitations. Examples can be electrical 14limitations or temperature limitations. 15 16Such limitations can be imposed by implementing new modules in SCP-firmware, 17which affect the final performance level that the platform will run in. 18 19Such modules are interposed between the SCMI performance interface module and 20the DVFS module. In this manner, the above modules can affect the 21final performance level/limits sent to DVFS. 22 23Since there can be many modules that may be affecting the final performance 24level, a *scalable* mechanism to insert or remove modules at compile-time is 25implemented. 26 27Such modules are called (performance) plugins and the entire machinery of 28insertion/removal as well as interaction with SCMI performance is managed by the 29*perf_plugins_handler* extension of SCMI performance. 30 31## Architecture 32 33### No plugins 34 35A scenario where a request for a performance limit is not *affected* by any 36other performance-related monitor/plugins. Simply scmi_performance module calls 37DVFS HAL. 38 39 +-----------+ +-----------+ 40 | +------------------------------------->+ | 41 | scmi-perf + + DVFS | 42 | +<-------------------------------------+ | 43 +-----------+ +-----------+ 44 45 46### With (performance) plugins 47 48In a scenario where some additional modules need to affect the performance 49limits, these are called prior to submit the request to DVFS. The plugins 50handler calls each of the plugins with the information regarding the performance 51request. In the same call, plugins can adjust the performance request according 52to their internal algorithms. 53 54Once all the plugins have been called, the plugins handler will provide SCMI 55performance with the adjusted performance limits which, in turn, will submit the 56request to DVFS accordingly. 57 58 +-----------+ +-----------+ 59 | +<------------------------------------>+ | 60 | scmi-perf +<--+ | DVFS | 61 | | | | | 62 +-----+- - -+---v-+ +-----------+ +-----------+ 63 | | | | 64 | plugins +<------->+ plugin x | 65 | handler +<---+ | | 66 +-----------+ | +-----------+ 67 | +-----------+ 68 | | | 69 +--->+ plugin y | 70 | | 71 +-----------+ 72 73The machinery relies on SCMI FastChannels as a source of performance requests 74and for its periodic polling. At every polling, the performance plugins are 75given the performance values and the adjusted values are returned on the same 76call via a data structure. 77 78Plugins can take advantage of this regular call for their internal algorithms. 79Note that there will always be a periodic call, regardless whether or not there 80is a "new" performance request in the FastChannels. 81 82### Asynchronous calls 83 84In some circumstances a plugin may needs to set a limit to the performance 85driver before waiting for the next tick. This can be done by using the provided 86perf_plugins_handler_api. 87This interface should be used only when the limit set cannot wait till the next 88periodic call, because of a significant performance degradation. 89A plugin should use the regular update call to affect the performance state 90whenever possible. 91 92### Min/Max limits policy 93 94The final values that will be transferred to DVFS are decided with the following 95rule: 96 97 final_perf_max_limit = MIN(agent_requested_limit, plugin_adjusted_limit) 98 99 final_perf_min_limit = MAX(agent_requested_limit, plugin_adjusted_limit) 100 101### Domain aggregation (physical/logical) 102 103Plugins_handler also implements a basic form of domain aggregation to support 104situations where the performance domains exposed to OS through SCMI are 105different from those that are available in the platform. These are respectively 106logical (performance control) and physical (aka dependency domain). 107 108A typical example is a platform where SCP exposes per-cpu controls through SCMI 109Performance while having fewer physical domains (DVFS on a cluster-basis or a 110group of CPUs for example). In this case, there is a need to collect and combine 111the different requests for the logical domains into a final single value that is 112the one to be applied to the physical domain. This aggregation is performed by 113the plugins handler. 114 115In the platform configuration, a plugin can choose whether its view is on 116logical or physical domains. 117A plugin is updated when the data for all the logical domains, within the same 118dependency domain, is collected. 119This way the plugins will have a snapshot of the performance requests for all 120the logical domains within a physical domain, and the domain identifier provided 121is as follow: 122 FWK_ID_SUB_ELEMENT(FWK_MODULE_IDX_SCMI_PERF, 123 physical_domain, last_logical_domain) 124 125For plugins whose view is on physical domains, requests coming from logical 126domains will need to appear as they came from a single physical domain (if 127logical domains are present), and this is done as part of the aggregation 128policy. Those plugins will receive an update with an aggregated value and the 129domain identifier is as above. In this case, the sub_element index can be 130ignored. 131 132 Example: Aggregation of logical values 133 134 +------------+-----------+-----------+-----------+ +------------------+ 135 | | log dom 0 | log dom 1 | phy dom | | plugin receives | 136 +------------------------------------------------+ +------------------+ 137 | limit_max | 1000 | 950 | 950 | | 950 | 138 +------------------------------------------------+ +------------------+ 139 | limit_min | 200 | 100 | 200 | | 200 | 140 +------------+-----------+-----------+-----------+ +------------------+ 141 142Then the same Min/Max policy described above applies when comparing values from 143OS and adjusted values from a plugin. 144 145NOTE: The plugins handler can handle the domain aggregation without the plugins. 146If only domain aggregation is required, there is no need to have performance 147plugins. 148 149### Data exchange 150 151Each plugin will receive a snapshot of the performance domain level and limits 152values at every tick (SCMI FastChannels) and it can modify the limits if it 153wishes to affect the original request coming from the SCMI interface. It is 154optional for the plugin to update the values. 155 156It is also possible for a plugin to receive a full snapshot of the performance 157level and limits for all the performance domains. This can be chosen in the 158module configuration. In this case, a plugin will be called once at every 159tick period. 160 161Possible configurations & data received: 162 163 +----------------------+------------------------------------------------+ 164 | config->dom_type | data received | 165 | (plugin_domain_type) | (perf_plugins_perf_update) | 166 +----------------------+------------------------------------------------+ 167 | _TYPE_PHYSICAL | Single physical domain. | 168 | | A plugin gets a call for each physical domain | 169 | | every tick period. | 170 | | | 171 | | Example, 3 physical domains: | 172 | | A plugin which implements the `update` function| 173 | | will be called 3 times (at every tick period): | 174 | | 1st call with data for domain 0 | 175 | | 2nd call with data for domain 1 | 176 | | 3rd call with data for domain 2 | 177 +----------------------+------------------------------------------------+ 178 | _TYPE_LOGICAL | Array of logical domains (within same physical)| 179 | | A plugin gets a call for each set of logical | 180 | | domains every tick period. | 181 | | | 182 | | Example, 1 physical domain with 3 logical | 183 | | domains: | 184 | | A plugin which implements the `update` function| 185 | | will be called 1 time (at every tick period): | 186 | | 1st call with data array for logical domains 0,| 187 +----------------------+------------------------------------------------+ 188 | _TYPE_FULL | Array of physical domains | 189 | | A plugin gets called once for all the physical | 190 | | domains every tick period. | 191 | | | 192 | | Example, 3 physical domains: | 193 | | A plugin which implements the `update` function| 194 | | will be called once (at every tick period): | 195 | | 1st call with data array for domain 0, 1 and 2 | 196 +----------------------+------------------------------------------------+ 197 198 199## Use 200 201Each plugin is expected to implement the update() function as specified in the 202perf_plugins_api documentation. 203They are also supposed to allow the scmi_performance module to bind to them and 204allow the above API to be bound. 205 206The whole feature is enabled by BS_FIRMWARE_HAS_PERF_PLUGIN_HANDLER (make) or 207SCP_ENABLE_PLUGIN_HANDLER_INIT (CMake). 208 209## Restrictions 210- A platform wishing to use the plugins handler, needs to have FastChannels 211implemented (and enabled). 212- This entire mechanism for adding/removing plugins is not supported with the 213traditional SCMI SMT/doorbell transport. 214- The order in which the plugins are listed in the plugins table (module's 215configuration )is the order in which they will be called by the plugins handler. 216However, plugins whose view is _TYPE_FULL should be placed as last entries in 217the table of plugins in the module's configuration. This is to ensure that all 218the other plugins have run before a consolidated full picture of the 219level/limits can be shared with the remaining plugins. 220 221## Configuration Example 1 (plugin with physical/DVFS domains view) 222 223 static const struct mod_scmi_perf_domain_config domains[] = { 224 [DVFS_DOMAIN_0] = { 225 .fast_channels_addr_scp = (uint64_t[]) { ... }, 226 .fast_channels_addr_ap = (uint64_t[]) { ... }, 227 }, 228 ... 229 [DVFS_DOMAIN_n] = { 230 .fast_channels_addr_scp = (uint64_t[]) { ... }, 231 .fast_channels_addr_ap = (uint64_t[]) { ... }, 232 }, 233 }; 234 235 static const struct mod_scmi_plugin_config plugins_table[] = { 236 [0] = { 237 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN>), 238 .dom_type = PERF_PLUGIN_DOM_TYPE_PHYSICAL, 239 }, 240 }; 241 242 struct fwk_module_config config_scmi_perf = { 243 .data = &((struct mod_scmi_perf_config){ 244 ... 245 .plugins = plugins_table, 246 .plugins_count = FWK_ARRAY_SIZE(plugins_table) 247 }), 248 }; 249 250## Configuration Example 2 (plugin with logical/SCMI domains view, 2 logical 251 domains, 1 DVFS domain) 252 253 static const struct mod_scmi_perf_domain_config domains[] = { 254 [SCMI_PERF_DOMAIN_0] = { 255 .fast_channels_addr_scp = (uint64_t[]) { ... }, 256 .fast_channels_addr_ap = (uint64_t[]) { ... }, 257 .phy_group_id = 258 FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DVFS, DVFS_ELEMENT_IDX_0), 259 }, 260 ... 261 [SCMI_PERF_DOMAIN_1] = { 262 .fast_channels_addr_scp = (uint64_t[]) { ... }, 263 .fast_channels_addr_ap = (uint64_t[]) { ... }, 264 .phy_group_id = 265 FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DVFS, DVFS_ELEMENT_IDX_0), 266 }, 267 }; 268 269 static const struct mod_scmi_plugin_config plugins_table[] = { 270 [0] = { 271 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN>), 272 .dom_type = PERF_PLUGIN_DOM_TYPE_LOGICAL, 273 }, 274 }; 275 276 struct fwk_module_config config_scmi_perf = { 277 .data = &((struct mod_scmi_perf_config){ 278 ... 279 .plugins = plugins_table, 280 .plugins_count = FWK_ARRAY_SIZE(plugins_table) 281 }), 282 }; 283 284Note that a plugin in this case can choose either logical or physical view. 285 286## Configuration Example 3 (no plugins, 2 logical domains, 1 DVFS domain) 287 288 static const struct mod_scmi_perf_domain_config domains[] = { 289 [SCMI_PERF_DOMAIN_0] = { 290 .fast_channels_addr_scp = (uint64_t[]) { ... }, 291 .fast_channels_addr_ap = (uint64_t[]) { ... }, 292 .phy_group_id = 293 FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DVFS, DVFS_ELEMENT_IDX_0), 294 }, 295 ... 296 [SCMI_PERF_DOMAIN_1] = { 297 .fast_channels_addr_scp = (uint64_t[]) { ... }, 298 .fast_channels_addr_ap = (uint64_t[]) { ... }, 299 .phy_group_id = 300 FWK_ID_ELEMENT_INIT(FWK_MODULE_IDX_DVFS, DVFS_ELEMENT_IDX_0), 301 }, 302 }; 303 304This example is for cases when only the logical domains aggregation policy is 305required. 306 307## Configuration Example 4 (2 plugins with physical domain view) 308 309 static const struct mod_scmi_perf_domain_config domains[] = { 310 [DVFS_DOMAIN_0] = { 311 .fast_channels_addr_scp = (uint64_t[]) { ... }, 312 .fast_channels_addr_ap = (uint64_t[]) { ... }, 313 }, 314 }; 315 316 static const struct mod_scmi_plugin_config plugins_table[] = { 317 [0] = { 318 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN>), 319 .dom_type = PERF_PLUGIN_DOM_TYPE_PHYSICAL, 320 }, 321 [1] = { 322 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN>), 323 .dom_type = PERF_PLUGIN_DOM_TYPE_PHYSICAL, 324 }, 325 }; 326 327 struct fwk_module_config config_scmi_perf = { 328 .data = &((struct mod_scmi_perf_config){ 329 ... 330 .plugins = plugins_table, 331 .plugins_count = FWK_ARRAY_SIZE(plugins_table) 332 }), 333 }; 334 335## Configuration Example 5 (3 plugins with different domain view) 336 337 static const struct mod_scmi_perf_domain_config domains[] = { 338 [DVFS_DOMAIN_0] = { 339 .fast_channels_addr_scp = (uint64_t[]) { ... }, 340 .fast_channels_addr_ap = (uint64_t[]) { ... }, 341 }, 342 ... 343 [DVFS_DOMAIN_n] = { ... }, 344 }; 345 346 static const struct mod_scmi_plugin_config plugins_table[] = { 347 [0] = { 348 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN0>), 349 .dom_type = PERF_PLUGIN_DOM_TYPE_<PHYSICAL/LOGICAL>, 350 }, 351 [1] = { 352 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN1>), 353 .dom_type = PERF_PLUGIN_DOM_TYPE_<PHYSICAL/LOGICAL>, 354 }, 355 /* 356 * This plugin id needs to be placed as last entry because its view is 357 * _TYPE_FULL (see restrictions section). 358 */ 359 [2] = { 360 .id = FWK_ID_MODULE_INIT(FWK_MODULE_IDX_<PLUGIN2>), 361 .dom_type = PERF_PLUGIN_DOM_TYPE_FULL, 362 }, 363 }; 364 365 struct fwk_module_config config_scmi_perf = { 366 .data = &((struct mod_scmi_perf_config){ 367 ... 368 .plugins = plugins_table, 369 .plugins_count = FWK_ARRAY_SIZE(plugins_table) 370 }), 371 }; 372 373NOTE: In this example no.5 the order of plugin callbacks is the following: 374- plugin 0 with physical/logical domain 0 375- plugin 1 with physical/logical domain 0 376- plugin 0 with physical/logical domain n 377- plugin 1 with physical/logical domain n 378- plugin 2 with physical/logical all domains 379