Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2010-2021 Intel Corporation
3 : : * Copyright(c) 2021 Arm Limited
4 : : * Copyright(c) 2024 Advanced Micro Devices, Inc.
5 : : */
6 : :
7 : : #include <stdlib.h>
8 : :
9 : : #include <rte_memcpy.h>
10 : : #include <rte_stdatomic.h>
11 : :
12 : : #include "amd_pstate_cpufreq.h"
13 : : #include "power_common.h"
14 : :
15 : : /* macros used for rounding frequency to nearest 1000 */
16 : : #define FREQ_ROUNDING_DELTA 500
17 : : #define ROUND_FREQ_TO_N_1000 1000
18 : :
19 : : #define POWER_CONVERT_TO_DECIMAL 10
20 : :
21 : : #define POWER_GOVERNOR_USERSPACE "userspace"
22 : : #define POWER_SYSFILE_SETSPEED \
23 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_setspeed"
24 : : #define POWER_SYSFILE_SCALING_MAX_FREQ \
25 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq"
26 : : #define POWER_SYSFILE_SCALING_MIN_FREQ \
27 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_min_freq"
28 : : #define POWER_SYSFILE_HIGHEST_PERF \
29 : : "/sys/devices/system/cpu/cpu%u/acpi_cppc/highest_perf"
30 : : #define POWER_SYSFILE_NOMINAL_PERF \
31 : : "/sys/devices/system/cpu/cpu%u/acpi_cppc/nominal_perf"
32 : : #define POWER_SYSFILE_NOMINAL_FREQ \
33 : : "/sys/devices/system/cpu/cpu%u/acpi_cppc/nominal_freq"
34 : :
35 : : #define POWER_AMD_PSTATE_DRIVER "amd-pstate"
36 : : #define BUS_FREQ 1000 /* khz */
37 : :
38 : : enum power_state {
39 : : POWER_IDLE = 0,
40 : : POWER_ONGOING,
41 : : POWER_USED,
42 : : POWER_UNKNOWN
43 : : };
44 : :
45 : : /**
46 : : * Power info per lcore.
47 : : */
48 : : struct __rte_cache_aligned amd_pstate_power_info {
49 : : uint32_t lcore_id; /**< Logical core id */
50 : : RTE_ATOMIC(uint32_t) state; /**< Power in use state */
51 : : FILE *f; /**< FD of scaling_setspeed */
52 : : char governor_ori[28]; /**< Original governor name */
53 : : uint32_t curr_idx; /**< Freq index in freqs array */
54 : : uint32_t nom_idx; /**< Nominal index in freqs array */
55 : : uint32_t highest_perf; /**< system wide max freq */
56 : : uint32_t nominal_perf; /**< system wide nominal freq */
57 : : uint16_t turbo_available; /**< Turbo Boost available */
58 : : uint16_t turbo_enable; /**< Turbo Boost enable/disable */
59 : : uint32_t nb_freqs; /**< number of available freqs */
60 : : uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
61 : : };
62 : :
63 : : static struct amd_pstate_power_info lcore_power_info[RTE_MAX_LCORE];
64 : :
65 : : /**
66 : : * It is to set specific freq for specific logical core, according to the index
67 : : * of supported frequencies.
68 : : */
69 : : static int
70 : 0 : set_freq_internal(struct amd_pstate_power_info *pi, uint32_t idx)
71 : : {
72 [ # # # # ]: 0 : if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
73 : 0 : POWER_LOG(ERR, "Invalid frequency index %u, which "
74 : : "should be less than %u", idx, pi->nb_freqs);
75 : 0 : return -1;
76 : : }
77 : :
78 : : /* Check if it is the same as current */
79 [ # # ]: 0 : if (idx == pi->curr_idx)
80 : : return 0;
81 : :
82 : : POWER_DEBUG_LOG("Frequency[%u] %u to be set for lcore %u",
83 : : idx, pi->freqs[idx], pi->lcore_id);
84 [ # # ]: 0 : if (fseek(pi->f, 0, SEEK_SET) < 0) {
85 : 0 : POWER_LOG(ERR, "Fail to set file position indicator to 0 "
86 : : "for setting frequency for lcore %u", pi->lcore_id);
87 : 0 : return -1;
88 : : }
89 [ # # ]: 0 : if (fprintf(pi->f, "%u", pi->freqs[idx]) < 0) {
90 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
91 : : "lcore %u", pi->lcore_id);
92 : 0 : return -1;
93 : : }
94 : 0 : fflush(pi->f);
95 : 0 : pi->curr_idx = idx;
96 : :
97 : 0 : return 1;
98 : : }
99 : :
100 : : /**
101 : : * It is to check the current scaling governor by reading sys file, and then
102 : : * set it into 'userspace' if it is not by writing the sys file. The original
103 : : * governor will be saved for rolling back.
104 : : */
105 : : static int
106 : : power_set_governor_userspace(struct amd_pstate_power_info *pi)
107 : : {
108 : 0 : return power_set_governor(pi->lcore_id, POWER_GOVERNOR_USERSPACE,
109 : 0 : pi->governor_ori, sizeof(pi->governor_ori));
110 : : }
111 : :
112 : : static int
113 : 0 : power_check_turbo(struct amd_pstate_power_info *pi)
114 : : {
115 : 0 : FILE *f_nom = NULL, *f_max = NULL;
116 : : int ret = -1;
117 : 0 : uint32_t nominal_perf = 0, highest_perf = 0;
118 : :
119 : 0 : open_core_sysfs_file(&f_max, "r", POWER_SYSFILE_HIGHEST_PERF,
120 : : pi->lcore_id);
121 [ # # ]: 0 : if (f_max == NULL) {
122 : 0 : POWER_LOG(ERR, "failed to open %s",
123 : : POWER_SYSFILE_HIGHEST_PERF);
124 : 0 : goto err;
125 : : }
126 : :
127 : 0 : open_core_sysfs_file(&f_nom, "r", POWER_SYSFILE_NOMINAL_PERF,
128 : : pi->lcore_id);
129 [ # # ]: 0 : if (f_nom == NULL) {
130 : 0 : POWER_LOG(ERR, "failed to open %s",
131 : : POWER_SYSFILE_NOMINAL_PERF);
132 : 0 : goto err;
133 : : }
134 : :
135 : 0 : ret = read_core_sysfs_u32(f_max, &highest_perf);
136 [ # # ]: 0 : if (ret < 0) {
137 : 0 : POWER_LOG(ERR, "Failed to read %s",
138 : : POWER_SYSFILE_HIGHEST_PERF);
139 : 0 : goto err;
140 : : }
141 : :
142 : 0 : ret = read_core_sysfs_u32(f_nom, &nominal_perf);
143 [ # # ]: 0 : if (ret < 0) {
144 : 0 : POWER_LOG(ERR, "Failed to read %s",
145 : : POWER_SYSFILE_NOMINAL_PERF);
146 : 0 : goto err;
147 : : }
148 : :
149 : 0 : pi->highest_perf = highest_perf;
150 : 0 : pi->nominal_perf = nominal_perf;
151 : :
152 [ # # ]: 0 : if (highest_perf > nominal_perf) {
153 : 0 : pi->turbo_available = 1;
154 : 0 : pi->turbo_enable = 1;
155 : : ret = 0;
156 : : POWER_DEBUG_LOG("Lcore %u can do Turbo Boost! highest perf %u, "
157 : : "nominal perf %u",
158 : : pi->lcore_id, highest_perf, nominal_perf);
159 : : } else {
160 : 0 : pi->turbo_available = 0;
161 : 0 : pi->turbo_enable = 0;
162 : : POWER_DEBUG_LOG("Lcore %u Turbo not available! highest perf %u, "
163 : : "nominal perf %u",
164 : : pi->lcore_id, highest_perf, nominal_perf);
165 : : }
166 : :
167 : 0 : err:
168 [ # # ]: 0 : if (f_max != NULL)
169 : 0 : fclose(f_max);
170 [ # # ]: 0 : if (f_nom != NULL)
171 : 0 : fclose(f_nom);
172 : :
173 : 0 : return ret;
174 : : }
175 : :
176 : : /**
177 : : * It is to get the available frequencies of the specific lcore by reading the
178 : : * sys file.
179 : : */
180 : : static int
181 : 0 : power_get_available_freqs(struct amd_pstate_power_info *pi)
182 : : {
183 : 0 : FILE *f_min = NULL, *f_max = NULL, *f_nom = NULL;
184 : : int ret = -1, nominal_idx = -1;
185 : 0 : uint32_t scaling_min_freq = 0, scaling_max_freq = 0;
186 : : uint32_t i, num_freqs = RTE_MAX_LCORE_FREQS;
187 : 0 : uint32_t nominal_freq = 0, scaling_freq = 0;
188 : : uint32_t freq_calc = 0;
189 : :
190 : 0 : open_core_sysfs_file(&f_max, "r", POWER_SYSFILE_SCALING_MAX_FREQ,
191 : : pi->lcore_id);
192 [ # # ]: 0 : if (f_max == NULL) {
193 : 0 : POWER_LOG(ERR, "failed to open %s",
194 : : POWER_SYSFILE_SCALING_MAX_FREQ);
195 : 0 : goto out;
196 : : }
197 : :
198 : 0 : open_core_sysfs_file(&f_min, "r", POWER_SYSFILE_SCALING_MIN_FREQ,
199 : : pi->lcore_id);
200 [ # # ]: 0 : if (f_min == NULL) {
201 : 0 : POWER_LOG(ERR, "failed to open %s",
202 : : POWER_SYSFILE_SCALING_MIN_FREQ);
203 : 0 : goto out;
204 : : }
205 : :
206 : 0 : open_core_sysfs_file(&f_nom, "r", POWER_SYSFILE_NOMINAL_FREQ,
207 : : pi->lcore_id);
208 [ # # ]: 0 : if (f_nom == NULL) {
209 : 0 : POWER_LOG(ERR, "failed to open %s",
210 : : POWER_SYSFILE_NOMINAL_FREQ);
211 : 0 : goto out;
212 : : }
213 : :
214 : 0 : ret = read_core_sysfs_u32(f_max, &scaling_max_freq);
215 [ # # ]: 0 : if (ret < 0) {
216 : 0 : POWER_LOG(ERR, "Failed to read %s",
217 : : POWER_SYSFILE_SCALING_MAX_FREQ);
218 : 0 : goto out;
219 : : }
220 : :
221 : 0 : ret = read_core_sysfs_u32(f_min, &scaling_min_freq);
222 [ # # ]: 0 : if (ret < 0) {
223 : 0 : POWER_LOG(ERR, "Failed to read %s",
224 : : POWER_SYSFILE_SCALING_MIN_FREQ);
225 : 0 : goto out;
226 : : }
227 : :
228 : 0 : ret = read_core_sysfs_u32(f_nom, &nominal_freq);
229 [ # # ]: 0 : if (ret < 0) {
230 : 0 : POWER_LOG(ERR, "Failed to read %s",
231 : : POWER_SYSFILE_NOMINAL_FREQ);
232 : 0 : goto out;
233 : : }
234 : :
235 : 0 : power_check_turbo(pi);
236 : :
237 [ # # ]: 0 : if (scaling_max_freq < scaling_min_freq) {
238 : 0 : POWER_LOG(ERR, "scaling min freq exceeds max freq, "
239 : : "not expected! Check system power policy");
240 : 0 : goto out;
241 [ # # ]: 0 : } else if (scaling_max_freq == scaling_min_freq) {
242 : : num_freqs = 1;
243 : : }
244 : :
245 : : if (num_freqs > 1) {
246 : 0 : scaling_freq = (scaling_max_freq - scaling_min_freq);
247 : 0 : scaling_freq <<= 10;
248 : 0 : scaling_freq /= (num_freqs - 1);
249 : 0 : scaling_freq >>= 10;
250 : : } else {
251 : : scaling_freq = 0;
252 : : }
253 : :
254 : : /* Generate the freq bucket array. */
255 [ # # ]: 0 : for (i = 0, pi->nb_freqs = 0; i < num_freqs; i++) {
256 : 0 : freq_calc = scaling_max_freq - (i * scaling_freq);
257 : : /* convert the frequency to nearest 1000 value
258 : : * Ex: if freq=1396789 then freq_conv=1397000
259 : : * Ex: if freq=800030 then freq_conv=800000
260 : : */
261 : 0 : freq_calc = (freq_calc + FREQ_ROUNDING_DELTA)
262 : : / ROUND_FREQ_TO_N_1000;
263 : 0 : freq_calc = freq_calc * ROUND_FREQ_TO_N_1000;
264 : :
265 : : /* update the frequency table only if required */
266 [ # # ]: 0 : if ((pi->nb_freqs == 0) ||
267 [ # # ]: 0 : pi->freqs[pi->nb_freqs-1] != freq_calc) {
268 : 0 : pi->freqs[pi->nb_freqs++] = freq_calc;
269 : : }
270 [ # # ]: 0 : if (nominal_idx == -1) {
271 [ # # ]: 0 : if ((nominal_freq * BUS_FREQ) >= freq_calc) {
272 : 0 : pi->nom_idx = pi->nb_freqs - 1;
273 : 0 : nominal_idx = pi->nom_idx;
274 : : }
275 : : }
276 : : }
277 : :
278 : : ret = 0;
279 : :
280 : : POWER_DEBUG_LOG("%d frequency(s) of lcore %u are available",
281 : : num_freqs, pi->lcore_id);
282 : :
283 : 0 : out:
284 [ # # ]: 0 : if (f_min != NULL)
285 : 0 : fclose(f_min);
286 [ # # ]: 0 : if (f_max != NULL)
287 : 0 : fclose(f_max);
288 [ # # ]: 0 : if (f_nom != NULL)
289 : 0 : fclose(f_nom);
290 : :
291 : 0 : return ret;
292 : : }
293 : :
294 : : /**
295 : : * It is to fopen the sys file for the future setting the lcore frequency.
296 : : */
297 : : static int
298 : 0 : power_init_for_setting_freq(struct amd_pstate_power_info *pi)
299 : : {
300 : 0 : FILE *f = NULL;
301 : : char buf[BUFSIZ];
302 : : uint32_t i, freq;
303 : : int ret;
304 : :
305 : 0 : open_core_sysfs_file(&f, "rw+", POWER_SYSFILE_SETSPEED, pi->lcore_id);
306 [ # # ]: 0 : if (f == NULL) {
307 : 0 : POWER_LOG(ERR, "failed to open %s",
308 : : POWER_SYSFILE_SETSPEED);
309 : 0 : goto err;
310 : : }
311 : :
312 : 0 : ret = read_core_sysfs_s(f, buf, sizeof(buf));
313 [ # # ]: 0 : if (ret < 0) {
314 : 0 : POWER_LOG(ERR, "Failed to read %s",
315 : : POWER_SYSFILE_SETSPEED);
316 : 0 : goto err;
317 : : }
318 : :
319 : 0 : freq = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL);
320 : :
321 : : /* convert the frequency to nearest 1000 value
322 : : * Ex: if freq=1396789 then freq_conv=1397000
323 : : * Ex: if freq=800030 then freq_conv=800000
324 : : */
325 : : unsigned int freq_conv = 0;
326 : 0 : freq_conv = (freq + FREQ_ROUNDING_DELTA)
327 : : / ROUND_FREQ_TO_N_1000;
328 : 0 : freq_conv = freq_conv * ROUND_FREQ_TO_N_1000;
329 : :
330 [ # # ]: 0 : for (i = 0; i < pi->nb_freqs; i++) {
331 [ # # ]: 0 : if (freq_conv == pi->freqs[i]) {
332 : 0 : pi->curr_idx = i;
333 : 0 : pi->f = f;
334 : 0 : return 0;
335 : : }
336 : : }
337 : :
338 : 0 : err:
339 [ # # ]: 0 : if (f != NULL)
340 : 0 : fclose(f);
341 : :
342 : : return -1;
343 : : }
344 : :
345 : : int
346 : 1 : power_amd_pstate_cpufreq_check_supported(void)
347 : : {
348 : 1 : return cpufreq_check_scaling_driver(POWER_AMD_PSTATE_DRIVER);
349 : : }
350 : :
351 : : int
352 : 1 : power_amd_pstate_cpufreq_init(unsigned int lcore_id)
353 : : {
354 : : struct amd_pstate_power_info *pi;
355 : : uint32_t exp_state;
356 : :
357 [ + - ]: 1 : if (!power_amd_pstate_cpufreq_check_supported()) {
358 : 1 : POWER_LOG(ERR, "%s driver is not supported",
359 : : POWER_AMD_PSTATE_DRIVER);
360 : 1 : return -1;
361 : : }
362 : :
363 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
364 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
365 : : lcore_id, RTE_MAX_LCORE - 1U);
366 : 0 : return -1;
367 : : }
368 : :
369 : 0 : pi = &lcore_power_info[lcore_id];
370 : : exp_state = POWER_IDLE;
371 : : /* The power in use state works as a guard variable between
372 : : * the CPU frequency control initialization and exit process.
373 : : * The ACQUIRE memory ordering here pairs with the RELEASE
374 : : * ordering below as lock to make sure the frequency operations
375 : : * in the critical section are done under the correct state.
376 : : */
377 [ # # ]: 0 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state),
378 : : &exp_state, POWER_ONGOING,
379 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
380 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
381 : : "in use", lcore_id);
382 : 0 : return -1;
383 : : }
384 : :
385 [ # # ]: 0 : if (power_get_lcore_mapped_cpu_id(lcore_id, &pi->lcore_id) < 0) {
386 : 0 : POWER_LOG(ERR, "Cannot get CPU ID mapped for lcore %u", lcore_id);
387 : 0 : return -1;
388 : : }
389 : :
390 : : /* Check and set the governor */
391 [ # # ]: 0 : if (power_set_governor_userspace(pi) < 0) {
392 : 0 : POWER_LOG(ERR, "Cannot set governor of lcore %u to "
393 : : "userspace", lcore_id);
394 : 0 : goto fail;
395 : : }
396 : :
397 : : /* Get the available frequencies */
398 [ # # ]: 0 : if (power_get_available_freqs(pi) < 0) {
399 : 0 : POWER_LOG(ERR, "Cannot get available frequencies of "
400 : : "lcore %u", lcore_id);
401 : 0 : goto fail;
402 : : }
403 : :
404 : : /* Init for setting lcore frequency */
405 [ # # ]: 0 : if (power_init_for_setting_freq(pi) < 0) {
406 : 0 : POWER_LOG(ERR, "Cannot init for setting frequency for "
407 : : "lcore %u", lcore_id);
408 : 0 : goto fail;
409 : : }
410 : :
411 : : /* Set freq to max by default */
412 [ # # ]: 0 : if (power_amd_pstate_cpufreq_freq_max(lcore_id) < 0) {
413 : 0 : POWER_LOG(ERR, "Cannot set frequency of lcore %u "
414 : : "to max", lcore_id);
415 : 0 : goto fail;
416 : : }
417 : :
418 : 0 : POWER_LOG(INFO, "Initialized successfully for lcore %u "
419 : : "power management", lcore_id);
420 : :
421 : 0 : rte_atomic_store_explicit(&(pi->state), POWER_USED, rte_memory_order_release);
422 : :
423 : 0 : return 0;
424 : :
425 : 0 : fail:
426 : 0 : rte_atomic_store_explicit(&(pi->state), POWER_UNKNOWN, rte_memory_order_release);
427 : 0 : return -1;
428 : : }
429 : :
430 : : /**
431 : : * It is to check the governor and then set the original governor back if
432 : : * needed by writing the sys file.
433 : : */
434 : : static int
435 : : power_set_governor_original(struct amd_pstate_power_info *pi)
436 : : {
437 : 0 : return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
438 : : }
439 : :
440 : : int
441 : 0 : power_amd_pstate_cpufreq_exit(unsigned int lcore_id)
442 : : {
443 : : struct amd_pstate_power_info *pi;
444 : : uint32_t exp_state;
445 : :
446 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
447 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
448 : : lcore_id, RTE_MAX_LCORE - 1U);
449 : 0 : return -1;
450 : : }
451 : : pi = &lcore_power_info[lcore_id];
452 : : exp_state = POWER_USED;
453 : : /* The power in use state works as a guard variable between
454 : : * the CPU frequency control initialization and exit process.
455 : : * The ACQUIRE memory ordering here pairs with the RELEASE
456 : : * ordering below as lock to make sure the frequency operations
457 : : * in the critical section are done under the correct state.
458 : : */
459 [ # # ]: 0 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state),
460 : : &exp_state, POWER_ONGOING,
461 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
462 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
463 : : "not used", lcore_id);
464 : 0 : return -1;
465 : : }
466 : :
467 : : /* Close FD of setting freq */
468 : 0 : fclose(pi->f);
469 : 0 : pi->f = NULL;
470 : :
471 : : /* Set the governor back to the original */
472 [ # # ]: 0 : if (power_set_governor_original(pi) < 0) {
473 : 0 : POWER_LOG(ERR, "Cannot set the governor of %u back "
474 : : "to the original", lcore_id);
475 : 0 : goto fail;
476 : : }
477 : :
478 : 0 : POWER_LOG(INFO, "Power management of lcore %u has exited from "
479 : : "'userspace' mode and been set back to the "
480 : : "original", lcore_id);
481 : 0 : rte_atomic_store_explicit(&(pi->state), POWER_IDLE, rte_memory_order_release);
482 : :
483 : 0 : return 0;
484 : :
485 : : fail:
486 : 0 : rte_atomic_store_explicit(&(pi->state), POWER_UNKNOWN, rte_memory_order_release);
487 : :
488 : 0 : return -1;
489 : : }
490 : :
491 : : uint32_t
492 : 0 : power_amd_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
493 : : {
494 : : struct amd_pstate_power_info *pi;
495 : :
496 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
497 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
498 : 0 : return 0;
499 : : }
500 : :
501 [ # # ]: 0 : if (freqs == NULL) {
502 : 0 : POWER_LOG(ERR, "NULL buffer supplied");
503 : 0 : return 0;
504 : : }
505 : :
506 : : pi = &lcore_power_info[lcore_id];
507 [ # # ]: 0 : if (num < pi->nb_freqs) {
508 : 0 : POWER_LOG(ERR, "Buffer size is not enough");
509 : 0 : return 0;
510 : : }
511 [ # # ]: 0 : rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
512 : :
513 : 0 : return pi->nb_freqs;
514 : : }
515 : :
516 : : uint32_t
517 : 0 : power_amd_pstate_cpufreq_get_freq(unsigned int lcore_id)
518 : : {
519 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
520 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
521 : 0 : return RTE_POWER_INVALID_FREQ_INDEX;
522 : : }
523 : :
524 : 0 : return lcore_power_info[lcore_id].curr_idx;
525 : : }
526 : :
527 : : int
528 : 0 : power_amd_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
529 : : {
530 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
531 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
532 : 0 : return -1;
533 : : }
534 : :
535 : 0 : return set_freq_internal(&(lcore_power_info[lcore_id]), index);
536 : : }
537 : :
538 : : int
539 : 0 : power_amd_pstate_cpufreq_freq_down(unsigned int lcore_id)
540 : : {
541 : : struct amd_pstate_power_info *pi;
542 : :
543 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
544 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
545 : 0 : return -1;
546 : : }
547 : :
548 : 0 : pi = &lcore_power_info[lcore_id];
549 [ # # ]: 0 : if (pi->curr_idx + 1 == pi->nb_freqs)
550 : : return 0;
551 : :
552 : : /* Frequencies in the array are from high to low. */
553 : 0 : return set_freq_internal(pi, pi->curr_idx + 1);
554 : : }
555 : :
556 : : int
557 : 0 : power_amd_pstate_cpufreq_freq_up(unsigned int lcore_id)
558 : : {
559 : : struct amd_pstate_power_info *pi;
560 : :
561 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
562 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
563 : 0 : return -1;
564 : : }
565 : :
566 : 0 : pi = &lcore_power_info[lcore_id];
567 [ # # # # ]: 0 : if (pi->curr_idx == 0 || (pi->curr_idx == pi->nom_idx &&
568 [ # # # # ]: 0 : pi->turbo_available && !pi->turbo_enable))
569 : : return 0;
570 : :
571 : : /* Frequencies in the array are from high to low. */
572 : 0 : return set_freq_internal(pi, pi->curr_idx - 1);
573 : : }
574 : :
575 : : int
576 : 0 : power_amd_pstate_cpufreq_freq_max(unsigned int lcore_id)
577 : : {
578 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
579 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
580 : 0 : return -1;
581 : : }
582 : :
583 : : /* Frequencies in the array are from high to low. */
584 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_available) {
585 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_enable)
586 : : /* Set to Turbo */
587 : 0 : return set_freq_internal(
588 : : &lcore_power_info[lcore_id], 0);
589 : : else
590 : : /* Set to max non-turbo */
591 : 0 : return set_freq_internal(
592 : : &lcore_power_info[lcore_id],
593 : : lcore_power_info[lcore_id].nom_idx);
594 : : } else
595 : 0 : return set_freq_internal(&lcore_power_info[lcore_id], 0);
596 : : }
597 : :
598 : : int
599 : 0 : power_amd_pstate_cpufreq_freq_min(unsigned int lcore_id)
600 : : {
601 : : struct amd_pstate_power_info *pi;
602 : :
603 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
604 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
605 : 0 : return -1;
606 : : }
607 : :
608 : 0 : pi = &lcore_power_info[lcore_id];
609 : :
610 : : /* Frequencies in the array are from high to low. */
611 : 0 : return set_freq_internal(pi, pi->nb_freqs - 1);
612 : : }
613 : :
614 : : int
615 : 0 : power_amd_pstate_turbo_status(unsigned int lcore_id)
616 : : {
617 : : struct amd_pstate_power_info *pi;
618 : :
619 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
620 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
621 : 0 : return -1;
622 : : }
623 : :
624 : : pi = &lcore_power_info[lcore_id];
625 : :
626 : 0 : return pi->turbo_enable;
627 : : }
628 : :
629 : : int
630 : 0 : power_amd_pstate_enable_turbo(unsigned int lcore_id)
631 : : {
632 : : struct amd_pstate_power_info *pi;
633 : :
634 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
635 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
636 : 0 : return -1;
637 : : }
638 : :
639 : : pi = &lcore_power_info[lcore_id];
640 : :
641 [ # # ]: 0 : if (pi->turbo_available)
642 : 0 : pi->turbo_enable = 1;
643 : : else {
644 : 0 : pi->turbo_enable = 0;
645 : 0 : POWER_LOG(ERR,
646 : : "Failed to enable turbo on lcore %u",
647 : : lcore_id);
648 : 0 : return -1;
649 : : }
650 : :
651 : : /* TODO: must set to max once enabling Turbo? Considering add condition:
652 : : * if ((pi->turbo_available) && (pi->curr_idx <= 1))
653 : : */
654 : : /* Max may have changed, so call to max function */
655 [ # # ]: 0 : if (power_amd_pstate_cpufreq_freq_max(lcore_id) < 0) {
656 : 0 : POWER_LOG(ERR,
657 : : "Failed to set frequency of lcore %u to max",
658 : : lcore_id);
659 : 0 : return -1;
660 : : }
661 : :
662 : : return 0;
663 : : }
664 : :
665 : : int
666 : 0 : power_amd_pstate_disable_turbo(unsigned int lcore_id)
667 : : {
668 : : struct amd_pstate_power_info *pi;
669 : :
670 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
671 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
672 : 0 : return -1;
673 : : }
674 : :
675 : : pi = &lcore_power_info[lcore_id];
676 : :
677 : 0 : pi->turbo_enable = 0;
678 : :
679 [ # # # # ]: 0 : if ((pi->turbo_available) && (pi->curr_idx <= pi->nom_idx)) {
680 : : /* Try to set freq to max by default coming out of turbo */
681 [ # # ]: 0 : if (power_amd_pstate_cpufreq_freq_max(lcore_id) < 0) {
682 : 0 : POWER_LOG(ERR,
683 : : "Failed to set frequency of lcore %u to max",
684 : : lcore_id);
685 : 0 : return -1;
686 : : }
687 : : }
688 : :
689 : : return 0;
690 : : }
691 : :
692 : : int
693 : 0 : power_amd_pstate_get_capabilities(unsigned int lcore_id,
694 : : struct rte_power_core_capabilities *caps)
695 : : {
696 : : struct amd_pstate_power_info *pi;
697 : :
698 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
699 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
700 : 0 : return -1;
701 : : }
702 [ # # ]: 0 : if (caps == NULL) {
703 : 0 : POWER_LOG(ERR, "Invalid argument");
704 : 0 : return -1;
705 : : }
706 : :
707 : : pi = &lcore_power_info[lcore_id];
708 : 0 : caps->capabilities = 0;
709 : 0 : caps->turbo = !!(pi->turbo_available);
710 : :
711 : 0 : return 0;
712 : : }
713 : :
714 : : static struct rte_power_cpufreq_ops amd_pstate_ops = {
715 : : .name = "amd-pstate",
716 : : .init = power_amd_pstate_cpufreq_init,
717 : : .exit = power_amd_pstate_cpufreq_exit,
718 : : .check_env_support = power_amd_pstate_cpufreq_check_supported,
719 : : .get_avail_freqs = power_amd_pstate_cpufreq_freqs,
720 : : .get_freq = power_amd_pstate_cpufreq_get_freq,
721 : : .set_freq = power_amd_pstate_cpufreq_set_freq,
722 : : .freq_down = power_amd_pstate_cpufreq_freq_down,
723 : : .freq_up = power_amd_pstate_cpufreq_freq_up,
724 : : .freq_max = power_amd_pstate_cpufreq_freq_max,
725 : : .freq_min = power_amd_pstate_cpufreq_freq_min,
726 : : .turbo_status = power_amd_pstate_turbo_status,
727 : : .enable_turbo = power_amd_pstate_enable_turbo,
728 : : .disable_turbo = power_amd_pstate_disable_turbo,
729 : : .get_caps = power_amd_pstate_get_capabilities
730 : : };
731 : :
732 : 251 : RTE_POWER_REGISTER_CPUFREQ_OPS(amd_pstate_ops);
|