-
Notifications
You must be signed in to change notification settings - Fork 139
Add support for Intel turbo frequency throttling through intel_pstate #227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 1 commit
4bf6bd5
5454d83
059a902
d4c2fb2
4065354
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
/** | ||
* intel_pstate.c - automatically control turbo frequency for MacBook Pro | ||
* Copyright (C) 2021 Gokturk Yuksek <[email protected]> | ||
* | ||
* 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 <stdio.h> | ||
#include <stdlib.h> | ||
#include <string.h> | ||
#include <unistd.h> | ||
#include <sys/types.h> | ||
#include <dirent.h> | ||
#include <errno.h> | ||
|
||
#include "intel_pstate.h" | ||
#include "util.h" | ||
#include <syslog.h> | ||
|
||
static int read_int(FILE *fp, int *val) | ||
{ | ||
char buf[4]; /* Maximum we can read in this module is '100\0' */ | ||
ssize_t ret; | ||
|
||
ret = pread(fileno(fp), buf, sizeof(buf), 0); | ||
if (ret >= 0) { | ||
buf[ret] = '\0'; | ||
*val = atoi(buf); | ||
return 0; | ||
} else { | ||
return (int)ret; | ||
} | ||
} | ||
|
||
static inline int write_str(FILE *fp, char *str, int len) | ||
{ | ||
ssize_t ret; | ||
ret = (int)pwrite(fileno(fp), str, len, 0); | ||
if (ret == len) | ||
return 0; | ||
else | ||
return -1; | ||
} | ||
|
||
static int write_int(FILE *fp, int val) | ||
{ | ||
char buf[4]; /* Maximum we can read in this module is '100\0' */ | ||
int ret; | ||
|
||
ret = snprintf(buf, sizeof(buf), "%d", val); | ||
if (ret < 0) | ||
return ret; | ||
|
||
return write_str(fp, buf, ret); | ||
} | ||
|
||
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 err; | ||
|
||
fp = fopen(path_max_perf_pct, "w+"); | ||
if (!fp) | ||
return -1; | ||
/* Save the initial value of max_perf_pct, to restore it on exit */ | ||
err = read_int(fp, &intel_pstate->preserved_max_perf_pct); | ||
if (err) | ||
return err; | ||
/* Start with the maximum performance percentage at 100% */ | ||
err = write_str(fp, "100", 4); | ||
if (err) | ||
return err; | ||
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 */ | ||
err = read_int(fp, &intel_pstate->preserved_min_perf_pct); | ||
if (err) | ||
return err; | ||
/* Set the minimum performance percentage to 0 */ | ||
err = write_str(fp, "0", 2); | ||
if (err) | ||
return err; | ||
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; | ||
|
||
closedir(dir); | ||
gktrk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
return 1; | ||
} | ||
|
||
int intel_pstate_adjust(t_intel_pstate *intel_pstate, int step) | ||
{ | ||
int val; | ||
int err; | ||
|
||
if (!intel_pstate) | ||
return 1; | ||
|
||
err = read_int(intel_pstate->f_max_perf_pct, &val); | ||
if (err) | ||
return err; | ||
|
||
mbp_log(LOG_INFO, "Adjusting intel_pstate: val: %d, step: %d", val, step); | ||
|
||
val += step; | ||
if (val < 0) | ||
val = 0; | ||
if (val > 100) | ||
val = 100; | ||
|
||
return write_int(intel_pstate->f_max_perf_pct, val); | ||
} | ||
|
||
void intel_pstate_exit(t_intel_pstate *intel_pstate) | ||
{ | ||
if (!intel_pstate) | ||
return; | ||
|
||
(void)write_int(intel_pstate->f_max_perf_pct, intel_pstate->preserved_max_perf_pct); | ||
gktrk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
(void)write_int(intel_pstate->f_min_perf_pct, 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)); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* intel_pstate.c - automatically control turbo frequency for MacBook Pro | ||
* Copyright (C) 2021 Gokturk Yuksek <[email protected]> | ||
* | ||
* 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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does |
||
|
||
#endif /*_INTEL_PT_H_*/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't this always 1? So you can just set There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would there be a case where it's not in the config file? Anyone upgrading from a previous release may not immediately update their config file. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes we must handle the missing value case as well. But it appears that |
||
} | ||
|
||
/* Destroy the settings object */ | ||
settings_delete(settings); | ||
} | ||
|
@@ -573,12 +584,31 @@ 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)); | ||
if (!intel_pstate) { | ||
gktrk marked this conversation as resolved.
Show resolved
Hide resolved
|
||
mbp_log(LOG_ERR, "Failed to allocate memory for intel_pstate control"); | ||
exit(EXIT_FAILURE); | ||
} | ||
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 +650,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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested with
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing you use the default value of Let's take your log for example. The turbo % goes from 40 to 36 at the beginning and the temperature starts falling. At this point, it might be falling because we capped the maximum turbo frequency, or it could be because the load has decreased and turbo isn't kicking in like it used to. There is also the thermal lag, which is apparent in the graph I posted above. Notice how in the middle sections of the graph, every time turbo pct is reduced, it's reflected in the temperature only a few seconds after. After seeing a temperature drop of 3C (from 76C to 73C), the pstate controller assumes that it's safe to increase the turbo pct by 1% up to 37%. Clearly, if I increase turbo for 1% for every 3C drop in the temperature, I'll never get back to 100%. At low temperatures. you do want the turbo to be at 100%. The fans will be enough to handle short, bursty turbo kick-ins that improve performance. So below Why decrease by 4% at a time? If you look at the beginning of the graph, you'll see that in a manner of seconds the temperature jumps from ~42C to ~77C. With a polling frequency of 1 second and a turbo reduction of 1%, we can never respond to that type of increase in a timely manner. So we decrease aggressively, increase opportunistically. Why increase by 1% only after a 3C drop in temperature? I originally coded with 1C and the change was very jittery. 3C allows for smoother transitions. The relationship between the turbo and the temperature is unlike the fan speed and the temperature. With the fan, you want higher fan speed at high temperatures, and lower fan speed at lower temperatures. With the turbo, because it's an opportunistic performance mechanism, you want it at maximum at low temperatures until the fans are not enough to contain the increase temperature. My mbpro will settle at 79C with a turbo pct of 80% under hours-long compilation workloads. However, for web browsing and things like that, it will have no issues running at 100% turbo. Sorry this has been a relatively long response to a comment. It's just more intricate than fan control. I'm more than open to suggestions of course. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tested with the latest PR doing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @gktrk Do we have a path forward here? I still don't understand the intended behavior. |
||
|
||
fan = fans; | ||
|
||
while(fan != NULL) { | ||
|
Uh oh!
There was an error while loading. Please reload this page.