perf-profile.c (6246B)
1 /* suid tool for setting acpi performance profile 2 * GPL-3 3 * https://www.kernel.org/doc/html/latest/userspace-api/sysfs-platform_profile.html 4 * https://mjmwired.net/kernel/Documentation/ABI/testing/sysfs-platform_profile 5 */ 6 #include <stdio.h> 7 #include <string.h> 8 #include <stdlib.h> 9 #include <unistd.h> 10 #include <sys/sysinfo.h> 11 #include <linux/limits.h> 12 13 #include "arg.h" 14 #include "common.h" 15 16 static const char *ACPI_PLPR_PATH = "/sys/firmware/acpi/platform_profile"; 17 static const char *S76_POW_PROF = "/run/system76-power.profile"; 18 19 static const char *DIRTY_WRITEBACK = "/proc/sys/vm/dirty_writeback_centisecs"; 20 static const char *DIRTY_EXPIRE = "/proc/sys/vm/dirty_expire_centisecs"; 21 22 static const char *SYS_CPU_PREFIX = "/sys/devices/system/cpu/cpu"; 23 24 static const char *PSTATE_DYNBOOST = "/sys/devices/system/cpu/intel_pstate/hwp_dynamic_boost"; 25 static const char *PSTATE_MAX_PERF = "/sys/devices/system/cpu/intel_pstate/max_perf_pct"; 26 static const char *PSTATE_MIN_PERF = "/sys/devices/system/cpu/intel_pstate/min_perf_pct"; 27 static const char *PSTATE_NO_TURBO = "/sys/devices/system/cpu/intel_pstate/no_turbo"; 28 29 char *argv0; 30 31 enum Profile { 32 LOWPOWER, 33 BALANCED, 34 PERFORMANCE, 35 }; 36 37 enum MType { 38 MIN, 39 MAX, 40 }; 41 42 static void usage(void) 43 { 44 die("usage: %s [-v] low-power|balanced|performance", argv0); 45 } 46 47 static void set_max_lost_work(int secs) 48 { 49 int centisecs = secs * 100; 50 51 if (write_oneshot_int(DIRTY_EXPIRE, centisecs)) 52 die("Could not open %s for writing:", DIRTY_EXPIRE); 53 54 if (write_oneshot_int(DIRTY_WRITEBACK, centisecs)) 55 die("Could not open %s for writing:", DIRTY_WRITEBACK); 56 } 57 58 static int get_frequency(enum MType typ, int cpu) 59 { 60 const char *rem; 61 char path[PATH_MAX], buf[10]; 62 int ret; 63 FILE *fd; 64 65 switch(typ) { 66 case MIN: 67 rem = "/cpufreq/cpuinfo_min_freq"; 68 break; 69 case MAX: 70 rem = "/cpufreq/cpuinfo_max_freq"; 71 break; 72 } 73 74 snprintf(path, PATH_MAX, "%s%d%s", SYS_CPU_PREFIX, cpu, rem); 75 76 if ((fd = fopen(path, "r")) == NULL) 77 die("Could not open cpu%d%s file for reading:", cpu, rem); 78 79 ret = atoi(fgets(buf, 10, fd)); 80 fclose(fd); 81 82 return ret; 83 } 84 85 static void set_frequency(enum MType typ, int cpu, int freq) 86 { 87 const char *rem; 88 char path[PATH_MAX]; 89 90 switch(typ) { 91 case MIN: 92 rem = "/cpufreq/scaling_min_freq"; 93 break; 94 case MAX: 95 rem = "/cpufreq/scaling_max_freq"; 96 break; 97 } 98 99 snprintf(path, PATH_MAX, "%s%d%s", SYS_CPU_PREFIX, cpu, rem); 100 101 if (write_oneshot_int(path, freq)) 102 die("Could not open cpu%d%s file for writing:", cpu, rem); 103 } 104 105 static void set_governor(int cpu, const char *governor) 106 { 107 const char *rem = "/cpufreq/scaling_governor"; 108 char path[PATH_MAX]; 109 110 snprintf(path, PATH_MAX, "%s%d%s", SYS_CPU_PREFIX, cpu, rem); 111 112 if (write_oneshot_str(path, governor)) 113 die("Could not write to cpu%d%s file:", cpu, rem); 114 } 115 116 static void cpufreq_set(enum Profile profile, int max_percent) 117 { 118 const char *governor; 119 int i, nproc, min, max; 120 121 /* We assume we have intel_pstate */ 122 switch(profile) { 123 case LOWPOWER: 124 case BALANCED: 125 governor = "powersave"; 126 break; 127 case PERFORMANCE: 128 governor = "performance"; 129 break; 130 } 131 132 /* We look at cpu0 but assume they're all the same */ 133 min = get_frequency(0, 0); 134 max = get_frequency(1, 0); 135 136 max = max * MIN(max_percent, 100) / 100; 137 138 nproc = get_nprocs(); 139 for (i = 0; i < nproc; i++) { 140 set_frequency(0, i, min); 141 set_frequency(1, i, max); 142 set_governor(i, governor); 143 } 144 } 145 146 static void set_lowpower(int acpi_platform_supported) 147 { 148 if (acpi_platform_supported) { 149 if (write_oneshot_str(ACPI_PLPR_PATH, "low-power")) 150 die("Could not open %s for writing:", ACPI_PLPR_PATH); 151 return; 152 } 153 154 set_max_lost_work(15); 155 cpufreq_set(LOWPOWER, 50); 156 157 /* intel_pstate values */ 158 if (write_oneshot_int(PSTATE_MIN_PERF, 0)) 159 die("Could not open %s for writing:", PSTATE_MIN_PERF); 160 161 if (write_oneshot_int(PSTATE_MAX_PERF, 50)) 162 die("Could not open %s for writing:", PSTATE_MAX_PERF); 163 164 if (write_oneshot_int(PSTATE_NO_TURBO, 1)) 165 die("Could not open %s for writing:", PSTATE_NO_TURBO); 166 } 167 168 static void set_balanced(int acpi_platform_supported) 169 { 170 if (acpi_platform_supported) { 171 if (write_oneshot_str(ACPI_PLPR_PATH, "balanced")) 172 die("Could not open %s for writing:", ACPI_PLPR_PATH); 173 return; 174 } 175 176 set_max_lost_work(15); 177 cpufreq_set(BALANCED, 100); 178 179 /* intel_pstate values */ 180 if (write_oneshot_int(PSTATE_DYNBOOST, 1)) 181 die("Could not open %s for writing:", PSTATE_DYNBOOST); 182 183 if (write_oneshot_int(PSTATE_MIN_PERF, 0)) 184 die("Could not open %s for writing:", PSTATE_MIN_PERF); 185 186 if (write_oneshot_int(PSTATE_MAX_PERF, 100)) 187 die("Could not open %s for writing:", PSTATE_MAX_PERF); 188 189 if (write_oneshot_int(PSTATE_NO_TURBO, 0)) 190 die("Could not open %s for writing:", PSTATE_NO_TURBO); 191 } 192 193 static void set_performance(int acpi_platform_supported) 194 { 195 if (acpi_platform_supported) { 196 if (write_oneshot_str(ACPI_PLPR_PATH, "performance")) 197 die("Could not open %s for writing:", ACPI_PLPR_PATH); 198 return; 199 } 200 201 set_max_lost_work(15); 202 cpufreq_set(PERFORMANCE, 100); 203 204 /* intel_pstate values */ 205 if (write_oneshot_int(PSTATE_DYNBOOST, 1)) 206 die("Could not open %s for writing:", PSTATE_DYNBOOST); 207 208 if (write_oneshot_int(PSTATE_MIN_PERF, 0)) 209 die("Could not open %s for writing:", PSTATE_MIN_PERF); 210 211 if (write_oneshot_int(PSTATE_MAX_PERF, 100)) 212 die("Could not open %s for writing:", PSTATE_MAX_PERF); 213 214 if (write_oneshot_int(PSTATE_NO_TURBO, 0)) 215 die("Could not open %s for writing:", PSTATE_NO_TURBO); 216 217 /* TODO: PCI runtime pm off */ 218 } 219 220 int main(int argc, char *argv[]) 221 { 222 int vflag = 0; 223 int acpi_platform_supported = 0; 224 225 ARGBEGIN { 226 case 'v': 227 vflag = 1; 228 break; 229 default: 230 usage(); 231 } ARGEND; 232 233 if (vflag) { 234 char buf[12]; 235 FILE *fd; 236 237 if ((fd = fopen(S76_POW_PROF, "r")) == NULL) 238 die("Could not open %s for reading:", S76_POW_PROF); 239 240 printf("Current profile: %s\n", fgets(buf, 12, fd)); 241 fclose(fd); 242 243 return 0; 244 } 245 246 if (argc != 1) 247 usage(); 248 249 if (!access(ACPI_PLPR_PATH, F_OK)) 250 acpi_platform_supported = 1; 251 252 if (!strcmp(argv[0], "low-power")) 253 set_lowpower(acpi_platform_supported); 254 else if (!strcmp(argv[0], "balanced")) 255 set_balanced(acpi_platform_supported); 256 else if (!strcmp(argv[0], "performance")) 257 set_performance(acpi_platform_supported); 258 else 259 usage(); 260 261 return write_oneshot_str(S76_POW_PROF, argv[0]); 262 }