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