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