2022-03-16 17:42:28 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <err.h>
|
2022-03-16 18:36:06 +00:00
|
|
|
#include <math.h>
|
2022-03-16 17:42:28 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/sysctl.h>
|
|
|
|
#include <time.h>
|
2022-03-21 22:11:24 +00:00
|
|
|
#include <sys/time.h>
|
|
|
|
#include <sys/sensors.h>
|
2022-03-16 17:42:28 +00:00
|
|
|
#include <sys/sched.h>
|
|
|
|
#include <signal.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <unistd.h>
|
2022-10-31 17:21:28 +00:00
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/errno.h>
|
|
|
|
#include <assert.h>
|
|
|
|
|
|
|
|
/* I have to redefined this because obsdfreqd is compiled with
|
|
|
|
-Wall and I don't know of a compiler agnostic way to disable
|
|
|
|
warnings, and I don't want to assume `cc' is clang forever */
|
|
|
|
struct ctlnameconst {
|
|
|
|
const char* ctl_name; /* struct ctlname has this nonconst */
|
|
|
|
int ctl_type;
|
|
|
|
};
|
|
|
|
#if __STDC_VERSION__ >= 201112L
|
|
|
|
/* be polite to maintainers and give them a better compile error */
|
|
|
|
# include <stddef.h> /* offsetof */
|
|
|
|
static_assert(sizeof(struct ctlnameconst) == sizeof(struct ctlname),
|
|
|
|
"struct ctlname changed, update ctlnameconst");
|
|
|
|
static_assert(offsetof(struct ctlnameconst, ctl_name) == offsetof(struct ctlname, ctl_name),
|
|
|
|
"struct ctlname changed, update ctlnameconst");
|
|
|
|
static_assert(offsetof(struct ctlnameconst, ctl_type) == offsetof(struct ctlname, ctl_type),
|
|
|
|
"struct ctlname changed, update ctlnameconst");
|
|
|
|
#endif
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-21 17:20:32 +00:00
|
|
|
int hard_min_freq, batt_min, wall_min;
|
|
|
|
int hard_max_freq, batt_max, wall_max;
|
|
|
|
int threshold, batt_threshold, wall_threshold;
|
|
|
|
int down_step, batt_down_step, wall_down_step;
|
|
|
|
int inertia, batt_inertia, wall_inertia;
|
|
|
|
int step, batt_step, wall_step;
|
|
|
|
int timefreq, batt_timefreq, wall_timefreq;
|
|
|
|
int temp_max, batt_tmax, wall_tmax;
|
2022-03-21 12:20:12 +00:00
|
|
|
int verbose = 0;
|
2022-03-21 17:20:32 +00:00
|
|
|
int max;
|
2022-03-16 21:05:59 +00:00
|
|
|
|
2022-10-31 17:21:28 +00:00
|
|
|
/* sysctl path to the temperature sensor we're tracking */
|
|
|
|
int temperature_mib[5];
|
|
|
|
/* if false(0), get_temp() will error out */
|
|
|
|
int temperature_sensor_found;
|
|
|
|
|
|
|
|
void try_to_find_default_temperature_sensor(void);
|
2022-03-21 22:11:24 +00:00
|
|
|
int get_temp(void);
|
2022-03-17 13:48:32 +00:00
|
|
|
void set_policy(const char*);
|
|
|
|
void quit_gracefully(int signum);
|
|
|
|
void usage(void);
|
|
|
|
void switch_wall(void);
|
|
|
|
void switch_batt(void);
|
|
|
|
void assign_values_from_param(char*, int*, int*);
|
2022-10-31 17:21:28 +00:00
|
|
|
void parse_sensor_path(char*);
|
|
|
|
|
|
|
|
/* heuristics to find a good temperature sensor */
|
|
|
|
void try_to_find_default_temperature_sensor(void) {
|
|
|
|
/* pick the first temperature sensor, but...
|
|
|
|
bonify devices named `cpuN'
|
|
|
|
TODO and we should probably blacklist devices like:
|
|
|
|
- GPU temperature sensors
|
|
|
|
- battery temperature sensors
|
|
|
|
- UPS temperature sensors
|
|
|
|
...just in case the CPU thermometer is not cpu0, or,
|
|
|
|
for some bizzare reason, there is no CPU thermometer.
|
|
|
|
|
|
|
|
There may be other strange cases, like OpenBSD running
|
|
|
|
on a Raspberry PI where I can connect an external
|
|
|
|
temperature sensor and sensor_install that; but I have
|
|
|
|
a feeling external thermometers would be at the very end */
|
|
|
|
int dev = 0;
|
|
|
|
|
|
|
|
temperature_mib[0] = CTL_HW;
|
|
|
|
temperature_mib[1] = HW_SENSORS;
|
|
|
|
for(dev = 0; ; ++dev) {
|
|
|
|
struct sensordev sensordev;
|
|
|
|
size_t sensordevlen = sizeof(struct sensordev);
|
|
|
|
memset(&sensordev, 0, sizeof(struct sensordev));
|
|
|
|
temperature_mib[2] = dev;
|
|
|
|
if(-1 == sysctl(
|
|
|
|
temperature_mib, 3,
|
|
|
|
&sensordev, &sensordevlen,
|
|
|
|
NULL, 0)
|
|
|
|
) {
|
|
|
|
/* end of array */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* does it have temperature sensors? */
|
|
|
|
if(sensordev.maxnumt[SENSOR_TEMP] <= 0) {
|
|
|
|
/* no */
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* potentially use this */
|
|
|
|
if(!temperature_sensor_found) {
|
|
|
|
temperature_sensor_found = 1;
|
|
|
|
temperature_mib[2] = sensordev.num;
|
|
|
|
temperature_mib[3] = SENSOR_TEMP;
|
|
|
|
temperature_mib[4] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* is it called cpuSOMETHING? */
|
|
|
|
if(strncmp(sensordev.xname, "cpu", 3) == 0) {
|
|
|
|
/* definitely use this! */
|
|
|
|
temperature_sensor_found = 1;
|
|
|
|
temperature_mib[2] = sensordev.num;
|
|
|
|
temperature_mib[3] = SENSOR_TEMP;
|
|
|
|
temperature_mib[4] = 0;
|
|
|
|
/* stop here, this is our best candidate */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(verbose) {
|
|
|
|
if(!temperature_sensor_found) {
|
|
|
|
fprintf(stderr, "Could not find any temperature sensors.\n");
|
|
|
|
} else {
|
|
|
|
/* we need to get the device name again in order to print it */
|
|
|
|
struct sensordev sensordev;
|
|
|
|
size_t sensordevlen = sizeof(struct sensordev);
|
|
|
|
memset(&sensordev, 0, sizeof(struct sensordev));
|
|
|
|
if(0 == sysctl(temperature_mib, 3, &sensordev, &sensordevlen, NULL, 0)) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Using hw.sensors.%s.temp%d as the temperature sensor\n",
|
|
|
|
sensordev.xname,
|
|
|
|
temperature_mib[4]);
|
|
|
|
} else {
|
|
|
|
/* panic? */
|
|
|
|
err(1, "Failed to read sensordev a second time?");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-21 22:11:24 +00:00
|
|
|
int get_temp() {
|
|
|
|
struct sensor sensor;
|
|
|
|
int value = 0;
|
|
|
|
size_t len = sizeof(sensor);
|
2022-03-21 11:44:00 +00:00
|
|
|
|
2022-10-31 17:21:28 +00:00
|
|
|
if (sysctl(temperature_mib, 5, &sensor, &len, NULL, 0) == -1)
|
|
|
|
err(1, "failed to read temperature");
|
2022-03-21 22:11:24 +00:00
|
|
|
|
|
|
|
// convert from uK to C
|
|
|
|
value = (sensor.value - 273150000) / 1000 / 1000;
|
|
|
|
|
|
|
|
return(value);
|
2022-03-21 11:44:00 +00:00
|
|
|
}
|
|
|
|
|
2022-03-16 17:42:28 +00:00
|
|
|
/* define the policy to auto or manual */
|
2022-03-17 13:48:32 +00:00
|
|
|
void set_policy(const char* policy) {
|
2022-03-21 11:46:00 +00:00
|
|
|
int mib[2] = { CTL_HW, HW_PERFPOLICY };
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-21 11:46:00 +00:00
|
|
|
if (sysctl(mib, 2, NULL, 0, (void *)policy, strlen(policy) + 1) == -1)
|
|
|
|
err(1, "sysctl: setting policy");
|
2022-03-16 17:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* restore policy auto upon exit */
|
2022-03-17 13:48:32 +00:00
|
|
|
void quit_gracefully(int signum) {
|
2022-10-31 17:21:28 +00:00
|
|
|
fprintf(stderr, "Caught signal %d, set auto policy\n", signum);
|
2022-03-16 17:42:28 +00:00
|
|
|
set_policy("auto");
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
void usage(void) {
|
2022-09-09 15:10:18 +00:00
|
|
|
fprintf(stderr,
|
|
|
|
"obsdfreqd [-hv] [-d downstepfrequency] [-i inertia] [-m maxfrequency]\n"
|
|
|
|
" [-l minfrequency] [-r threshold] [-s stepfrequency]\n"
|
2022-10-31 17:21:28 +00:00
|
|
|
" [-t timefreq] [-T maxtemperature [-S sensorpath]]\n");
|
2022-03-16 18:36:06 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* switch to wall profile */
|
2022-03-16 21:05:59 +00:00
|
|
|
void switch_wall() {
|
2022-03-21 17:20:32 +00:00
|
|
|
hard_min_freq = wall_min;
|
|
|
|
hard_max_freq = max = wall_max;
|
2022-03-16 21:05:59 +00:00
|
|
|
threshold = wall_threshold;
|
|
|
|
down_step = wall_down_step;
|
|
|
|
inertia = wall_inertia;
|
|
|
|
step = wall_step;
|
|
|
|
timefreq = wall_timefreq;
|
2022-03-21 12:12:24 +00:00
|
|
|
temp_max = wall_tmax;
|
2022-03-16 21:05:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* switch to battery profile */
|
2022-03-16 21:05:59 +00:00
|
|
|
void switch_batt() {
|
2022-03-21 17:20:32 +00:00
|
|
|
hard_min_freq = batt_min;
|
|
|
|
hard_max_freq = max = batt_max;
|
2022-03-16 21:05:59 +00:00
|
|
|
threshold = batt_threshold;
|
|
|
|
down_step = batt_down_step;
|
|
|
|
inertia = batt_inertia;
|
|
|
|
step = batt_step;
|
|
|
|
timefreq = batt_timefreq;
|
2022-03-21 12:12:24 +00:00
|
|
|
temp_max = batt_tmax;
|
2022-03-16 21:05:59 +00:00
|
|
|
}
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* assign values to variable if comma separated
|
|
|
|
* if not, assign value to two variables
|
|
|
|
*/
|
2022-03-16 21:05:59 +00:00
|
|
|
void assign_values_from_param(char* parameter, int* charging, int* battery) {
|
|
|
|
int count = 0;
|
|
|
|
char *token = strtok(parameter, ",");
|
|
|
|
|
|
|
|
while (token != NULL) {
|
|
|
|
if(count == 0)
|
|
|
|
*charging = atoi(token);
|
|
|
|
if(count == 1)
|
|
|
|
*battery = atoi(token);
|
|
|
|
token = strtok(NULL, ",");
|
|
|
|
count++;
|
|
|
|
if(count > 1)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(count == 0) {
|
|
|
|
*charging = atoi(parameter);
|
|
|
|
*battery = *charging;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(count == 1)
|
|
|
|
*battery = *charging;
|
|
|
|
}
|
|
|
|
|
2022-10-31 17:21:28 +00:00
|
|
|
/* parse optarg for a sensor path, sysctl style;
|
|
|
|
* expecting hw.sensors.*.temp*;
|
|
|
|
* sets temperature_mid and temperature_sensor_found
|
|
|
|
* IFF it's a valid path, else sets errno to something
|
|
|
|
* relevant.
|
|
|
|
*/
|
|
|
|
void parse_sensor_path(char* path) {
|
|
|
|
const struct ctlnameconst ctlnames[] = CTL_NAMES;
|
|
|
|
const struct ctlnameconst ctlhwnames[] = CTL_HW_NAMES;
|
|
|
|
const size_t prefix_len =
|
|
|
|
/*hw*/strlen(ctlnames[CTL_HW].ctl_name) +
|
|
|
|
/*.*/1 +
|
|
|
|
/*sensors*/strlen(ctlhwnames[HW_SENSORS].ctl_name) +
|
|
|
|
/*.*/1;
|
|
|
|
char* prefix = malloc(prefix_len + 1);
|
|
|
|
/* these are pointers into optarg */
|
|
|
|
char* sensordevname = NULL, *sensortype = NULL, *sensornumts = NULL;
|
|
|
|
int sensornumt = -1;
|
|
|
|
const size_t tempslen = strlen(sensor_type_s[SENSOR_TEMP]);
|
|
|
|
const char* errstr = NULL;
|
|
|
|
int dev = 0;
|
|
|
|
|
|
|
|
if(prefix == NULL) {
|
|
|
|
errno = ENOMEM;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
memset(prefix, 0, prefix_len + 1);
|
|
|
|
strcpy(prefix, ctlnames[CTL_HW].ctl_name);
|
|
|
|
strcat(prefix, ".");
|
|
|
|
strcat(prefix, ctlhwnames[HW_SENSORS].ctl_name);
|
|
|
|
strcat(prefix, ".");
|
|
|
|
|
|
|
|
/* we only allow hw.sensors...; everything else is irrelevant */
|
|
|
|
if(strncmp(path, prefix, prefix_len) != 0) {
|
|
|
|
if(verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Valid temperature sensors start with %s\n"
|
|
|
|
"Run `sysctl -a' to find sensors",
|
|
|
|
prefix);
|
|
|
|
errno = EINVAL;
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* parse sensordevname and tail */
|
|
|
|
sensordevname = path + prefix_len;
|
|
|
|
sensortype = strchr(sensordevname, '.');
|
|
|
|
if(!sensortype) {
|
|
|
|
if(verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"Valid sensor names are of the form hw.sensors.device.temp42\n"
|
|
|
|
"for example hw.sensors.cpu0.temp0\n");
|
|
|
|
errno = EINVAL;
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
*sensortype++ = '\0'; /* save sensordevname, we need to find it later */
|
|
|
|
|
|
|
|
/* assert tail strats with "temp" */
|
|
|
|
if(strncmp(sensortype, sensor_type_s[SENSOR_TEMP], tempslen) != 0) {
|
|
|
|
if(verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s does not look like a temperature sensor\n",
|
|
|
|
path);
|
|
|
|
errno = EINVAL;
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* convert remaining string to number = numt */
|
|
|
|
sensornumts = sensortype + tempslen;
|
|
|
|
sensornumt = strtonum(sensornumts, 0, INT_MAX, &errstr);
|
|
|
|
if(errstr != NULL) {
|
|
|
|
if(verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"%s does not look like a temperature sensor\n",
|
|
|
|
path);
|
|
|
|
errno = EINVAL;
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* the string format looks good, now let's see if we can actually
|
|
|
|
find the sensor */
|
|
|
|
temperature_mib[0] = CTL_HW;
|
|
|
|
temperature_mib[1] = HW_SENSORS;
|
|
|
|
/* this is a NULL terminated array of sensordev's */
|
|
|
|
for(dev = 0; ; ++dev) {
|
|
|
|
struct sensordev sensordev;
|
|
|
|
size_t sensordevlen = sizeof(struct sensordev);
|
|
|
|
|
|
|
|
memset(&sensordev, 0, sizeof(struct sensordev));
|
|
|
|
temperature_mib[2] = dev;
|
|
|
|
if(-1 == sysctl(
|
|
|
|
temperature_mib, 3,
|
|
|
|
&sensordev, &sensordevlen,
|
|
|
|
NULL, 0)
|
|
|
|
) {
|
|
|
|
/* probably end of array; sysctl sets errno to ENOENT */
|
|
|
|
/* the man page doesn't say what other errno's it could
|
|
|
|
set, so let's assume it *is* ENOENT */
|
|
|
|
if(verbose)
|
|
|
|
/* using warn to print errno in case it wasn't ENOENT */
|
|
|
|
warn("No such sensor device: %s\n", sensordevname);
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert(sensordevlen == sizeof(struct sensordev));
|
|
|
|
|
|
|
|
if(strcmp(sensordevname, sensordev.xname) == 0) {
|
|
|
|
/* found it; before we yield success, check if it has
|
|
|
|
a temperature sensor */
|
|
|
|
if(sensornumt >= sensordev.maxnumt[SENSOR_TEMP]) {
|
|
|
|
/* no dice */
|
|
|
|
if(verbose)
|
|
|
|
fprintf(stderr,
|
|
|
|
"temp%d is out of range, max is temp%d\n",
|
|
|
|
sensornumt,
|
|
|
|
sensordev.maxnumt[SENSOR_TEMP]);
|
|
|
|
errno = ENOENT;
|
|
|
|
goto badprefix;
|
|
|
|
}
|
|
|
|
temperature_mib[2] = sensordev.num;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* okay, we have it; finish populating mib and claim we're done */
|
|
|
|
temperature_mib[3] = SENSOR_TEMP;
|
|
|
|
temperature_mib[4] = sensornumt;
|
|
|
|
temperature_sensor_found = 1;
|
|
|
|
|
|
|
|
if(verbose) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"Using hw.sensors.%s.temp%d as the temperature sensor\n",
|
|
|
|
sensordevname,
|
|
|
|
sensornumt);
|
|
|
|
}
|
|
|
|
|
|
|
|
badprefix:
|
|
|
|
free(prefix);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-03-16 18:36:06 +00:00
|
|
|
int main(int argc, char *argv[]) {
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-16 18:36:06 +00:00
|
|
|
int opt;
|
2022-03-21 12:33:11 +00:00
|
|
|
|
|
|
|
int mib_perf[2] = { CTL_HW, HW_SETPERF };
|
|
|
|
int mib_powerplug[2] = { CTL_HW, HW_POWER };
|
|
|
|
int mib_load[2] = { CTL_KERN, KERN_CPTIME };
|
|
|
|
|
2022-03-16 17:42:28 +00:00
|
|
|
long cpu[CPUSTATES], cpu_previous[CPUSTATES];
|
2022-03-16 19:15:15 +00:00
|
|
|
int frequency = 0;
|
2022-04-09 15:19:16 +00:00
|
|
|
int current_mode = 0;
|
2022-03-16 19:15:15 +00:00
|
|
|
int value, current_frequency, inertia_timer = 0;
|
2022-03-16 18:52:51 +00:00
|
|
|
int cpu_usage_percent = 0, cpu_usage;
|
2022-03-21 11:44:00 +00:00
|
|
|
float temp;
|
2022-03-16 17:42:28 +00:00
|
|
|
size_t len, len_cpu;
|
|
|
|
|
2022-03-21 11:50:00 +00:00
|
|
|
// battery defaults
|
2022-03-21 17:20:32 +00:00
|
|
|
hard_min_freq = batt_min= 0;
|
|
|
|
hard_max_freq = batt_max= 100;
|
|
|
|
threshold = batt_threshold= 30;
|
|
|
|
down_step = batt_down_step= 100;
|
|
|
|
inertia = batt_inertia= 5;
|
|
|
|
step = batt_step= 100;
|
|
|
|
timefreq = batt_timefreq= 100;
|
|
|
|
temp_max = batt_tmax= 0;
|
2022-03-21 11:50:00 +00:00
|
|
|
|
|
|
|
// wall defaults
|
2022-03-21 12:12:24 +00:00
|
|
|
wall_min= 0;
|
|
|
|
wall_max= 100;
|
|
|
|
wall_threshold= 30;
|
|
|
|
wall_down_step= 30;
|
2022-03-21 12:36:15 +00:00
|
|
|
wall_inertia= 10;
|
2022-03-21 12:12:24 +00:00
|
|
|
wall_step= 100;
|
|
|
|
wall_timefreq= 100;
|
|
|
|
wall_tmax= 0;
|
2022-03-16 21:05:59 +00:00
|
|
|
|
2022-03-21 22:11:24 +00:00
|
|
|
if (unveil("/var/empty", "r") == -1)
|
|
|
|
err(1, "unveil failed");
|
|
|
|
unveil(NULL, NULL);
|
2022-03-16 18:49:28 +00:00
|
|
|
|
2022-10-31 17:21:28 +00:00
|
|
|
while((opt = getopt(argc, argv, "d:hi:l:m:r:s:S:t:T:v")) != -1) {
|
2022-03-16 18:36:06 +00:00
|
|
|
switch(opt) {
|
2022-03-16 19:15:15 +00:00
|
|
|
case 'd':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_down_step, &batt_down_step);
|
2022-03-16 19:15:15 +00:00
|
|
|
if(down_step > 100 || down_step <= 0)
|
|
|
|
err(1, "decay step must be positive and up to 100");
|
|
|
|
break;
|
|
|
|
case 'i':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_inertia, &batt_inertia);
|
2022-03-16 19:15:15 +00:00
|
|
|
if(inertia < 0)
|
|
|
|
err(1, "inertia must be positive");
|
|
|
|
break;
|
2022-03-16 18:36:06 +00:00
|
|
|
case 'l':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_min, &batt_min);
|
2022-03-21 17:20:32 +00:00
|
|
|
if(hard_min_freq > 100 || hard_min_freq < 0)
|
2022-03-16 18:36:06 +00:00
|
|
|
err(1, "minimum frequency must be between 0 and 100");
|
|
|
|
break;
|
|
|
|
case 'm':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_max, &batt_max);
|
2022-03-21 17:20:32 +00:00
|
|
|
if(hard_max_freq > 100 || hard_max_freq < 0)
|
2022-03-16 18:36:06 +00:00
|
|
|
err(1, "maximum frequency must be between 0 and 100");
|
|
|
|
break;
|
2022-03-21 12:20:12 +00:00
|
|
|
case 'v':
|
|
|
|
verbose = 1;
|
2022-03-16 21:05:59 +00:00
|
|
|
break;
|
2022-03-16 19:15:15 +00:00
|
|
|
case 'r':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_threshold, &batt_threshold);
|
2022-03-16 19:15:15 +00:00
|
|
|
if(threshold < 0)
|
|
|
|
err(1, "CPU use threshold must be positive");
|
|
|
|
break;
|
2022-03-16 18:36:06 +00:00
|
|
|
case 's':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_step, &batt_step);
|
2022-03-16 18:36:06 +00:00
|
|
|
if(step > 100 || step <= 0)
|
|
|
|
err(1, "step must be positive and up to 100");
|
|
|
|
break;
|
2022-10-31 17:21:28 +00:00
|
|
|
case 'S':
|
|
|
|
parse_sensor_path(optarg);
|
|
|
|
if(!temperature_sensor_found)
|
|
|
|
err(1, "invalid temperature sensor");
|
|
|
|
break;
|
2022-03-16 18:49:42 +00:00
|
|
|
case 't':
|
2022-03-16 21:05:59 +00:00
|
|
|
assign_values_from_param(optarg, &wall_timefreq, &batt_timefreq);
|
|
|
|
if(wall_timefreq <= 0 || batt_timefreq <= 0)
|
|
|
|
err(1, "time frequency must be positive");
|
|
|
|
break;
|
2022-03-21 11:44:00 +00:00
|
|
|
case 'T':
|
2022-03-21 12:12:24 +00:00
|
|
|
assign_values_from_param(optarg, &wall_tmax, &batt_tmax);
|
|
|
|
if(wall_tmax <= 0 || batt_tmax <= 0)
|
|
|
|
err(1, "temperature must be positive");
|
2022-03-21 11:44:00 +00:00
|
|
|
break;
|
2022-03-16 18:40:40 +00:00
|
|
|
case 'h':
|
2022-03-16 18:36:06 +00:00
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 17:42:28 +00:00
|
|
|
len = sizeof(value);
|
|
|
|
len_cpu = sizeof(cpu);
|
|
|
|
|
2022-03-16 21:45:21 +00:00
|
|
|
signal(SIGINT, quit_gracefully);
|
|
|
|
signal(SIGTERM, quit_gracefully);
|
2022-03-16 17:42:28 +00:00
|
|
|
set_policy("manual");
|
2022-03-21 17:20:32 +00:00
|
|
|
switch_batt();
|
|
|
|
|
|
|
|
if(hard_max_freq < hard_min_freq)
|
|
|
|
err(1, "maximum frequency can't be smaller than minimum frequency");
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-21 12:20:12 +00:00
|
|
|
if (verbose) {
|
|
|
|
if(temp_max > 0) {
|
|
|
|
printf("mode;Temperature;maximum_frequency;current_frequency;cpu usage;inertia;new frequency\n");
|
|
|
|
} else {
|
|
|
|
printf("mode;current_frequency;cpu usage;inertia;new frequency\n");
|
|
|
|
}
|
|
|
|
}
|
2022-03-17 20:18:32 +00:00
|
|
|
|
2022-10-31 17:21:28 +00:00
|
|
|
/* initialize default temp sensor if we need to */
|
|
|
|
if(temp_max > 0 && !temperature_sensor_found) {
|
|
|
|
try_to_find_default_temperature_sensor();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* warn if -S was specified, but not -T */
|
|
|
|
if(temp_max <= 0 && temperature_sensor_found) {
|
|
|
|
errno = EINVAL;
|
|
|
|
warn("you have specified a temperature sensor without setting\ntemperature limits with -T");
|
|
|
|
errno = 0;
|
|
|
|
}
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* avoid weird reading for first delta */
|
2022-03-16 17:42:28 +00:00
|
|
|
if (sysctl(mib_load, 2, &cpu_previous, &len_cpu, NULL, 0) == -1)
|
|
|
|
err(1, "sysctl");
|
2022-03-16 18:36:06 +00:00
|
|
|
usleep(1000*500);
|
2022-03-16 17:42:28 +00:00
|
|
|
|
|
|
|
/* main loop */
|
|
|
|
for(;;) {
|
2022-03-17 13:48:32 +00:00
|
|
|
/* get if using power plug or not */
|
2022-03-16 17:42:28 +00:00
|
|
|
if (sysctl(mib_powerplug, 2, &value, &len, NULL, 0) == -1)
|
|
|
|
err(1, "sysctl");
|
2022-03-16 21:05:59 +00:00
|
|
|
|
2022-03-21 12:20:12 +00:00
|
|
|
if(verbose) printf("%i;", value);
|
|
|
|
|
2022-03-21 07:35:00 +00:00
|
|
|
if(current_mode != value) {
|
|
|
|
current_mode = value;
|
|
|
|
if(value == 0)
|
|
|
|
switch_batt();
|
|
|
|
else
|
|
|
|
switch_wall();
|
|
|
|
}
|
2022-03-16 21:05:59 +00:00
|
|
|
|
2022-03-21 17:20:59 +00:00
|
|
|
/* manage temperature */
|
2022-03-21 11:50:00 +00:00
|
|
|
if(temp_max > 0) {
|
2022-03-21 17:20:59 +00:00
|
|
|
|
2022-03-21 22:11:24 +00:00
|
|
|
temp = get_temp();
|
|
|
|
|
2022-03-21 11:50:00 +00:00
|
|
|
if(temp > temp_max) {
|
2022-03-21 17:20:32 +00:00
|
|
|
if(max > hard_min_freq)
|
2022-03-21 11:50:00 +00:00
|
|
|
max--;
|
|
|
|
} else {
|
2022-03-21 17:20:32 +00:00
|
|
|
if(max < hard_max_freq)
|
2022-03-21 11:50:00 +00:00
|
|
|
max++;
|
|
|
|
}
|
2022-03-21 12:20:12 +00:00
|
|
|
if(verbose) printf("%.0f;%i;", temp, max);
|
2022-03-21 11:50:00 +00:00
|
|
|
}
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* get current frequency */
|
2022-03-16 18:36:06 +00:00
|
|
|
if (sysctl(mib_perf, 2, ¤t_frequency, &len, NULL, 0) == -1)
|
2022-03-16 17:42:28 +00:00
|
|
|
err(1, "sysctl");
|
2022-03-21 12:20:12 +00:00
|
|
|
if(verbose) printf("%i;", current_frequency);
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* get where the CPU time is spent, last field is IDLE */
|
2022-03-16 17:42:28 +00:00
|
|
|
if (sysctl(mib_load, 2, &cpu, &len_cpu, NULL, 0) == -1)
|
|
|
|
err(1, "sysctl");
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* calculate delta between old and last cpu readings */
|
2022-03-16 17:42:28 +00:00
|
|
|
cpu_usage = cpu[0]-cpu_previous[0] +
|
|
|
|
cpu[1]-cpu_previous[1] +
|
|
|
|
cpu[2]-cpu_previous[2] +
|
|
|
|
cpu[3]-cpu_previous[3] +
|
2022-03-16 18:36:06 +00:00
|
|
|
cpu[4]-cpu_previous[4] +
|
|
|
|
cpu[5]-cpu_previous[5];
|
|
|
|
|
2022-03-16 18:52:51 +00:00
|
|
|
cpu_usage_percent = 100-round(100*(cpu[5]-cpu_previous[5])/cpu_usage);
|
2022-03-16 17:42:28 +00:00
|
|
|
memcpy(cpu_previous, cpu, sizeof(cpu));
|
2022-03-21 12:20:12 +00:00
|
|
|
if(verbose) printf("%i;", cpu_usage_percent);
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* change frequency */
|
2022-03-16 17:42:28 +00:00
|
|
|
len = sizeof(frequency);
|
2022-03-16 18:36:06 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* small brain condition to increase CPU */
|
2022-03-16 19:15:15 +00:00
|
|
|
if(cpu_usage_percent > threshold) {
|
2022-03-16 18:36:06 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* increase frequency by step if under max */
|
2022-03-16 19:15:15 +00:00
|
|
|
if(frequency+step < max)
|
2022-03-16 18:36:06 +00:00
|
|
|
frequency = frequency + step;
|
2022-03-16 19:15:15 +00:00
|
|
|
else
|
|
|
|
frequency = max;
|
2022-03-16 18:36:06 +00:00
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* don't try to set frequency more than 100% */
|
2022-03-21 17:20:32 +00:00
|
|
|
if( frequency > hard_max_freq )
|
|
|
|
frequency = hard_max_freq;
|
2022-03-16 18:36:06 +00:00
|
|
|
|
2022-03-17 20:18:53 +00:00
|
|
|
if(inertia_timer < inertia)
|
|
|
|
inertia_timer++;
|
2022-03-16 19:15:15 +00:00
|
|
|
|
2022-03-16 17:42:28 +00:00
|
|
|
if (sysctl(mib_perf, 2, NULL, 0, &frequency, len) == -1)
|
|
|
|
err(1, "sysctl");
|
2022-03-21 11:46:00 +00:00
|
|
|
|
|
|
|
} else {
|
2022-03-16 19:15:15 +00:00
|
|
|
|
|
|
|
if(inertia_timer == 0) {
|
2022-03-17 13:48:32 +00:00
|
|
|
/* keep frequency more than min */
|
2022-03-21 17:20:32 +00:00
|
|
|
if(frequency-down_step < hard_min_freq)
|
|
|
|
frequency = hard_min_freq;
|
2022-03-16 19:15:15 +00:00
|
|
|
else
|
|
|
|
frequency = frequency - down_step;
|
|
|
|
|
2022-03-17 13:48:32 +00:00
|
|
|
/* don't try to set frequency below 0% */
|
2022-03-21 17:20:32 +00:00
|
|
|
if (frequency < hard_min_freq )
|
|
|
|
frequency = hard_min_freq;
|
2022-03-16 19:15:15 +00:00
|
|
|
|
|
|
|
if (sysctl(mib_perf, 2, NULL, 0, &frequency, len) == -1)
|
|
|
|
err(1, "sysctl");
|
|
|
|
} else {
|
|
|
|
inertia_timer--;
|
|
|
|
}
|
2022-03-16 17:42:28 +00:00
|
|
|
}
|
|
|
|
|
2022-03-21 12:20:12 +00:00
|
|
|
if(verbose) printf("%i;%i\n", inertia_timer, frequency);
|
2022-03-16 17:42:28 +00:00
|
|
|
|
2022-03-16 18:49:42 +00:00
|
|
|
usleep(1000*timefreq);
|
2022-03-16 17:42:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return(0);
|
|
|
|
}
|