perf-profile.c - system76-tools - collection of utilities for system76 laptops
 (HTM) 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 }