diff --git a/mbpfan.conf b/mbpfan.conf index 39d4b57..daa7d4c 100644 --- a/mbpfan.conf +++ b/mbpfan.conf @@ -16,3 +16,4 @@ low_temp = 63 # try ranges 55-63, default is 63 high_temp = 66 # try ranges 58-66, default is 66 max_temp = 86 # take highest number returned by "cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_max", divide by 1000 polling_interval = 1 # default is 1 seconds +intel_pstate_control = 0 # whether mbpfan should throttle down max turbo speed when exceeding max temp diff --git a/src/daemon.c b/src/daemon.c index 3f933e8..0ef2710 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -34,6 +34,7 @@ #include "global.h" #include "daemon.h" #include "util.h" +#include "intel_pstate.h" int daemonize = 1; int verbose = 0; @@ -108,6 +109,9 @@ static void cleanup_and_exit(int exit_code) sensors = next_sensor; } + intel_pstate_exit(intel_pstate); + free(intel_pstate); + exit(exit_code); } diff --git a/src/intel_pstate.c b/src/intel_pstate.c new file mode 100644 index 0000000..6903c0e --- /dev/null +++ b/src/intel_pstate.c @@ -0,0 +1,117 @@ +/** + * intel_pstate.c - automatically control turbo frequency for MacBook Pro + * Copyright (C) 2021 Gokturk Yuksek + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "intel_pstate.h" +#include "util.h" +#include + +int intel_pstate_init(t_intel_pstate *intel_pstate) +{ + const char *path_max_perf_pct = INTEL_PT_PATH "/max_perf_pct"; + const char *path_min_perf_pct = INTEL_PT_PATH "/min_perf_pct"; + FILE *fp; + int ret; + + fp = fopen(path_max_perf_pct, "w+"); + if (!fp) + return -1; + /* Save the initial value of max_perf_pct, to restore it on exit */ + ret = fscanf(fp, "%d", &intel_pstate->preserved_max_perf_pct); + if (ret != 1) + return -1; + /* Start with the maximum performance percentage at 100% */ + ret = fprintf(fp, "100"); + if (ret != 3) /* "100" -> 3 characters */ + return -1; + intel_pstate->f_max_perf_pct = fp; + + fp = fopen(path_min_perf_pct, "w+"); + if (!fp) + return -1; + /* Save the initial value of min_perf_pct, to restore it on exit */ + ret = fscanf(fp, "%d", &intel_pstate->preserved_min_perf_pct); + if (ret != 1) + return -1; + /* Set the minimum performance percentage to 0 */ + ret = fprintf(fp, "0"); + if (ret != 1) /* "0" -> 1 character */ + return -1; + intel_pstate->f_min_perf_pct = fp; + + return 0; +} + +int intel_pstate_is_available(void) +{ + DIR* dir = opendir(INTEL_PT_PATH); + + if ((!dir) && ENOENT == errno) + return 0; + + if (dir) + closedir(dir); + return 1; +} + +int intel_pstate_adjust(t_intel_pstate *intel_pstate, int step) +{ + int cur_val, new_val; + int ret; + + if (!intel_pstate) + return 1; + + rewind(intel_pstate->f_max_perf_pct); + ret = fscanf(intel_pstate->f_max_perf_pct, "%d", &cur_val); + if (ret != 1) + return -1; + + new_val = cur_val + step; + if (new_val < 0) + new_val = 0; + if (new_val > 100) + new_val = 100; + + if (new_val == cur_val) + return 0; + + mbp_log(LOG_INFO, "Adjusting intel_pstate: cur_val: %d, step: %d", cur_val, step); + + ret = fprintf(intel_pstate->f_max_perf_pct, "%d", new_val); + return (ret > 0) ? 0 : -1; +} + +void intel_pstate_exit(t_intel_pstate *intel_pstate) +{ + if (!intel_pstate) + return; + + (void)fprintf(intel_pstate->f_max_perf_pct, "%d", intel_pstate->preserved_max_perf_pct); + (void)fprintf(intel_pstate->f_min_perf_pct, "%d", intel_pstate->preserved_min_perf_pct); + + fclose(intel_pstate->f_max_perf_pct); + fclose(intel_pstate->f_min_perf_pct); + + memset(intel_pstate, 0, sizeof(*intel_pstate)); +} diff --git a/src/intel_pstate.h b/src/intel_pstate.h new file mode 100644 index 0000000..61c2278 --- /dev/null +++ b/src/intel_pstate.h @@ -0,0 +1,38 @@ +/** + * intel_pstate.c - automatically control turbo frequency for MacBook Pro + * Copyright (C) 2021 Gokturk Yuksek + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef _INTEL_PT_H_ +#define _INTEL_PT_H_ + +#define INTEL_PT_PATH "/sys/devices/system/cpu/intel_pstate/" + +struct s_intel_pstate { + FILE *f_max_perf_pct; + FILE *f_min_perf_pct; + int preserved_max_perf_pct; + int preserved_min_perf_pct; +}; + +typedef struct s_intel_pstate t_intel_pstate; + +extern t_intel_pstate *intel_pstate; + +extern int intel_pstate_init(t_intel_pstate *intel_pstate); +extern int intel_pstate_is_available(void); +extern int intel_pstate_adjust(t_intel_pstate *intel_pstate, int step); +extern void intel_pstate_exit(t_intel_pstate *intel_pstate); + +#endif /*_INTEL_PT_H_*/ diff --git a/src/mbpfan.c b/src/mbpfan.c index 1916d64..99a9ee0 100644 --- a/src/mbpfan.c +++ b/src/mbpfan.c @@ -45,6 +45,7 @@ #include "global.h" #include "settings.h" #include "util.h" +#include "intel_pstate.h" /* lazy min/max... */ #define min(a,b) ((a) < (b) ? (a) : (b)) @@ -61,6 +62,9 @@ int low_temp = 63; // try ranges 55-63 int high_temp = 66; // try ranges 58-66 int max_temp = 86; // do not set it > 90 +/* Whether mbpfan should control the turbo frequency or not */ +int intel_pstate_control = 0; // disabled by default + // maximum number of processors etc supported #define NUM_PROCESSORS 6 #define NUM_HWMONS 12 @@ -74,6 +78,7 @@ int polling_interval = 1; t_sensors* sensors = NULL; t_fans* fans = NULL; +t_intel_pstate* intel_pstate = NULL; char *smprintf(const char *fmt, ...) { @@ -524,6 +529,12 @@ void retrieve_settings(const char* settings_path, t_fans* fans) polling_interval = result; } + result = settings_get_int(settings, "general", "intel_pstate_control"); + + if (result != 0) { + intel_pstate_control = (result == 0) ? 0 : 1; + } + /* Destroy the settings object */ settings_delete(settings); } @@ -573,12 +584,27 @@ void mbpfan() { int old_temp, new_temp, fan_speed, steps; int temp_change; + int err; sensors = retrieve_sensors(); fans = retrieve_fans(); retrieve_settings(NULL, fans); + if (intel_pstate_control) { + if (!intel_pstate_is_available()) { + mbp_log(LOG_ERR, "Intel pstate control is requested but not available"); + exit(EXIT_FAILURE); + } else { + intel_pstate = (t_intel_pstate*)malloc(sizeof(t_intel_pstate)); + err = intel_pstate_init(intel_pstate); + if (err) { + mbp_log(LOG_ERR, "Failed to initialize intel_pstate control"); + exit(EXIT_FAILURE); + } + } + } + t_fans* fan = fans; while(fan != NULL) { @@ -620,6 +646,13 @@ void mbpfan() old_temp = new_temp; new_temp = get_temp(sensors); + if (new_temp <= high_temp) /* maximize turbo below high_temp */ + intel_pstate_adjust(intel_pstate, +100); + else if (new_temp >= max_temp) /* throttle down to keep the temp in control */ + intel_pstate_adjust(intel_pstate, -4); + else if ((new_temp - old_temp) <= -3) /* core is cooling down, increase turbo */ + intel_pstate_adjust(intel_pstate, +1); + fan = fans; while(fan != NULL) {