Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2018 Intel Corporation
3 : : */
4 : :
5 : : #include <stdio.h>
6 : : #include <stdlib.h>
7 : : #include <fcntl.h>
8 : : #include <string.h>
9 : : #include <unistd.h>
10 : : #include <limits.h>
11 : : #include <errno.h>
12 : : #include <inttypes.h>
13 : :
14 : : #include <rte_memcpy.h>
15 : : #include <rte_stdatomic.h>
16 : :
17 : : #include "rte_power_pmd_mgmt.h"
18 : : #include "power_pstate_cpufreq.h"
19 : : #include "power_common.h"
20 : :
21 : : /* macros used for rounding frequency to nearest 100000 */
22 : : #define FREQ_ROUNDING_DELTA 50000
23 : : #define ROUND_FREQ_TO_N_100000 100000
24 : :
25 : : #define BUS_FREQ 100000
26 : :
27 : : #define POWER_GOVERNOR_PERF "performance"
28 : : #define POWER_SYSFILE_MAX_FREQ \
29 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_max_freq"
30 : : #define POWER_SYSFILE_MIN_FREQ \
31 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_min_freq"
32 : : #define POWER_SYSFILE_CUR_FREQ \
33 : : "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq"
34 : : #define POWER_SYSFILE_BASE_MAX_FREQ \
35 : : "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq"
36 : : #define POWER_SYSFILE_BASE_MIN_FREQ \
37 : : "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_min_freq"
38 : : #define POWER_SYSFILE_BASE_FREQ \
39 : : "/sys/devices/system/cpu/cpu%u/cpufreq/base_frequency"
40 : : #define POWER_SYSFILE_TURBO_PCT \
41 : : "/sys/devices/system/cpu/intel_pstate/turbo_pct"
42 : : #define POWER_PSTATE_DRIVER "intel_pstate"
43 : :
44 : :
45 : : enum power_state {
46 : : POWER_IDLE = 0,
47 : : POWER_ONGOING,
48 : : POWER_USED,
49 : : POWER_UNKNOWN
50 : : };
51 : :
52 : : struct __rte_cache_aligned pstate_power_info {
53 : : unsigned int lcore_id; /**< Logical core id */
54 : : uint32_t freqs[RTE_MAX_LCORE_FREQS]; /**< Frequency array */
55 : : uint32_t nb_freqs; /**< number of available freqs */
56 : : FILE *f_cur_min; /**< FD of scaling_min */
57 : : FILE *f_cur_max; /**< FD of scaling_max */
58 : : char governor_ori[32]; /**< Original governor name */
59 : : uint32_t curr_idx; /**< Freq index in freqs array */
60 : : uint32_t non_turbo_max_ratio; /**< Non Turbo Max ratio */
61 : : uint32_t sys_max_freq; /**< system wide max freq */
62 : : uint32_t core_base_freq; /**< core base freq */
63 : : RTE_ATOMIC(uint32_t) state; /**< Power in use state */
64 : : uint16_t turbo_available; /**< Turbo Boost available */
65 : : uint16_t turbo_enable; /**< Turbo Boost enable/disable */
66 : : uint16_t priority_core; /**< High Performance core */
67 : : };
68 : :
69 : :
70 : : static struct pstate_power_info lcore_power_info[RTE_MAX_LCORE];
71 : :
72 : : /**
73 : : * It is to read the turbo mode percentage from sysfs
74 : : */
75 : : static int32_t
76 : 0 : power_read_turbo_pct(uint64_t *outVal)
77 : : {
78 : : int fd, ret;
79 : 0 : char val[4] = {0};
80 : : char *endptr;
81 : :
82 : : fd = open(POWER_SYSFILE_TURBO_PCT, O_RDONLY);
83 : :
84 [ # # ]: 0 : if (fd < 0) {
85 : 0 : POWER_LOG(ERR, "Error opening '%s': %s", POWER_SYSFILE_TURBO_PCT,
86 : : strerror(errno));
87 : 0 : return fd;
88 : : }
89 : :
90 : 0 : ret = read(fd, val, sizeof(val));
91 : :
92 [ # # ]: 0 : if (ret < 0) {
93 : 0 : POWER_LOG(ERR, "Error reading '%s': %s", POWER_SYSFILE_TURBO_PCT,
94 : : strerror(errno));
95 : 0 : goto out;
96 : : }
97 : :
98 : 0 : errno = 0;
99 : 0 : *outVal = (uint64_t) strtol(val, &endptr, 10);
100 [ # # # # ]: 0 : if (errno != 0 || (*endptr != 0 && *endptr != '\n')) {
101 : 0 : POWER_LOG(ERR, "Error converting str to digits, read from %s: %s",
102 : : POWER_SYSFILE_TURBO_PCT, strerror(errno));
103 : : ret = -1;
104 : 0 : goto out;
105 : : }
106 : :
107 : : POWER_DEBUG_LOG("power turbo pct: %"PRIu64, *outVal);
108 : :
109 : 0 : out: close(fd);
110 : 0 : return ret;
111 : : }
112 : :
113 : : /**
114 : : * It is to fopen the sys file for the future setting the lcore frequency.
115 : : */
116 : : static int
117 : 0 : power_init_for_setting_freq(struct pstate_power_info *pi)
118 : : {
119 : 0 : FILE *f_base = NULL, *f_base_min = NULL, *f_base_max = NULL,
120 : 0 : *f_min = NULL, *f_max = NULL;
121 : : uint32_t base_ratio, base_min_ratio, base_max_ratio;
122 : : uint64_t max_non_turbo;
123 : : int ret;
124 : :
125 : : /* open all files we expect to have open */
126 : 0 : open_core_sysfs_file(&f_base_max, "r", POWER_SYSFILE_BASE_MAX_FREQ,
127 : : pi->lcore_id);
128 [ # # ]: 0 : if (f_base_max == NULL) {
129 : 0 : POWER_LOG(ERR, "failed to open %s",
130 : : POWER_SYSFILE_BASE_MAX_FREQ);
131 : 0 : goto err;
132 : : }
133 : :
134 : 0 : open_core_sysfs_file(&f_base_min, "r", POWER_SYSFILE_BASE_MIN_FREQ,
135 : : pi->lcore_id);
136 [ # # ]: 0 : if (f_base_min == NULL) {
137 : 0 : POWER_LOG(ERR, "failed to open %s",
138 : : POWER_SYSFILE_BASE_MIN_FREQ);
139 : 0 : goto err;
140 : : }
141 : :
142 : 0 : open_core_sysfs_file(&f_min, "rw+", POWER_SYSFILE_MIN_FREQ,
143 : : pi->lcore_id);
144 [ # # ]: 0 : if (f_min == NULL) {
145 : 0 : POWER_LOG(ERR, "failed to open %s",
146 : : POWER_SYSFILE_MIN_FREQ);
147 : 0 : goto err;
148 : : }
149 : :
150 : 0 : open_core_sysfs_file(&f_max, "rw+", POWER_SYSFILE_MAX_FREQ,
151 : : pi->lcore_id);
152 [ # # ]: 0 : if (f_max == NULL) {
153 : 0 : POWER_LOG(ERR, "failed to open %s",
154 : : POWER_SYSFILE_MAX_FREQ);
155 : 0 : goto err;
156 : : }
157 : :
158 : 0 : open_core_sysfs_file(&f_base, "r", POWER_SYSFILE_BASE_FREQ,
159 : : pi->lcore_id);
160 : : /* base ratio file may not exist in some kernels, so no error check */
161 : :
162 : : /* read base max ratio */
163 : 0 : ret = read_core_sysfs_u32(f_base_max, &base_max_ratio);
164 [ # # ]: 0 : if (ret < 0) {
165 : 0 : POWER_LOG(ERR, "Failed to read %s",
166 : : POWER_SYSFILE_BASE_MAX_FREQ);
167 : 0 : goto err;
168 : : }
169 : :
170 : : /* read base min ratio */
171 : 0 : ret = read_core_sysfs_u32(f_base_min, &base_min_ratio);
172 [ # # ]: 0 : if (ret < 0) {
173 : 0 : POWER_LOG(ERR, "Failed to read %s",
174 : : POWER_SYSFILE_BASE_MIN_FREQ);
175 : 0 : goto err;
176 : : }
177 : :
178 : : /* base ratio may not exist */
179 [ # # ]: 0 : if (f_base != NULL) {
180 : 0 : ret = read_core_sysfs_u32(f_base, &base_ratio);
181 [ # # ]: 0 : if (ret < 0) {
182 : 0 : POWER_LOG(ERR, "Failed to read %s",
183 : : POWER_SYSFILE_BASE_FREQ);
184 : 0 : goto err;
185 : : }
186 : : } else {
187 : 0 : base_ratio = 0;
188 : : }
189 : :
190 : : /* convert ratios to bins */
191 : 0 : base_max_ratio /= BUS_FREQ;
192 : 0 : base_min_ratio /= BUS_FREQ;
193 : 0 : base_ratio /= BUS_FREQ;
194 : :
195 : : /* assign file handles */
196 : 0 : pi->f_cur_min = f_min;
197 : 0 : pi->f_cur_max = f_max;
198 : :
199 : : /* try to get turbo from global sysfs entry for less privileges than from MSR */
200 [ # # ]: 0 : if (power_read_turbo_pct(&max_non_turbo) < 0)
201 : 0 : goto err;
202 : : /* no errors after this point */
203 : :
204 : 0 : max_non_turbo = base_min_ratio
205 : 0 : + (100 - max_non_turbo) * (base_max_ratio - base_min_ratio) / 100;
206 : :
207 : : POWER_DEBUG_LOG("no turbo perf %"PRIu64, max_non_turbo);
208 : :
209 : 0 : pi->non_turbo_max_ratio = (uint32_t)max_non_turbo;
210 : :
211 : : /*
212 : : * If base_frequency is reported as greater than the maximum
213 : : * turbo frequency, that's a known issue with some kernels.
214 : : * Set base_frequency to max_non_turbo as a workaround.
215 : : */
216 [ # # ]: 0 : if (base_ratio > base_max_ratio) {
217 : : /* base_ratio is greater than max turbo. Kernel bug. */
218 : 0 : pi->priority_core = 0;
219 : 0 : goto out;
220 : : }
221 : :
222 : : /*
223 : : * If base_frequency is reported as greater than the maximum
224 : : * non-turbo frequency, then mark it as a high priority core.
225 : : */
226 [ # # ]: 0 : if (base_ratio > max_non_turbo)
227 : 0 : pi->priority_core = 1;
228 : : else
229 : 0 : pi->priority_core = 0;
230 : 0 : pi->core_base_freq = base_ratio * BUS_FREQ;
231 : :
232 : 0 : out:
233 [ # # ]: 0 : if (f_base != NULL)
234 : 0 : fclose(f_base);
235 : 0 : fclose(f_base_max);
236 : 0 : fclose(f_base_min);
237 : : /* f_min and f_max are stored, no need to close */
238 : 0 : return 0;
239 : :
240 : 0 : err:
241 [ # # ]: 0 : if (f_base != NULL)
242 : 0 : fclose(f_base);
243 [ # # ]: 0 : if (f_base_min != NULL)
244 : 0 : fclose(f_base_min);
245 [ # # ]: 0 : if (f_base_max != NULL)
246 : 0 : fclose(f_base_max);
247 [ # # ]: 0 : if (f_min != NULL)
248 : 0 : fclose(f_min);
249 [ # # ]: 0 : if (f_max != NULL)
250 : 0 : fclose(f_max);
251 : : return -1;
252 : : }
253 : :
254 : : static int
255 : 0 : set_freq_internal(struct pstate_power_info *pi, uint32_t idx)
256 : : {
257 : : uint32_t target_freq = 0;
258 : :
259 [ # # # # ]: 0 : if (idx >= RTE_MAX_LCORE_FREQS || idx >= pi->nb_freqs) {
260 : 0 : POWER_LOG(ERR, "Invalid frequency index %u, which "
261 : : "should be less than %u", idx, pi->nb_freqs);
262 : 0 : return -1;
263 : : }
264 : :
265 : : /* Check if it is the same as current */
266 [ # # ]: 0 : if (idx == pi->curr_idx)
267 : : return 0;
268 : :
269 : : /* Because Intel Pstate Driver only allow user change min/max hint
270 : : * User need change the min/max as same value.
271 : : */
272 [ # # ]: 0 : if (fseek(pi->f_cur_min, 0, SEEK_SET) < 0) {
273 : 0 : POWER_LOG(ERR, "Fail to set file position indicator to 0 "
274 : : "for setting frequency for lcore %u",
275 : : pi->lcore_id);
276 : 0 : return -1;
277 : : }
278 : :
279 [ # # ]: 0 : if (fseek(pi->f_cur_max, 0, SEEK_SET) < 0) {
280 : 0 : POWER_LOG(ERR, "Fail to set file position indicator to 0 "
281 : : "for setting frequency for lcore %u",
282 : : pi->lcore_id);
283 : 0 : return -1;
284 : : }
285 : :
286 : : /* Turbo is available and enabled, first freq bucket is sys max freq */
287 [ # # # # ]: 0 : if (pi->turbo_available && idx == 0) {
288 [ # # ]: 0 : if (pi->turbo_enable)
289 : 0 : target_freq = pi->sys_max_freq;
290 : : else {
291 : 0 : POWER_LOG(ERR, "Turbo is off, frequency can't be scaled up more %u",
292 : : pi->lcore_id);
293 : 0 : return -1;
294 : : }
295 : : } else
296 : 0 : target_freq = pi->freqs[idx];
297 : :
298 : : /* Decrease freq, the min freq should be updated first */
299 [ # # ]: 0 : if (idx > pi->curr_idx) {
300 : :
301 [ # # ]: 0 : if (fprintf(pi->f_cur_min, "%u", target_freq) < 0) {
302 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
303 : : "lcore %u", pi->lcore_id);
304 : 0 : return -1;
305 : : }
306 : :
307 [ # # ]: 0 : if (fprintf(pi->f_cur_max, "%u", target_freq) < 0) {
308 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
309 : : "lcore %u", pi->lcore_id);
310 : 0 : return -1;
311 : : }
312 : :
313 : : POWER_DEBUG_LOG("Frequency '%u' to be set for lcore %u",
314 : : target_freq, pi->lcore_id);
315 : :
316 : 0 : fflush(pi->f_cur_min);
317 : 0 : fflush(pi->f_cur_max);
318 : :
319 : : }
320 : :
321 : : /* Increase freq, the max freq should be updated first */
322 [ # # ]: 0 : if (idx < pi->curr_idx) {
323 : :
324 [ # # ]: 0 : if (fprintf(pi->f_cur_max, "%u", target_freq) < 0) {
325 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
326 : : "lcore %u", pi->lcore_id);
327 : 0 : return -1;
328 : : }
329 : :
330 [ # # ]: 0 : if (fprintf(pi->f_cur_min, "%u", target_freq) < 0) {
331 : 0 : POWER_LOG(ERR, "Fail to write new frequency for "
332 : : "lcore %u", pi->lcore_id);
333 : 0 : return -1;
334 : : }
335 : :
336 : : POWER_DEBUG_LOG("Frequency '%u' to be set for lcore %u",
337 : : target_freq, pi->lcore_id);
338 : :
339 : 0 : fflush(pi->f_cur_max);
340 : 0 : fflush(pi->f_cur_min);
341 : : }
342 : :
343 : 0 : pi->curr_idx = idx;
344 : :
345 : 0 : return 1;
346 : : }
347 : :
348 : : /**
349 : : * It is to check the current scaling governor by reading sys file, and then
350 : : * set it into 'performance' if it is not by writing the sys file. The original
351 : : * governor will be saved for rolling back.
352 : : */
353 : : static int
354 : : power_set_governor_performance(struct pstate_power_info *pi)
355 : : {
356 : 1 : return power_set_governor(pi->lcore_id, POWER_GOVERNOR_PERF,
357 : 1 : pi->governor_ori, sizeof(pi->governor_ori));
358 : : }
359 : :
360 : : /**
361 : : * It is to check the governor and then set the original governor back if
362 : : * needed by writing the sys file.
363 : : */
364 : : static int
365 : : power_set_governor_original(struct pstate_power_info *pi)
366 : : {
367 : 0 : return power_set_governor(pi->lcore_id, pi->governor_ori, NULL, 0);
368 : : }
369 : :
370 : : /**
371 : : * It is to get the available frequencies of the specific lcore by reading the
372 : : * sys file.
373 : : */
374 : : static int
375 : 0 : power_get_available_freqs(struct pstate_power_info *pi)
376 : : {
377 : 0 : FILE *f_min = NULL, *f_max = NULL;
378 : : int ret = -1;
379 : 0 : uint32_t sys_min_freq = 0, sys_max_freq = 0, base_max_freq = 0;
380 : : int config_min_freq, config_max_freq;
381 : : uint32_t i, num_freqs = 0;
382 : :
383 : : /* open all files */
384 : 0 : open_core_sysfs_file(&f_max, "r", POWER_SYSFILE_BASE_MAX_FREQ,
385 : : pi->lcore_id);
386 [ # # ]: 0 : if (f_max == NULL) {
387 : 0 : POWER_LOG(ERR, "failed to open %s",
388 : : POWER_SYSFILE_BASE_MAX_FREQ);
389 : 0 : goto out;
390 : : }
391 : :
392 : 0 : open_core_sysfs_file(&f_min, "r", POWER_SYSFILE_BASE_MIN_FREQ,
393 : : pi->lcore_id);
394 [ # # ]: 0 : if (f_min == NULL) {
395 : 0 : POWER_LOG(ERR, "failed to open %s",
396 : : POWER_SYSFILE_BASE_MIN_FREQ);
397 : 0 : goto out;
398 : : }
399 : :
400 : : /* read base ratios */
401 : 0 : ret = read_core_sysfs_u32(f_max, &sys_max_freq);
402 [ # # ]: 0 : if (ret < 0) {
403 : 0 : POWER_LOG(ERR, "Failed to read %s",
404 : : POWER_SYSFILE_BASE_MAX_FREQ);
405 : 0 : goto out;
406 : : }
407 : :
408 : 0 : ret = read_core_sysfs_u32(f_min, &sys_min_freq);
409 [ # # ]: 0 : if (ret < 0) {
410 : 0 : POWER_LOG(ERR, "Failed to read %s",
411 : : POWER_SYSFILE_BASE_MIN_FREQ);
412 : 0 : goto out;
413 : : }
414 : :
415 : : /* check for config set by user or application to limit frequency range */
416 : 0 : config_min_freq = rte_power_pmd_mgmt_get_scaling_freq_min(pi->lcore_id);
417 [ # # ]: 0 : if (config_min_freq < 0)
418 : 0 : goto out;
419 : 0 : config_max_freq = rte_power_pmd_mgmt_get_scaling_freq_max(pi->lcore_id);
420 [ # # ]: 0 : if (config_max_freq < 0)
421 : 0 : goto out;
422 : :
423 : 0 : sys_min_freq = RTE_MAX(sys_min_freq, (uint32_t)config_min_freq);
424 [ # # ]: 0 : if (config_max_freq > 0) /* Only use config_max_freq if a value has been set */
425 : 0 : sys_max_freq = RTE_MIN(sys_max_freq, (uint32_t)config_max_freq);
426 : :
427 [ # # ]: 0 : if (sys_max_freq < sys_min_freq)
428 : 0 : goto out;
429 : :
430 : 0 : pi->sys_max_freq = sys_max_freq;
431 : :
432 [ # # ]: 0 : if (pi->priority_core == 1)
433 : 0 : base_max_freq = pi->core_base_freq;
434 : : else
435 : 0 : base_max_freq = pi->non_turbo_max_ratio * BUS_FREQ;
436 : :
437 : : POWER_DEBUG_LOG("sys min %u, sys max %u, base_max %u",
438 : : sys_min_freq,
439 : : sys_max_freq,
440 : : base_max_freq);
441 : :
442 [ # # ]: 0 : if (base_max_freq < sys_max_freq)
443 : 0 : pi->turbo_available = 1;
444 : : else
445 : 0 : pi->turbo_available = 0;
446 : :
447 : : /* If turbo is available then there is one extra freq bucket
448 : : * to store the sys max freq which value is base_max +1
449 : : */
450 : 0 : num_freqs = (RTE_MIN(base_max_freq, sys_max_freq) - sys_min_freq) / BUS_FREQ
451 : 0 : + 1 + pi->turbo_available;
452 [ # # ]: 0 : if (num_freqs >= RTE_MAX_LCORE_FREQS) {
453 : 0 : POWER_LOG(ERR, "Too many available frequencies: %d",
454 : : num_freqs);
455 : 0 : goto out;
456 : : }
457 : :
458 : : /* Generate the freq bucket array.
459 : : * If turbo is available the freq bucket[0] value is base_max +1
460 : : * the bucket[1] is base_max, bucket[2] is base_max - BUS_FREQ
461 : : * and so on.
462 : : * If turbo is not available bucket[0] is base_max and so on
463 : : */
464 [ # # ]: 0 : for (i = 0, pi->nb_freqs = 0; i < num_freqs; i++) {
465 [ # # # # ]: 0 : if ((i == 0) && pi->turbo_available)
466 : 0 : pi->freqs[pi->nb_freqs++] = RTE_MIN(base_max_freq, sys_max_freq) + 1;
467 : : else
468 : 0 : pi->freqs[pi->nb_freqs++] = RTE_MIN(base_max_freq, sys_max_freq) -
469 : 0 : (i - pi->turbo_available) * BUS_FREQ;
470 : : }
471 : :
472 : : ret = 0;
473 : :
474 : : POWER_DEBUG_LOG("%d frequency(s) of lcore %u are available",
475 : : num_freqs, pi->lcore_id);
476 : :
477 : 0 : out:
478 [ # # ]: 0 : if (f_min != NULL)
479 : 0 : fclose(f_min);
480 [ # # ]: 0 : if (f_max != NULL)
481 : 0 : fclose(f_max);
482 : :
483 : 0 : return ret;
484 : : }
485 : :
486 : : static int
487 : 0 : power_get_cur_idx(struct pstate_power_info *pi)
488 : : {
489 : : FILE *f_cur;
490 : : int ret = -1;
491 : 0 : uint32_t sys_cur_freq = 0;
492 : : unsigned int i;
493 : :
494 : 0 : open_core_sysfs_file(&f_cur, "r", POWER_SYSFILE_CUR_FREQ,
495 : : pi->lcore_id);
496 [ # # ]: 0 : if (f_cur == NULL) {
497 : 0 : POWER_LOG(ERR, "failed to open %s",
498 : : POWER_SYSFILE_CUR_FREQ);
499 : 0 : goto fail;
500 : : }
501 : :
502 : 0 : ret = read_core_sysfs_u32(f_cur, &sys_cur_freq);
503 [ # # ]: 0 : if (ret < 0) {
504 : 0 : POWER_LOG(ERR, "Failed to read %s",
505 : : POWER_SYSFILE_CUR_FREQ);
506 : 0 : goto fail;
507 : : }
508 : :
509 : : /* convert the frequency to nearest 100000 value
510 : : * Ex: if sys_cur_freq=1396789 then freq_conv=1400000
511 : : * Ex: if sys_cur_freq=800030 then freq_conv=800000
512 : : * Ex: if sys_cur_freq=800030 then freq_conv=800000
513 : : */
514 : : unsigned int freq_conv = 0;
515 : 0 : freq_conv = (sys_cur_freq + FREQ_ROUNDING_DELTA)
516 : : / ROUND_FREQ_TO_N_100000;
517 : 0 : freq_conv = freq_conv * ROUND_FREQ_TO_N_100000;
518 : :
519 [ # # ]: 0 : for (i = 0; i < pi->nb_freqs; i++) {
520 [ # # ]: 0 : if (freq_conv == pi->freqs[i]) {
521 : 0 : pi->curr_idx = i;
522 : 0 : break;
523 : : }
524 : : }
525 : :
526 : : ret = 0;
527 : 0 : fail:
528 [ # # ]: 0 : if (f_cur != NULL)
529 : 0 : fclose(f_cur);
530 : 0 : return ret;
531 : : }
532 : :
533 : : int
534 : 0 : power_pstate_cpufreq_check_supported(void)
535 : : {
536 : 0 : return cpufreq_check_scaling_driver(POWER_PSTATE_DRIVER);
537 : : }
538 : :
539 : : int
540 : 1 : power_pstate_cpufreq_init(unsigned int lcore_id)
541 : : {
542 : : struct pstate_power_info *pi;
543 : : uint32_t exp_state;
544 : :
545 [ - + ]: 1 : if (lcore_id >= RTE_MAX_LCORE) {
546 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceed %u",
547 : : lcore_id, RTE_MAX_LCORE - 1U);
548 : 0 : return -1;
549 : : }
550 : :
551 : 1 : pi = &lcore_power_info[lcore_id];
552 : : exp_state = POWER_IDLE;
553 : : /* The power in use state works as a guard variable between
554 : : * the CPU frequency control initialization and exit process.
555 : : * The ACQUIRE memory ordering here pairs with the RELEASE
556 : : * ordering below as lock to make sure the frequency operations
557 : : * in the critical section are done under the correct state.
558 : : */
559 [ - + ]: 1 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state,
560 : : POWER_ONGOING,
561 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
562 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
563 : : "in use", lcore_id);
564 : 0 : return -1;
565 : : }
566 : :
567 : 1 : pi->lcore_id = lcore_id;
568 : : /* Check and set the governor */
569 [ + - ]: 1 : if (power_set_governor_performance(pi) < 0) {
570 : 1 : POWER_LOG(ERR, "Cannot set governor of lcore %u to "
571 : : "performance", lcore_id);
572 : 1 : goto fail;
573 : : }
574 : : /* Init for setting lcore frequency */
575 [ # # ]: 0 : if (power_init_for_setting_freq(pi) < 0) {
576 : 0 : POWER_LOG(ERR, "Cannot init for setting frequency for "
577 : : "lcore %u", lcore_id);
578 : 0 : goto fail;
579 : : }
580 : :
581 : : /* Get the available frequencies */
582 [ # # ]: 0 : if (power_get_available_freqs(pi) < 0) {
583 : 0 : POWER_LOG(ERR, "Cannot get available frequencies of "
584 : : "lcore %u", lcore_id);
585 : 0 : goto fail;
586 : : }
587 : :
588 [ # # ]: 0 : if (power_get_cur_idx(pi) < 0) {
589 : 0 : POWER_LOG(ERR, "Cannot get current frequency "
590 : : "index of lcore %u", lcore_id);
591 : 0 : goto fail;
592 : : }
593 : :
594 : : /* Set freq to max by default */
595 [ # # ]: 0 : if (power_pstate_cpufreq_freq_max(lcore_id) < 0) {
596 : 0 : POWER_LOG(ERR, "Cannot set frequency of lcore %u "
597 : : "to max", lcore_id);
598 : 0 : goto fail;
599 : : }
600 : :
601 : 0 : POWER_LOG(INFO, "Initialized successfully for lcore %u "
602 : : "power management", lcore_id);
603 : : exp_state = POWER_ONGOING;
604 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_USED,
605 : : rte_memory_order_release, rte_memory_order_relaxed);
606 : :
607 : 0 : return 0;
608 : :
609 : 1 : fail:
610 : : exp_state = POWER_ONGOING;
611 : 1 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_UNKNOWN,
612 : : rte_memory_order_release, rte_memory_order_relaxed);
613 : :
614 : 1 : return -1;
615 : : }
616 : :
617 : : int
618 : 0 : power_pstate_cpufreq_exit(unsigned int lcore_id)
619 : : {
620 : : struct pstate_power_info *pi;
621 : : uint32_t exp_state;
622 : :
623 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
624 : 0 : POWER_LOG(ERR, "Lcore id %u can not exceeds %u",
625 : : lcore_id, RTE_MAX_LCORE - 1U);
626 : 0 : return -1;
627 : : }
628 : : pi = &lcore_power_info[lcore_id];
629 : :
630 : : exp_state = POWER_USED;
631 : : /* The power in use state works as a guard variable between
632 : : * the CPU frequency control initialization and exit process.
633 : : * The ACQUIRE memory ordering here pairs with the RELEASE
634 : : * ordering below as lock to make sure the frequency operations
635 : : * in the critical section are under done the correct state.
636 : : */
637 [ # # ]: 0 : if (!rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state,
638 : : POWER_ONGOING,
639 : : rte_memory_order_acquire, rte_memory_order_relaxed)) {
640 : 0 : POWER_LOG(INFO, "Power management of lcore %u is "
641 : : "not used", lcore_id);
642 : 0 : return -1;
643 : : }
644 : :
645 : : /* Close FD of setting freq */
646 : 0 : fclose(pi->f_cur_min);
647 : 0 : fclose(pi->f_cur_max);
648 : 0 : pi->f_cur_min = NULL;
649 : 0 : pi->f_cur_max = NULL;
650 : :
651 : : /* Set the governor back to the original */
652 [ # # ]: 0 : if (power_set_governor_original(pi) < 0) {
653 : 0 : POWER_LOG(ERR, "Cannot set the governor of %u back "
654 : : "to the original", lcore_id);
655 : 0 : goto fail;
656 : : }
657 : :
658 : 0 : POWER_LOG(INFO, "Power management of lcore %u has exited from "
659 : : "'performance' mode and been set back to the "
660 : : "original", lcore_id);
661 : : exp_state = POWER_ONGOING;
662 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_IDLE,
663 : : rte_memory_order_release, rte_memory_order_relaxed);
664 : :
665 : 0 : return 0;
666 : :
667 : : fail:
668 : : exp_state = POWER_ONGOING;
669 : 0 : rte_atomic_compare_exchange_strong_explicit(&(pi->state), &exp_state, POWER_UNKNOWN,
670 : : rte_memory_order_release, rte_memory_order_relaxed);
671 : :
672 : 0 : return -1;
673 : : }
674 : :
675 : :
676 : : uint32_t
677 : 0 : power_pstate_cpufreq_freqs(unsigned int lcore_id, uint32_t *freqs, uint32_t num)
678 : : {
679 : : struct pstate_power_info *pi;
680 : :
681 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
682 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
683 : 0 : return 0;
684 : : }
685 : :
686 [ # # ]: 0 : if (freqs == NULL) {
687 : 0 : POWER_LOG(ERR, "NULL buffer supplied");
688 : 0 : return 0;
689 : : }
690 : :
691 : : pi = &lcore_power_info[lcore_id];
692 [ # # ]: 0 : if (num < pi->nb_freqs) {
693 : 0 : POWER_LOG(ERR, "Buffer size is not enough");
694 : 0 : return 0;
695 : : }
696 [ # # ]: 0 : rte_memcpy(freqs, pi->freqs, pi->nb_freqs * sizeof(uint32_t));
697 : :
698 : 0 : return pi->nb_freqs;
699 : : }
700 : :
701 : : uint32_t
702 : 0 : power_pstate_cpufreq_get_freq(unsigned int lcore_id)
703 : : {
704 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
705 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
706 : 0 : return RTE_POWER_INVALID_FREQ_INDEX;
707 : : }
708 : :
709 : 0 : return lcore_power_info[lcore_id].curr_idx;
710 : : }
711 : :
712 : :
713 : : int
714 : 0 : power_pstate_cpufreq_set_freq(unsigned int lcore_id, uint32_t index)
715 : : {
716 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
717 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
718 : 0 : return -1;
719 : : }
720 : :
721 : 0 : return set_freq_internal(&(lcore_power_info[lcore_id]), index);
722 : : }
723 : :
724 : : int
725 : 0 : power_pstate_cpufreq_freq_up(unsigned int lcore_id)
726 : : {
727 : : struct pstate_power_info *pi;
728 : :
729 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
730 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
731 : 0 : return -1;
732 : : }
733 : :
734 : 0 : pi = &lcore_power_info[lcore_id];
735 [ # # # # ]: 0 : if (pi->curr_idx == 0 ||
736 [ # # # # ]: 0 : (pi->curr_idx == 1 && pi->turbo_available && !pi->turbo_enable))
737 : : return 0;
738 : :
739 : : /* Frequencies in the array are from high to low. */
740 : 0 : return set_freq_internal(pi, pi->curr_idx - 1);
741 : : }
742 : :
743 : : int
744 : 0 : power_pstate_cpufreq_freq_down(unsigned int lcore_id)
745 : : {
746 : : struct pstate_power_info *pi;
747 : :
748 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
749 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
750 : 0 : return -1;
751 : : }
752 : :
753 : 0 : pi = &lcore_power_info[lcore_id];
754 [ # # ]: 0 : if (pi->curr_idx + 1 == pi->nb_freqs)
755 : : return 0;
756 : :
757 : : /* Frequencies in the array are from high to low. */
758 : 0 : return set_freq_internal(pi, pi->curr_idx + 1);
759 : : }
760 : :
761 : : int
762 : 0 : power_pstate_cpufreq_freq_max(unsigned int lcore_id)
763 : : {
764 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
765 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
766 : 0 : return -1;
767 : : }
768 : :
769 : : /* Frequencies in the array are from high to low. */
770 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_available) {
771 [ # # ]: 0 : if (lcore_power_info[lcore_id].turbo_enable)
772 : : /* Set to Turbo */
773 : 0 : return set_freq_internal(
774 : : &lcore_power_info[lcore_id], 0);
775 : : else
776 : : /* Set to max non-turbo */
777 : 0 : return set_freq_internal(
778 : : &lcore_power_info[lcore_id], 1);
779 : : } else
780 : 0 : return set_freq_internal(&lcore_power_info[lcore_id], 0);
781 : : }
782 : :
783 : :
784 : : int
785 : 0 : power_pstate_cpufreq_freq_min(unsigned int lcore_id)
786 : : {
787 : : struct pstate_power_info *pi;
788 : :
789 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
790 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
791 : 0 : return -1;
792 : : }
793 : :
794 : 0 : pi = &lcore_power_info[lcore_id];
795 : :
796 : : /* Frequencies in the array are from high to low. */
797 : 0 : return set_freq_internal(pi, pi->nb_freqs - 1);
798 : : }
799 : :
800 : :
801 : : int
802 : 0 : power_pstate_turbo_status(unsigned int lcore_id)
803 : : {
804 : : struct pstate_power_info *pi;
805 : :
806 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
807 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
808 : 0 : return -1;
809 : : }
810 : :
811 : : pi = &lcore_power_info[lcore_id];
812 : :
813 : 0 : return pi->turbo_enable;
814 : : }
815 : :
816 : : int
817 : 0 : power_pstate_enable_turbo(unsigned int lcore_id)
818 : : {
819 : : struct pstate_power_info *pi;
820 : :
821 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
822 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
823 : 0 : return -1;
824 : : }
825 : :
826 : : pi = &lcore_power_info[lcore_id];
827 : :
828 [ # # ]: 0 : if (pi->turbo_available)
829 : 0 : pi->turbo_enable = 1;
830 : : else {
831 : 0 : pi->turbo_enable = 0;
832 : 0 : POWER_LOG(ERR,
833 : : "Failed to enable turbo on lcore %u",
834 : : lcore_id);
835 : 0 : return -1;
836 : : }
837 : :
838 : 0 : return 0;
839 : : }
840 : :
841 : :
842 : : int
843 : 0 : power_pstate_disable_turbo(unsigned int lcore_id)
844 : : {
845 : : struct pstate_power_info *pi;
846 : :
847 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
848 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
849 : 0 : return -1;
850 : : }
851 : :
852 : : pi = &lcore_power_info[lcore_id];
853 : :
854 : 0 : pi->turbo_enable = 0;
855 : :
856 [ # # # # ]: 0 : if (pi->turbo_available && pi->curr_idx <= 1) {
857 : : /* Try to set freq to max by default coming out of turbo */
858 [ # # ]: 0 : if (power_pstate_cpufreq_freq_max(lcore_id) < 0) {
859 : 0 : POWER_LOG(ERR,
860 : : "Failed to set frequency of lcore %u to max",
861 : : lcore_id);
862 : 0 : return -1;
863 : : }
864 : : }
865 : :
866 : : return 0;
867 : : }
868 : :
869 : :
870 : 0 : int power_pstate_get_capabilities(unsigned int lcore_id,
871 : : struct rte_power_core_capabilities *caps)
872 : : {
873 : : struct pstate_power_info *pi;
874 : :
875 [ # # ]: 0 : if (lcore_id >= RTE_MAX_LCORE) {
876 : 0 : POWER_LOG(ERR, "Invalid lcore ID");
877 : 0 : return -1;
878 : : }
879 [ # # ]: 0 : if (caps == NULL) {
880 : 0 : POWER_LOG(ERR, "Invalid argument");
881 : 0 : return -1;
882 : : }
883 : :
884 : : pi = &lcore_power_info[lcore_id];
885 : 0 : caps->capabilities = 0;
886 : 0 : caps->turbo = !!(pi->turbo_available);
887 : 0 : caps->priority = pi->priority_core;
888 : :
889 : 0 : return 0;
890 : : }
|