system76-tools

collection of utilities for system76 laptops
git clone https://git.parazyd.org/system76-tools
Log | Files | Refs | README | LICENSE

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 }