Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright(c) 2022 Intel Corporation
3 : : */
4 : :
5 : : #include <errno.h>
6 : : #include <dirent.h>
7 : : #include <fnmatch.h>
8 : :
9 : : #include <rte_memcpy.h>
10 : :
11 : : #include "power_intel_uncore.h"
12 : : #include "power_common.h"
13 : :
14 : : #define MAX_UNCORE_FREQS 32
15 : : #define MAX_NUMA_DIE 8
16 : : #define BUS_FREQ 100000
17 : : #define FILTER_LENGTH 18
18 : : #define PACKAGE_FILTER "package_%02u_die_*"
19 : : #define DIE_FILTER "package_%02u_die_%02u"
20 : : #define INTEL_UNCORE_FREQUENCY_DIR "/sys/devices/system/cpu/intel_uncore_frequency"
21 : : #define POWER_GOVERNOR_PERF "performance"
22 : : #define POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ \
23 : : "/sys/devices/system/cpu/intel_uncore_frequency/package_%02u_die_%02u/max_freq_khz"
24 : : #define POWER_INTEL_UNCORE_SYSFILE_MIN_FREQ \
25 : : "/sys/devices/system/cpu/intel_uncore_frequency/package_%02u_die_%02u/min_freq_khz"
26 : : #define POWER_INTEL_UNCORE_SYSFILE_BASE_MAX_FREQ \
27 : : "/sys/devices/system/cpu/intel_uncore_frequency/package_%02u_die_%02u/initial_max_freq_khz"
28 : : #define POWER_INTEL_UNCORE_SYSFILE_BASE_MIN_FREQ \
29 : : "/sys/devices/system/cpu/intel_uncore_frequency/package_%02u_die_%02u/initial_min_freq_khz"
30 : :
31 : :
32 : : struct uncore_power_info {
33 : : unsigned int die; /* Core die id */
34 : : unsigned int pkg; /* Package id */
35 : : uint32_t freqs[MAX_UNCORE_FREQS]; /* Frequency array */
36 : : uint32_t nb_freqs; /* Number of available freqs */
37 : : FILE *f_cur_min; /* FD of scaling_min */
38 : : FILE *f_cur_max; /* FD of scaling_max */
39 : : uint32_t curr_idx; /* Freq index in freqs array */
40 : : uint32_t org_min_freq; /* Original min freq of uncore */
41 : : uint32_t org_max_freq; /* Original max freq of uncore */
42 : : uint32_t init_max_freq; /* System max uncore freq */
43 : : uint32_t init_min_freq; /* System min uncore freq */
44 : : } __rte_cache_aligned;
45 : :
46 : : static struct uncore_power_info uncore_info[RTE_MAX_NUMA_NODES][MAX_NUMA_DIE];
47 : :
48 : : static int
49 : 0 : set_uncore_freq_internal(struct uncore_power_info *ui, uint32_t idx)
50 : : {
51 : : uint32_t target_uncore_freq, curr_max_freq;
52 : : int ret;
53 : :
54 [ # # # # ]: 0 : if (idx >= MAX_UNCORE_FREQS || idx >= ui->nb_freqs) {
55 : 0 : POWER_LOG(DEBUG, "Invalid uncore frequency index %u, which "
56 : : "should be less than %u", idx, ui->nb_freqs);
57 : 0 : return -1;
58 : : }
59 : :
60 : 0 : target_uncore_freq = ui->freqs[idx];
61 : :
62 : : /* check current max freq, so that the value to be flushed first
63 : : * can be accurately recorded
64 : : */
65 : 0 : open_core_sysfs_file(&ui->f_cur_max, "rw+", POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ,
66 : : ui->pkg, ui->die);
67 [ # # ]: 0 : if (ui->f_cur_max == NULL) {
68 : 0 : POWER_LOG(DEBUG, "failed to open %s",
69 : : POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ);
70 : 0 : return -1;
71 : : }
72 : 0 : ret = read_core_sysfs_u32(ui->f_cur_max, &curr_max_freq);
73 [ # # ]: 0 : if (ret < 0) {
74 : 0 : POWER_LOG(DEBUG, "Failed to read %s",
75 : : POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ);
76 : 0 : fclose(ui->f_cur_max);
77 : 0 : return -1;
78 : : }
79 : :
80 : : /* check this value first before fprintf value to f_cur_max, so value isn't overwritten */
81 [ # # ]: 0 : if (fprintf(ui->f_cur_min, "%u", target_uncore_freq) < 0) {
82 : 0 : POWER_LOG(ERR, "Fail to write new uncore frequency for "
83 : : "pkg %02u die %02u", ui->pkg, ui->die);
84 : 0 : return -1;
85 : : }
86 : :
87 [ # # ]: 0 : if (fprintf(ui->f_cur_max, "%u", target_uncore_freq) < 0) {
88 : 0 : POWER_LOG(ERR, "Fail to write new uncore frequency for "
89 : : "pkg %02u die %02u", ui->pkg, ui->die);
90 : 0 : return -1;
91 : : }
92 : :
93 : : POWER_DEBUG_LOG("Uncore frequency '%u' to be set for pkg %02u die %02u",
94 : : target_uncore_freq, ui->pkg, ui->die);
95 : :
96 : : /* write the minimum value first if the target freq is less than current max */
97 [ # # ]: 0 : if (target_uncore_freq <= curr_max_freq) {
98 : 0 : fflush(ui->f_cur_min);
99 : 0 : fflush(ui->f_cur_max);
100 : : } else {
101 : 0 : fflush(ui->f_cur_max);
102 : 0 : fflush(ui->f_cur_min);
103 : : }
104 : 0 : ui->curr_idx = idx;
105 : :
106 : 0 : return 0;
107 : : }
108 : :
109 : : /*
110 : : * Fopen the sys file for the future setting of the uncore die frequency.
111 : : */
112 : : static int
113 : 0 : power_init_for_setting_uncore_freq(struct uncore_power_info *ui)
114 : : {
115 : 0 : FILE *f_base_min = NULL, *f_base_max = NULL, *f_min = NULL, *f_max = NULL;
116 : 0 : uint32_t base_min_freq = 0, base_max_freq = 0, min_freq = 0, max_freq = 0;
117 : : int ret;
118 : :
119 : : /* open and read all uncore sys files */
120 : : /* Base max */
121 : 0 : open_core_sysfs_file(&f_base_max, "r", POWER_INTEL_UNCORE_SYSFILE_BASE_MAX_FREQ,
122 : : ui->pkg, ui->die);
123 [ # # ]: 0 : if (f_base_max == NULL) {
124 : 0 : POWER_LOG(DEBUG, "failed to open %s",
125 : : POWER_INTEL_UNCORE_SYSFILE_BASE_MAX_FREQ);
126 : 0 : goto err;
127 : : }
128 : 0 : ret = read_core_sysfs_u32(f_base_max, &base_max_freq);
129 [ # # ]: 0 : if (ret < 0) {
130 : 0 : POWER_LOG(DEBUG, "Failed to read %s",
131 : : POWER_INTEL_UNCORE_SYSFILE_BASE_MAX_FREQ);
132 : 0 : goto err;
133 : : }
134 : :
135 : : /* Base min */
136 : 0 : open_core_sysfs_file(&f_base_min, "r", POWER_INTEL_UNCORE_SYSFILE_BASE_MIN_FREQ,
137 : : ui->pkg, ui->die);
138 [ # # ]: 0 : if (f_base_min == NULL) {
139 : 0 : POWER_LOG(DEBUG, "failed to open %s",
140 : : POWER_INTEL_UNCORE_SYSFILE_BASE_MIN_FREQ);
141 : 0 : goto err;
142 : : }
143 : : if (f_base_min != NULL) {
144 : 0 : ret = read_core_sysfs_u32(f_base_min, &base_min_freq);
145 [ # # ]: 0 : if (ret < 0) {
146 : 0 : POWER_LOG(DEBUG, "Failed to read %s",
147 : : POWER_INTEL_UNCORE_SYSFILE_BASE_MIN_FREQ);
148 : 0 : goto err;
149 : : }
150 : : }
151 : :
152 : : /* Curr min */
153 : 0 : open_core_sysfs_file(&f_min, "rw+", POWER_INTEL_UNCORE_SYSFILE_MIN_FREQ,
154 : : ui->pkg, ui->die);
155 [ # # ]: 0 : if (f_min == NULL) {
156 : 0 : POWER_LOG(DEBUG, "failed to open %s",
157 : : POWER_INTEL_UNCORE_SYSFILE_MIN_FREQ);
158 : 0 : goto err;
159 : : }
160 : : if (f_min != NULL) {
161 : 0 : ret = read_core_sysfs_u32(f_min, &min_freq);
162 [ # # ]: 0 : if (ret < 0) {
163 : 0 : POWER_LOG(DEBUG, "Failed to read %s",
164 : : POWER_INTEL_UNCORE_SYSFILE_MIN_FREQ);
165 : 0 : goto err;
166 : : }
167 : : }
168 : :
169 : : /* Curr max */
170 : 0 : open_core_sysfs_file(&f_max, "rw+", POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ,
171 : : ui->pkg, ui->die);
172 [ # # ]: 0 : if (f_max == NULL) {
173 : 0 : POWER_LOG(DEBUG, "failed to open %s",
174 : : POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ);
175 : 0 : goto err;
176 : : }
177 : : if (f_max != NULL) {
178 : 0 : ret = read_core_sysfs_u32(f_max, &max_freq);
179 [ # # ]: 0 : if (ret < 0) {
180 : 0 : POWER_LOG(DEBUG, "Failed to read %s",
181 : : POWER_INTEL_UNCORE_SYSFILE_MAX_FREQ);
182 : 0 : goto err;
183 : : }
184 : : }
185 : :
186 : : /* assign file handles */
187 : 0 : ui->f_cur_min = f_min;
188 : 0 : ui->f_cur_max = f_max;
189 : : /* save current min + max freq's so that they can be restored on exit */
190 : 0 : ui->org_min_freq = min_freq;
191 : 0 : ui->org_max_freq = max_freq;
192 : 0 : ui->init_max_freq = base_max_freq;
193 : 0 : ui->init_min_freq = base_min_freq;
194 : :
195 : 0 : fclose(f_base_min);
196 : 0 : fclose(f_base_max);
197 : : /* f_min and f_max are stored, no need to close */
198 : :
199 : 0 : return 0;
200 : :
201 : 0 : err:
202 [ # # ]: 0 : if (f_base_min != NULL)
203 : 0 : fclose(f_base_min);
204 [ # # ]: 0 : if (f_base_max != NULL)
205 : 0 : fclose(f_base_max);
206 [ # # ]: 0 : if (f_min != NULL)
207 : 0 : fclose(f_min);
208 [ # # ]: 0 : if (f_max != NULL)
209 : 0 : fclose(f_max);
210 : : return -1;
211 : : }
212 : :
213 : : /*
214 : : * Get the available uncore frequencies of the specific die by reading the
215 : : * sys file.
216 : : */
217 : : static int
218 : 0 : power_get_available_uncore_freqs(struct uncore_power_info *ui)
219 : : {
220 : : int ret = -1;
221 : : uint32_t i, num_uncore_freqs = 0;
222 : :
223 : 0 : num_uncore_freqs = (ui->init_max_freq - ui->init_min_freq) / BUS_FREQ + 1;
224 [ # # ]: 0 : if (num_uncore_freqs >= MAX_UNCORE_FREQS) {
225 : 0 : POWER_LOG(ERR, "Too many available uncore frequencies: %d",
226 : : num_uncore_freqs);
227 : 0 : goto out;
228 : : }
229 : :
230 : : /* Generate the uncore freq bucket array. */
231 [ # # ]: 0 : for (i = 0; i < num_uncore_freqs; i++)
232 : 0 : ui->freqs[i] = ui->init_max_freq - (i) * BUS_FREQ;
233 : :
234 : 0 : ui->nb_freqs = num_uncore_freqs;
235 : :
236 : : ret = 0;
237 : :
238 : : POWER_DEBUG_LOG("%d frequency(s) of pkg %02u die %02u are available",
239 : : num_uncore_freqs, ui->pkg, ui->die);
240 : :
241 : 0 : out:
242 : 0 : return ret;
243 : : }
244 : :
245 : : static int
246 : 0 : check_pkg_die_values(unsigned int pkg, unsigned int die)
247 : : {
248 : : unsigned int max_pkgs, max_dies;
249 : 0 : max_pkgs = power_intel_uncore_get_num_pkgs();
250 [ # # ]: 0 : if (max_pkgs == 0)
251 : : return -1;
252 [ # # ]: 0 : if (pkg >= max_pkgs) {
253 : 0 : POWER_LOG(DEBUG, "Package number %02u can not exceed %u",
254 : : pkg, max_pkgs);
255 : 0 : return -1;
256 : : }
257 : :
258 : 0 : max_dies = power_intel_uncore_get_num_dies(pkg);
259 [ # # ]: 0 : if (max_dies == 0)
260 : : return -1;
261 [ # # ]: 0 : if (die >= max_dies) {
262 : 0 : POWER_LOG(DEBUG, "Die number %02u can not exceed %u",
263 : : die, max_dies);
264 : 0 : return -1;
265 : : }
266 : :
267 : : return 0;
268 : : }
269 : :
270 : : int
271 : 0 : power_intel_uncore_init(unsigned int pkg, unsigned int die)
272 : : {
273 : : struct uncore_power_info *ui;
274 : :
275 : 0 : int ret = check_pkg_die_values(pkg, die);
276 [ # # ]: 0 : if (ret < 0)
277 : : return -1;
278 : :
279 : 0 : ui = &uncore_info[pkg][die];
280 : 0 : ui->die = die;
281 : 0 : ui->pkg = pkg;
282 : :
283 : : /* Init for setting uncore die frequency */
284 [ # # ]: 0 : if (power_init_for_setting_uncore_freq(ui) < 0) {
285 : 0 : POWER_LOG(DEBUG, "Cannot init for setting uncore frequency for "
286 : : "pkg %02u die %02u", pkg, die);
287 : 0 : return -1;
288 : : }
289 : :
290 : : /* Get the available frequencies */
291 [ # # ]: 0 : if (power_get_available_uncore_freqs(ui) < 0) {
292 : 0 : POWER_LOG(DEBUG, "Cannot get available uncore frequencies of "
293 : : "pkg %02u die %02u", pkg, die);
294 : 0 : return -1;
295 : : }
296 : :
297 : : return 0;
298 : : }
299 : :
300 : : int
301 : 0 : power_intel_uncore_exit(unsigned int pkg, unsigned int die)
302 : : {
303 : : struct uncore_power_info *ui;
304 : :
305 : 0 : int ret = check_pkg_die_values(pkg, die);
306 [ # # ]: 0 : if (ret < 0)
307 : : return -1;
308 : :
309 : : ui = &uncore_info[pkg][die];
310 : :
311 [ # # ]: 0 : if (fprintf(ui->f_cur_min, "%u", ui->org_min_freq) < 0) {
312 : 0 : POWER_LOG(ERR, "Fail to write original uncore frequency for "
313 : : "pkg %02u die %02u", ui->pkg, ui->die);
314 : 0 : return -1;
315 : : }
316 : :
317 [ # # ]: 0 : if (fprintf(ui->f_cur_max, "%u", ui->org_max_freq) < 0) {
318 : 0 : POWER_LOG(ERR, "Fail to write original uncore frequency for "
319 : : "pkg %02u die %02u", ui->pkg, ui->die);
320 : 0 : return -1;
321 : : }
322 : :
323 : 0 : fflush(ui->f_cur_min);
324 : 0 : fflush(ui->f_cur_max);
325 : :
326 : : /* Close FD of setting freq */
327 : 0 : fclose(ui->f_cur_min);
328 : 0 : fclose(ui->f_cur_max);
329 : 0 : ui->f_cur_min = NULL;
330 : 0 : ui->f_cur_max = NULL;
331 : :
332 : 0 : return 0;
333 : : }
334 : :
335 : : uint32_t
336 : 0 : power_get_intel_uncore_freq(unsigned int pkg, unsigned int die)
337 : : {
338 : 0 : int ret = check_pkg_die_values(pkg, die);
339 [ # # ]: 0 : if (ret < 0)
340 : : return -1;
341 : :
342 : 0 : return uncore_info[pkg][die].curr_idx;
343 : : }
344 : :
345 : : int
346 : 0 : power_set_intel_uncore_freq(unsigned int pkg, unsigned int die, uint32_t index)
347 : : {
348 : 0 : int ret = check_pkg_die_values(pkg, die);
349 [ # # ]: 0 : if (ret < 0)
350 : : return -1;
351 : :
352 : 0 : return set_uncore_freq_internal(&(uncore_info[pkg][die]), index);
353 : : }
354 : :
355 : : int
356 : 0 : power_intel_uncore_freq_max(unsigned int pkg, unsigned int die)
357 : : {
358 : 0 : int ret = check_pkg_die_values(pkg, die);
359 [ # # ]: 0 : if (ret < 0)
360 : : return -1;
361 : :
362 : 0 : return set_uncore_freq_internal(&(uncore_info[pkg][die]), 0);
363 : : }
364 : :
365 : :
366 : : int
367 : 0 : power_intel_uncore_freq_min(unsigned int pkg, unsigned int die)
368 : : {
369 : 0 : int ret = check_pkg_die_values(pkg, die);
370 [ # # ]: 0 : if (ret < 0)
371 : : return -1;
372 : :
373 : : struct uncore_power_info *ui = &uncore_info[pkg][die];
374 : :
375 : 0 : return set_uncore_freq_internal(&(uncore_info[pkg][die]), ui->nb_freqs - 1);
376 : : }
377 : :
378 : : int
379 : 0 : power_intel_uncore_freqs(unsigned int pkg, unsigned int die, uint32_t *freqs, uint32_t num)
380 : : {
381 : : struct uncore_power_info *ui;
382 : :
383 : 0 : int ret = check_pkg_die_values(pkg, die);
384 [ # # ]: 0 : if (ret < 0)
385 : : return -1;
386 : :
387 [ # # ]: 0 : if (freqs == NULL) {
388 : 0 : POWER_LOG(ERR, "NULL buffer supplied");
389 : 0 : return 0;
390 : : }
391 : :
392 : : ui = &uncore_info[pkg][die];
393 [ # # ]: 0 : if (num < ui->nb_freqs) {
394 : 0 : POWER_LOG(ERR, "Buffer size is not enough");
395 : 0 : return 0;
396 : : }
397 [ # # ]: 0 : rte_memcpy(freqs, ui->freqs, ui->nb_freqs * sizeof(uint32_t));
398 : :
399 : 0 : return ui->nb_freqs;
400 : : }
401 : :
402 : : int
403 : 0 : power_intel_uncore_get_num_freqs(unsigned int pkg, unsigned int die)
404 : : {
405 : 0 : int ret = check_pkg_die_values(pkg, die);
406 [ # # ]: 0 : if (ret < 0)
407 : : return -1;
408 : :
409 : 0 : return uncore_info[pkg][die].nb_freqs;
410 : : }
411 : :
412 : : unsigned int
413 : 1 : power_intel_uncore_get_num_pkgs(void)
414 : : {
415 : : DIR *d;
416 : : struct dirent *dir;
417 : : unsigned int count = 0;
418 : : char filter[FILTER_LENGTH];
419 : :
420 : 1 : d = opendir(INTEL_UNCORE_FREQUENCY_DIR);
421 [ + - ]: 1 : if (d == NULL) {
422 : 1 : POWER_LOG(ERR,
423 : : "Uncore frequency management not supported/enabled on this kernel. "
424 : : "Please enable CONFIG_INTEL_UNCORE_FREQ_CONTROL if on Intel x86 with linux kernel"
425 : : " >= 5.6");
426 : 1 : return 0;
427 : : }
428 : :
429 : : /* search by incrementing file name for max pkg file value */
430 [ # # ]: 0 : while ((dir = readdir(d)) != NULL) {
431 : : snprintf(filter, FILTER_LENGTH, PACKAGE_FILTER, count);
432 : : /* make sure filter string is in file name (don't include hidden files) */
433 [ # # ]: 0 : if (fnmatch(filter, dir->d_name, 0) == 0)
434 : 0 : count++;
435 : : }
436 : :
437 : 0 : closedir(d);
438 : :
439 : 0 : return count;
440 : : }
441 : :
442 : : unsigned int
443 : 0 : power_intel_uncore_get_num_dies(unsigned int pkg)
444 : : {
445 : : DIR *d;
446 : : struct dirent *dir;
447 : : unsigned int count = 0, max_pkgs;
448 : : char filter[FILTER_LENGTH];
449 : :
450 : 0 : max_pkgs = power_intel_uncore_get_num_pkgs();
451 [ # # ]: 0 : if (max_pkgs == 0)
452 : : return 0;
453 [ # # ]: 0 : if (pkg >= max_pkgs) {
454 : 0 : POWER_LOG(DEBUG, "Invalid package number");
455 : 0 : return 0;
456 : : }
457 : :
458 : 0 : d = opendir(INTEL_UNCORE_FREQUENCY_DIR);
459 [ # # ]: 0 : if (d == NULL) {
460 : 0 : POWER_LOG(ERR,
461 : : "Uncore frequency management not supported/enabled on this kernel. "
462 : : "Please enable CONFIG_INTEL_UNCORE_FREQ_CONTROL if on Intel x86 with linux kernel"
463 : : " >= 5.6");
464 : 0 : return 0;
465 : : }
466 : :
467 : : /* search by incrementing file name for max die file value */
468 [ # # ]: 0 : while ((dir = readdir(d)) != NULL) {
469 : : snprintf(filter, FILTER_LENGTH, DIE_FILTER, pkg, count);
470 : : /* make sure filter string is in file name (don't include hidden files) */
471 [ # # ]: 0 : if (fnmatch(filter, dir->d_name, 0) == 0)
472 : 0 : count++;
473 : : }
474 : :
475 : 0 : closedir(d);
476 : :
477 : 0 : return count;
478 : : }
|