Compare commits

...

17 Commits
main ... main

Author SHA1 Message Date
Solène Rapenne a55ad56fbb
move upstream URL 2023-08-23 09:53:47 +02:00
Solène Rapenne a909c1a20e add a gitignore file 2023-08-22 16:34:56 +02:00
Solène Rapenne c851c1cc80 threshold default value is now relative to the CPU number
obsdfreqd uses hw.ncpuonline to calculate a threshold
matching 80% of a single core usage. This helps the
default value to work fine with computer from 1 to many cores.
2023-08-22 16:33:00 +02:00
solene 18eaf38df8 Merge pull request 'use of fprintf() is not async-signal safe; call write() instead' (#2) from seanmcg/obsdfreqd:signal_handler_reentrancy into main
Reviewed-on: solene/obsdfreqd#2
2022-12-01 19:54:52 +00:00
Sean McGovern 8ac1d97556 use of fprintf() is not async-signal safe; call write() instead 2022-12-01 14:28:39 -05:00
Solene Rapenne ee35b241ad README: tweaks 2022-11-12 21:05:56 +01:00
Vlad Meșco 0d3c032afb code improvement
1) in get_temp, check temperature_sensor_found to avoid a bogus sysctl
   if a temperature sensor was not found
2) fix checks for min_freq and max_freq (were improperly checked, resulting
   in a failed sysctl if passing e.g. -m 1600)
3) preserve contents of argv[] because kvm_getargv grabs whatever is in the
   program's argv[], making ps report a different command line compared to
   how the program was started when parsing `,'
2022-11-07 23:31:40 +01:00
Solene Rapenne 48e0c80864 use -O2 instead of -Os 2022-11-07 23:30:45 +01:00
Solene Rapenne 89a2fe9a91 rework examples 2022-10-31 18:25:08 +01:00
Solene Rapenne ee170551b0 improve temperature throttling
- enhance CPU temperature sensor detection
- add a new parameter to give a sensor identifier

patch from Vlad Meşco, thanks!
2022-10-31 18:21:28 +01:00
Klemens Nanni de605f3552 Leave rc.d(8) script handling to the OpenBSD port
sysutils/obsdfreqd now has a local copy of the script as is common with
daemons in port.  Apply the ports patch so the new release can build
without any.
2022-09-12 23:11:35 +02:00
Klemens Nanni df93f44eb7 Sync usage with manual
Make spacing, wrapping as well as argument names match.
Much nicer to read on default 80-char wide xterm.
2022-09-09 17:44:08 +02:00
Klemens Nanni f7aed1accb manual: Use default .Ex in EXIT STATUS
No need to spell out manuall as obsdfreqd follows standard semantics.
2022-09-09 17:44:08 +02:00
Klemens Nanni fd9c73695a manual: Fix spelling 2022-09-09 17:44:08 +02:00
Klemens Nanni 690c0e5c8e manual: Fix "mandoc -T lint" errors and warnings 2022-09-09 17:44:08 +02:00
Klemens Nanni 77d28d3bd6 manual: Fix flags markup
SYNOPSIS already tells which flags are optional, so no need to markup each
flag with .Op individually.

Removing that marco also makes tags work, i.e. ":tv<Enter>" in the pager
will no jump to -v.
2022-09-09 17:44:08 +02:00
solene 6831806411 Merge pull request 'Fix markup' (#1) from rjc/obsdfreqd:main into main
Reviewed-on: solene/obsdfreqd#1
2022-07-06 16:31:27 +00:00
6 changed files with 412 additions and 71 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
obsdfreqd
*.orig

View File

@ -3,8 +3,7 @@ LDFLAGS = -lm
CFLAGS += -pedantic -Wall -Wextra -Wmissing-prototypes \
-Wunused-function -Wshadow -Wstrict-overflow -fno-strict-aliasing \
-Wunused-variable -Wstrict-prototypes -Wwrite-strings \
-Os
-Wunused-variable -Wstrict-prototypes -Wwrite-strings -O2
obsdfreqd: main.c
${CC} ${CFLAGS} ${LDFLAGS} main.c -o $@
@ -16,5 +15,4 @@ clean:
install: obsdfreqd
install -o root -g wheel -m 555 obsdfreqd ${PREFIX}/sbin/obsdfreqd
install -o root -g wheel -m 555 obsdfreqd.rc ${DESTDIR}/etc/rc.d/obsdfreqd
install -o root -g wheel -m 444 obsdfreqd.1 ${PREFIX}/man/man1/obsdfreqd.1

View File

@ -1,15 +1,20 @@
# Project moved
Due to technical issues with tildegit.org, the project moved to sourcehut.
- New upstream URL: https://git.sr.ht/~solene/obsdfreqd
# obsdfreqd
Userland CPU frequency scheduling for OpenBSD >= 7.1
# TLDR
- clone this repository
- as root `make install`
- `pkg_add obsdfreqd`
- as root `rcctl enable obsdfreqd` and `rcctl stop apmd ; rcctl disable apmd`
- as root `rcctl start obsdfreqd`
- apmd can be kept but not with flag `-A`
- most interesting flag for end users is `-T`
- if any tuning is needed, it may be `-T` for temperature limit
# Compilation
@ -19,10 +24,25 @@ As easy as `make`
Run `obsdfreqd` as root, quit with `Ctrl+C`.
# Installation
# Source installation
`make install` as root, enable the service using `rcctl enable
obsdfreqd`.
`make install` as root, enable the service using `rcctl enable obsdfreqd`.
Create `/etc/rc.d/obsdfreqd` with this content and make it executable:
```
#!/bin/ksh
daemon="/usr/local/sbin/obsdfreqd"
. /etc/rc.d/rc.subr
pexp="${daemon}.*"
rc_reload=NO
rc_bg=YES
rc_cmd $1
```
Start the service with `rcctl start obsdfreqd`.
@ -38,10 +58,11 @@ Parameters are applied when both plugged on the wall or on battery, parameters c
- `-i inertia` sets the number of cycles after which the frequency will decay, 0 is the default
- `-m maxfrequency` sets the maximum frequency the CPU can reach in percent, 100% is default
- `-l minfrequency` sets the minimum frequency the CPU must be lowered to, 0% is default
- `-r threshold` sets the CPU usage in % that will trigger the frequency increase, 30% is the default
- `-r threshold` sets the CPU usage in % that will trigger the frequency increase, defaults to 80% of a single core.
- `-s stepfrequency` sets the percent of frequency added every cycle when increasing, 10% is default
- `-t timefreq` sets the milliseconds between each poll, 300 is the default
- `-T maxtemperature` sets the temperature threshold under which the maximum frequency will be temporary lowered until the CPU cools down
- `-S sensor` specify a sensor (using its sysctl path) to use with `-T`
**Example**:

361
main.c
View File

@ -11,6 +11,28 @@
#include <signal.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/errno.h>
#include <assert.h>
#include <syslog.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
int hard_min_freq, batt_min, wall_min;
int hard_max_freq, batt_max, wall_max;
@ -23,28 +45,114 @@ int temp_max, batt_tmax, wall_tmax;
int verbose = 0;
int max;
/* 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);
int get_temp(void);
int get_cpu_online(void);
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*);
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?");
}
}
}
}
int get_temp() {
struct sensor sensor;
int mib[5];
int value = 0;
size_t len = sizeof(sensor);
mib[0] = CTL_HW;
mib[1] = HW_SENSORS;
mib[2] = 0;
mib[3] = SENSOR_TEMP;
mib[4] = 0;
if (!temperature_sensor_found) {
errno = ENOENT;
err(1, "no temperature sensor");
}
if (sysctl(mib, 5, &sensor, &len, NULL, 0) == -1)
err(1, "sysctl to get temperature");
if (sysctl(temperature_mib, 5, &sensor, &len, NULL, 0) == -1)
err(1, "failed to read temperature");
// convert from uK to C
value = (sensor.value - 273150000) / 1000 / 1000;
@ -52,6 +160,21 @@ int get_temp() {
return(value);
}
/* get the number of online CPUs */
int get_cpu_online(void) {
int mib_cpu[2] = { CTL_HW, HW_NCPUONLINE };
int ncpu = 1;
size_t len_ncpu = sizeof(len_ncpu);
if (sysctl(mib_cpu, 2, &ncpu, &len_ncpu, NULL, 0) == -1)
err(1, "sysctl");
if (ncpu <= 0)
err(1, "cpuonline detection");
return(ncpu);
}
/* define the policy to auto or manual */
void set_policy(const char* policy) {
int mib[2] = { CTL_HW, HW_PERFPOLICY };
@ -62,13 +185,39 @@ void set_policy(const char* policy) {
/* restore policy auto upon exit */
void quit_gracefully(int signum) {
printf("Caught signal %d, set auto policy\n", signum);
char message[48];
strncpy(message, "Caught signal ", 14);
switch(signum) {
case SIGINT:
strncat(message, "SIGINT", 6);
break;
case SIGTERM:
strncat(message, "SIGTERM", 7);
break;
default:
break;
}
strncat(message, ", set auto policy\n", 18);
if (isatty(STDERR_FILENO)) {
write(STDERR_FILENO, message, strlen(message));
} else {
struct syslog_data data = SYSLOG_DATA_INIT;
syslog_r(LOG_DAEMON|LOG_INFO, &data, "%s", message);
}
set_policy("auto");
exit(0);
}
void usage(void) {
printf("obsdfreqd [-h] [-v] [-i cycles] [-l min_freq] [-m max_freq] [-d percent_down_freq_step] [-r threshold] [-s percent_freq_step] [-t milliseconds]\n");
fprintf(stderr,
"obsdfreqd [-hv] [-d downstepfrequency] [-i inertia] [-m maxfrequency]\n"
" [-l minfrequency] [-r threshold] [-s stepfrequency]\n"
" [-t timefreq] [-T maxtemperature [-S sensorpath]]\n");
}
/* switch to wall profile */
@ -98,9 +247,15 @@ void switch_batt() {
/* 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) {
void assign_values_from_param(char* parameterk, int* charging, int* battery) {
char *parameter = strdup(parameterk);
int count = 0;
char *token = strtok(parameter, ",");
char *token = NULL;
if(parameter == NULL)
err(1, "malloc failed");
token = strtok(parameter, ",");
while (token != NULL) {
if(count == 0)
@ -120,8 +275,157 @@ void assign_values_from_param(char* parameter, int* charging, int* battery) {
if(count == 1)
*battery = *charging;
free(parameter);
}
/* 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* pathk) {
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* path = NULL;
char* prefix = NULL;
/* 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;
path = strdup(pathk);
if(path == NULL)
err(1, "malloc failed");
prefix = malloc(prefix_len + 1);
if(prefix == NULL)
err(1, "malloc failed");
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",
pathk);
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);
free(path);
}
int main(int argc, char *argv[]) {
int opt;
@ -137,11 +441,17 @@ int main(int argc, char *argv[]) {
int cpu_usage_percent = 0, cpu_usage;
float temp;
size_t len, len_cpu;
int ncpu;
ncpu = get_cpu_online();
// battery defaults
hard_min_freq = batt_min= 0;
hard_max_freq = batt_max= 100;
threshold = batt_threshold= 30;
// threshold defaults to 80% usage of one CPU
threshold = batt_threshold= floor(100/ncpu*0.8);
down_step = batt_down_step= 100;
inertia = batt_inertia= 5;
step = batt_step= 100;
@ -162,7 +472,7 @@ int main(int argc, char *argv[]) {
err(1, "unveil failed");
unveil(NULL, NULL);
while((opt = getopt(argc, argv, "d:hi:l:m:r:s:t:T:v")) != -1) {
while((opt = getopt(argc, argv, "d:hi:l:m:r:s:S:t:T:v")) != -1) {
switch(opt) {
case 'd':
assign_values_from_param(optarg, &wall_down_step, &batt_down_step);
@ -176,12 +486,14 @@ int main(int argc, char *argv[]) {
break;
case 'l':
assign_values_from_param(optarg, &wall_min, &batt_min);
if(hard_min_freq > 100 || hard_min_freq < 0)
if(wall_min > 100 || wall_min < 0 ||
batt_min > 100 || batt_min < 0)
err(1, "minimum frequency must be between 0 and 100");
break;
case 'm':
assign_values_from_param(optarg, &wall_max, &batt_max);
if(hard_max_freq > 100 || hard_max_freq < 0)
if(wall_max > 100 || wall_max < 0 ||
batt_max > 100 || batt_max < 0)
err(1, "maximum frequency must be between 0 and 100");
break;
case 'v':
@ -197,6 +509,11 @@ int main(int argc, char *argv[]) {
if(step > 100 || step <= 0)
err(1, "step must be positive and up to 100");
break;
case 'S':
parse_sensor_path(optarg);
if(!temperature_sensor_found)
err(1, "invalid temperature sensor");
break;
case 't':
assign_values_from_param(optarg, &wall_timefreq, &batt_timefreq);
if(wall_timefreq <= 0 || batt_timefreq <= 0)
@ -233,6 +550,18 @@ int main(int argc, char *argv[]) {
}
}
/* 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;
}
/* avoid weird reading for first delta */
if (sysctl(mib_load, 2, &cpu_previous, &len_cpu, NULL, 0) == -1)
err(1, "sysctl");

View File

@ -6,18 +6,18 @@
.Nd manage the CPU frequency on OpenBSD from userland
.Sh SYNOPSIS
.Nm
.Op Fl v
.Op Fl h
.Op Fl hv
.Op Fl d Ar downstepfrequency
.Op Fl i Ar inertia
.Op Fl m Ar maxfrequency
.Op Fl l Ar minfrequency
.Op Fl r Ar threshold
.Op Fl s Ar stepfrequency
.Op Fl t Ar timefreq
.Op Fl T Ar maxtemperature
.Op Fl t Ar timefrequency
.Oo Fl T Ar maxtemperature
.Op Fl S Ar sensorpath
.Oc
.Sh DESCRIPTION
.Pp
.Nm
is a program to play with the CPU frequency scheduler by providing
many variables involved in the process.
@ -25,51 +25,54 @@ many variables involved in the process.
will switch the frequency policy to manual to manage the frequency itself
and will restore the policy to auto upon exit.
.Sh OPTIONS
Arguments can be comma separated to provide different values for when on battery,
if so, the first value is used when power plug is connected and second value
when on battery.
.Bl -tag -width
.It Op Fl v
Arguments can be comma separated to provide different values for when on
battery, if so, the first value is used when power plug is connected and second
value when on battery.
.Bl -tag -width Ds
.It Fl v
Enable verbose output in CSV format.
.It Op Fl h
.It Fl h
Display usage
.It Op Fl d Ar downstepfrequency
.It Fl d Ar downstepfrequency
Defines the frequency decrease step in percent.
.It Op Fl i Ar inertia
Defines the number of cycles under the CPU threshold before decreasing frequency.
.It Op Fl m Ar maxfrequency
.It Fl i Ar inertia
Defines the number of cycles under the CPU threshold before decreasing
frequency.
.It Fl m Ar maxfrequency
Defines the maximum frequency in percent the CPU can be increased to.
.It Op Fl l Ar minfrequency
.It Fl l Ar minfrequency
Defines the minimum frequency in percent the CPU can be reduced to.
.It Op Fl r Ar threshold
.It Fl r Ar threshold
Defines the CPU usage in percent above which the frequency is increased.
.It Op Fl s Ar stepfrequency
The default is to use a value that match the usage of 80% of a single core.
Example: for a 4 CPU system, the threshold will be 100/4*0.8 = 20%
.It Fl s Ar stepfrequency
Defines the frequency increase step in percent.
.It Op Fl t Ar timefreq
.It Fl t Ar timefrequency
Defines the time in milliseconds between each cycle.
.It Op Fl T Ar maxtemperature
.It Fl T Ar maxtemperature
Defines the maximum temperature in Celcius
.Nm
will allow before lowering the maximum frequency until it the temperature
will allow before lowering the maximum frequency until the temperature
cooled down.
.Ed
.It Fl S Ar sensorpath
In conjunction with
.Sy -T,
.No allows you to specify which sensor to use for temperature readings. If unspecified,
.Nm
will try to guess which temperature sensor to use (usually hw.sensors.cpu0.temp0).
.El
.Sh EXIT STATUS
.Pp
In case of a fatal error,
.Nm
will exit on a status code 1.
In normal operations,
.Nm
will exit on a status code 0.
.Ex
.Sh EXAMPLES
This is a balanced power profile.
Temperature throttling at 50°C
.Bd -literal -offset indent
obsdfreqd -t 150 -r 30 -d 10 -i 2
obsdfreqd -T 50
.Ed
.Pp
Use different values for when on battery to save power.
Temperature throttling at 50°C using sensor hw.sensors.acpithinkpad0.temp0
.Bd -literal -offset indent
obsdfreqd -m 100,60 -r 25,40 -t 90,300 -d 10,100 -i 10,0
obsdfreqd -T 50 -S hw.sensors.acpithinkpad0.temp0
.Ed
.Sh DIAGNOSTICS
.Nm
@ -79,7 +82,6 @@ has a verbose mode to get realtime information about frequency.
.Sh LICENSE
See the LICENSE file for the terms of redistribution.
.Sh BUGS
.Pp
When set to run a startup, if you also start the
.Xr apmd 8
service, make sure to start it with

View File

@ -1,11 +0,0 @@
#!/bin/ksh
daemon="/usr/local/sbin/obsdfreqd"
. /etc/rc.d/rc.subr
pexp="${daemon}.*"
rc_reload=NO
rc_bg=YES
rc_cmd $1