perf-profile.c - system76-tools - collection of utilities for system76 laptops
HTML git clone https://git.parazyd.org/system76-tools
DIR Log
DIR Files
DIR Refs
DIR README
DIR 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 }