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