1 /* SPDX-License-Identifier: BSD-3-Clause 2 * Copyright(c) 2020 Intel Corporation 3 */ 4 5 #include <limits.h> 6 #include <stdlib.h> 7 #include <stdio.h> 8 #include <string.h> 9 10 #include <rte_log.h> 11 #include <rte_string_fns.h> 12 13 #include "power_common.h" 14 15 #define POWER_SYSFILE_SCALING_DRIVER \ 16 "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_driver" 17 #define POWER_SYSFILE_GOVERNOR \ 18 "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_governor" 19 #define POWER_CONVERT_TO_DECIMAL 10 20 21 int 22 cpufreq_check_scaling_driver(const char *driver_name) 23 { 24 unsigned int lcore_id = 0; /* always check core 0 */ 25 char readbuf[PATH_MAX]; 26 size_t end_idx; 27 char *s; 28 FILE *f; 29 30 /* 31 * Check if scaling driver matches what we expect. 32 */ 33 open_core_sysfs_file(&f, "r", POWER_SYSFILE_SCALING_DRIVER, 34 lcore_id); 35 /* if there's no driver at all, bail out */ 36 if (f == NULL) 37 return 0; 38 39 s = fgets(readbuf, sizeof(readbuf), f); 40 /* don't need it any more */ 41 fclose(f); 42 43 /* if we can't read it, consider unsupported */ 44 if (s == NULL) 45 return 0; 46 47 /* when read from sysfs, driver name has an extra newline at the end */ 48 end_idx = strnlen(readbuf, sizeof(readbuf)); 49 if (end_idx > 0 && readbuf[end_idx - 1] == '\n') { 50 end_idx--; 51 readbuf[end_idx] = '\0'; 52 } 53 54 /* does the driver name match? */ 55 if (strncmp(readbuf, driver_name, sizeof(readbuf)) != 0) 56 return 0; 57 58 /* 59 * We might have a situation where the driver is supported, but we don't 60 * have permissions to do frequency scaling. This error should not be 61 * handled here, so consider the system to support scaling for now. 62 */ 63 return 1; 64 } 65 66 int 67 open_core_sysfs_file(FILE **f, const char *mode, const char *format, ...) 68 { 69 char fullpath[PATH_MAX]; 70 va_list ap; 71 FILE *tmpf; 72 73 va_start(ap, format); 74 vsnprintf(fullpath, sizeof(fullpath), format, ap); 75 va_end(ap); 76 tmpf = fopen(fullpath, mode); 77 *f = tmpf; 78 if (tmpf == NULL) 79 return -1; 80 81 return 0; 82 } 83 84 int 85 read_core_sysfs_u32(FILE *f, uint32_t *val) 86 { 87 char buf[BUFSIZ]; 88 uint32_t fval; 89 char *s; 90 91 s = fgets(buf, sizeof(buf), f); 92 if (s == NULL) 93 return -1; 94 95 /* fgets puts null terminator in, but do this just in case */ 96 buf[BUFSIZ - 1] = '\0'; 97 98 /* strip off any terminating newlines */ 99 *strchrnul(buf, '\n') = '\0'; 100 101 fval = strtoul(buf, NULL, POWER_CONVERT_TO_DECIMAL); 102 103 /* write the value */ 104 *val = fval; 105 106 return 0; 107 } 108 109 int 110 read_core_sysfs_s(FILE *f, char *buf, unsigned int len) 111 { 112 char *s; 113 114 s = fgets(buf, len, f); 115 if (s == NULL) 116 return -1; 117 118 /* fgets puts null terminator in, but do this just in case */ 119 buf[len - 1] = '\0'; 120 121 /* strip off any terminating newlines */ 122 *strchrnul(buf, '\n') = '\0'; 123 124 return 0; 125 } 126 127 int 128 write_core_sysfs_s(FILE *f, const char *str) 129 { 130 int ret; 131 132 ret = fseek(f, 0, SEEK_SET); 133 if (ret != 0) 134 return -1; 135 136 ret = fputs(str, f); 137 if (ret < 0) 138 return -1; 139 140 /* flush the output */ 141 ret = fflush(f); 142 if (ret != 0) 143 return -1; 144 145 return 0; 146 } 147 148 /** 149 * It is to check the current scaling governor by reading sys file, and then 150 * set it into 'performance' if it is not by writing the sys file. The original 151 * governor will be saved for rolling back. 152 */ 153 int 154 power_set_governor(unsigned int lcore_id, const char *new_governor, 155 char *orig_governor, size_t orig_governor_len) 156 { 157 FILE *f_governor = NULL; 158 int ret = -1; 159 char buf[BUFSIZ]; 160 161 open_core_sysfs_file(&f_governor, "rw+", POWER_SYSFILE_GOVERNOR, 162 lcore_id); 163 if (f_governor == NULL) { 164 RTE_LOG(ERR, POWER, "failed to open %s\n", 165 POWER_SYSFILE_GOVERNOR); 166 goto out; 167 } 168 169 ret = read_core_sysfs_s(f_governor, buf, sizeof(buf)); 170 if (ret < 0) { 171 RTE_LOG(ERR, POWER, "Failed to read %s\n", 172 POWER_SYSFILE_GOVERNOR); 173 goto out; 174 } 175 176 /* Save the original governor, if it was provided */ 177 if (orig_governor) 178 rte_strscpy(orig_governor, buf, orig_governor_len); 179 180 /* Check if current governor is already what we want */ 181 if (strcmp(buf, new_governor) == 0) { 182 ret = 0; 183 POWER_DEBUG_TRACE("Power management governor of lcore %u is " 184 "already %s\n", lcore_id, new_governor); 185 goto out; 186 } 187 188 /* Write the new governor */ 189 ret = write_core_sysfs_s(f_governor, new_governor); 190 if (ret < 0) { 191 RTE_LOG(ERR, POWER, "Failed to write %s\n", 192 POWER_SYSFILE_GOVERNOR); 193 goto out; 194 } 195 196 ret = 0; 197 RTE_LOG(INFO, POWER, "Power management governor of lcore %u has been " 198 "set to '%s' successfully\n", lcore_id, new_governor); 199 out: 200 if (f_governor != NULL) 201 fclose(f_governor); 202 203 return ret; 204 } 205