This repository has been archived on 2023-08-23. You can view files and clone it, but cannot push or open issues or pull requests.
obsdfreqd/main.c

341 lines
9.5 KiB
C
Raw Normal View History

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>
#include <sys/sched.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
int min, batt_min, wall_min;
int max, 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 11:44:00 +00:00
float 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-03-16 17:42:28 +00:00
2022-03-21 11:44:00 +00:00
float get_temp() {
FILE *fp;
char path[1035];
char *eptr;
fp = popen("/usr/sbin/sysctl -n hw.sensors.cpu0", "r");
if (fp == NULL)
err(1, "popen failed");
fgets(path, sizeof(path), fp);
char *token = strtok(path, " ");
pclose(fp);
return(strtof(token, &eptr));
}
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) {
printf("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-03-16 19:27:16 +00:00
printf("obsdfreqd [-h] [-q] [-i cycles] [-l min_freq] [-m max_freq] [-d percent_down_freq_step] [-r threshold] [-s percent_freq_step] [-t milliseconds]\n");
2022-03-16 18:36:06 +00:00
}
2022-03-17 13:48:32 +00:00
/* switch to wall profile */
void switch_wall() {
min = wall_min;
max = wall_max;
threshold = wall_threshold;
down_step = wall_down_step;
inertia = wall_inertia;
step = wall_step;
timefreq = wall_timefreq;
temp_max = wall_tmax;
}
2022-03-17 13:48:32 +00:00
/* switch to battery profile */
void switch_batt() {
min = batt_min;
max = batt_max;
threshold = batt_threshold;
down_step = batt_down_step;
inertia = batt_inertia;
step = batt_step;
timefreq = batt_timefreq;
temp_max = batt_tmax;
}
2022-03-17 13:48:32 +00:00
/* assign values to variable if comma separated
* if not, assign value to two variables
*/
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-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-16 17:42:28 +00:00
int mib_perf[2];
int mib_powerplug[2];
int mib_load[2];
long cpu[CPUSTATES], cpu_previous[CPUSTATES];
int frequency = 0;
int current_mode;
2022-03-16 19:27:16 +00:00
int quiet = 0;
int value, current_frequency, inertia_timer = 0;
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;
// battery defaults
min = batt_min= 0;
max = 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;
// wall defaults
wall_min= 0;
wall_max= 100;
wall_threshold= 30;
wall_down_step= 30;
wall_inertia= 5;
wall_step= 100;
wall_timefreq= 100;
wall_tmax= 0;
2022-03-21 11:44:00 +00:00
//if (unveil("/var/empty", "r") == -1)
// err(1, "unveil failed");
//unveil(NULL, NULL);
2022-03-16 18:49:28 +00:00
2022-03-21 11:44:00 +00:00
while((opt = getopt(argc, argv, "d:hi:l:m:qr:s:t:T:")) != -1) {
2022-03-16 18:36:06 +00:00
switch(opt) {
case 'd':
assign_values_from_param(optarg, &wall_down_step, &batt_down_step);
if(down_step > 100 || down_step <= 0)
err(1, "decay step must be positive and up to 100");
break;
case 'i':
assign_values_from_param(optarg, &wall_inertia, &batt_inertia);
if(inertia < 0)
err(1, "inertia must be positive");
break;
2022-03-16 18:36:06 +00:00
case 'l':
assign_values_from_param(optarg, &wall_min, &batt_min);
2022-03-16 18:36:06 +00:00
if(min > 100 || min < 0)
err(1, "minimum frequency must be between 0 and 100");
break;
case 'm':
assign_values_from_param(optarg, &wall_max, &batt_max);
2022-03-16 18:36:06 +00:00
if(max > 100 || max < 0)
err(1, "maximum frequency must be between 0 and 100");
break;
2022-03-16 19:27:16 +00:00
case 'q':
quiet = 1;
break;
case 'r':
assign_values_from_param(optarg, &wall_threshold, &batt_threshold);
if(threshold < 0)
err(1, "CPU use threshold must be positive");
break;
2022-03-16 18:36:06 +00:00
case 's':
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;
case 't':
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':
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
mib_perf[0] = CTL_HW;
mib_perf[1] = HW_SETPERF;
mib_powerplug[0] = CTL_HW;
mib_powerplug[1] = HW_POWER;
mib_load[0] = CTL_KERN;
mib_load[1] = KERN_CPTIME;
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");
if (quiet == 0)
printf("mode;Temperature;maximum_frequency;current_frequency;cpu usage;inertia;new frequency\n");
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");
if(quiet == 0) printf("%i;", value);
if(current_mode != value) {
current_mode = value;
if(value == 0)
switch_batt();
else
switch_wall();
}
// manage temperature
if(temp_max > 0) {
temp = get_temp();
if(temp > temp_max) {
if(max > min)
max--;
} else {
if(max < 100)
max++;
}
printf("%.0f;%i;", temp, max);
}
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, &current_frequency, &len, NULL, 0) == -1)
2022-03-16 17:42:28 +00:00
err(1, "sysctl");
if(quiet == 0) 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-17 13:48:32 +00:00
/* debug */
/*
if(quiet == 0) printf("\nDEBUG: User: %3i\tNice: %3i\t Sys: %3i\tSpin: %3i\t Intr: %3i\tIdle: %3i\n",
cpu[0]-cpu_previous[0],
cpu[1]-cpu_previous[1],
cpu[2]-cpu_previous[2],
cpu[3]-cpu_previous[3],
cpu[4]-cpu_previous[4],
cpu[5]-cpu_previous[5]);
if(quiet == 0) printf("cpu usage = %i et idle = %i\n", cpu_usage, cpu[5] - cpu_previous[5]);
*/
2022-03-16 18:36:06 +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));
if(quiet == 0) 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 */
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 */
if(frequency+step < max)
2022-03-16 18:36:06 +00:00
frequency = frequency + step;
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-16 18:36:06 +00:00
if( frequency > 100 )
frequency = 100;
2022-03-17 20:18:53 +00:00
if(inertia_timer < inertia)
inertia_timer++;
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 {
if(inertia_timer == 0) {
2022-03-17 13:48:32 +00:00
/* keep frequency more than min */
if(frequency-down_step < min)
frequency = min;
else
frequency = frequency - down_step;
2022-03-17 13:48:32 +00:00
/* don't try to set frequency below 0% */
if (frequency < 0 )
frequency = 0;
if (sysctl(mib_perf, 2, NULL, 0, &frequency, len) == -1)
err(1, "sysctl");
} else {
inertia_timer--;
}
2022-03-16 17:42:28 +00:00
}
if(quiet == 0) printf("%i;%i;", inertia_timer, frequency);
2022-03-16 17:42:28 +00:00
2022-03-16 19:27:16 +00:00
if(quiet == 0) printf("\n");
usleep(1000*timefreq);
2022-03-16 17:42:28 +00:00
}
return(0);
}