CPUFREQ(4)

๐Ÿ“ฆ version 15.1, in freebsd 14.5, freebsd 15.1, nixpkgs unstable

๐Ÿฆ‹ CPUFREQ(4) โ€” like, the CPU frequency control framework, bestie ๐Ÿฆ‹

Okay so this is the cpufreq page from section 4 of the Kernel Interfaces Manual, and honestly? It's giving "let me introduce you to the friend group that runs your CPU's clock" ๐Ÿ’… Stick with me, it's all gonna make sense.


โœจ NAME โœจ

cpufreq โ€” the CPU frequency control framework. That's the whole vibe. ๐Ÿ‘‘


๐ŸŒธ SYNOPSIS ๐ŸŒธ

device cpufreq

#include <sys/cpu.h>

int
cpufreq_levels(device_t dev, struct cf_level *levels, int *count);

int
cpufreq_set(device_t dev, const struct cf_level *level, int priority);

int
cpufreq_get(device_t dev, struct cf_level *level);

int
cpufreq_drv_settings(device_t dev, struct cf_setting *sets, int *count);

int
cpufreq_drv_type(device_t dev, int *type);

int
cpufreq_drv_set(device_t dev, const struct cf_setting *set);

int
cpufreq_drv_get(device_t dev, struct cf_setting *set);

๐Ÿ’– DESCRIPTION ๐Ÿ’–

The cpufreq driver is literally the ultimate group-chat admin for CPU frequency control drivers โ€” it gives you one unified kernel and user interface to all of them. ๐Ÿซถ It takes multiple drivers that each offer different settings and smushes them into a single interface of all possible levels. Iconic behavior, no cap.

You can hit this interface directly via sysctl(8), or you can just tell /etc/rc.d/power_profile that it should switch settings whenever the AC line state changes โ€” you do that through rc.conf(5). Set it and forget it, fr fr. โœจ


๐ŸŽ€ SYSCTL VARIABLES ๐ŸŽ€

Heads up bestie: these settings may be overridden by kernel drivers requesting alternate settings. If that happens, the original values get restored once the condition has passed (think: the system has cooled sufficiently ๐Ÿ˜ฎโ€๐Ÿ’จ). And if a sysctl can't be set because of an override condition, it returns EPERM. Rude, but okay.

โš ๏ธ Big warning time โš ๏ธ The frequency cannot be changed if TSC is in use as the timecounter and the hardware does not support invariant TSC. Why? Because the timecounter system needs a source with a constant rate โ€” that's just the rules. (On invariant TSC hardware, the TSC runs at the P0 rate regardless of the configured P-state, so it's fine.) Modern hardware mostly has invariant TSC, so this is usually a non-issue. You can change the timecounter source with the kern.timecounter.hardware sysctl, and the available modes live in the kern.timecounter.choice sysctl entry. ๐Ÿ’…
  • dev.cpu.%d.freq โ€” the current active CPU frequency, in MHz. Live and in color. ๐Ÿ”ฅ
  • dev.cpu.%d.freq_driver โ€” the specific cpufreq driver that this cpu is using. Main character reveal.
  • dev.cpu.%d.freq_levels โ€” the currently available levels for the CPU, as frequency/power usage. Values are in units of MHz and milliwatts. โญ
  • dev.DEVICE.%d.freq_settings โ€” the currently available settings for the driver, also frequency/power usage, also MHz and milliwatts. This one is lowkey a debugging hero โ€” it's helpful for understanding which settings are offered by which driver. ๐Ÿ”
  • debug.cpufreq.lowest โ€” the lowest CPU frequency in MHz to offer to users. Also accessible via a tunable with the same name. Use it to disable very low levels that may be unusable on some systems. ๐Ÿ™ˆ
  • debug.cpufreq.verbose โ€” prints verbose messages. Also accessible via a tunable with the same name. For the girlies who need the lore. ๐Ÿ“–
  • debug.hwpstate_pstate_limit โ€” if enabled, the AMD hwpstate driver limits administrative control of P-states (including by powerd(8)) to the value in the 0xc0010061 MSR, known as "PStateCurLim[CurPstateLimit]." It is disabled (0) by default. On some hardware, the limit register seems to simply follow the configured P-state, which results in the inability to ever raise the P-state back to P0 from a reduced frequency state. ๐Ÿ˜ญ So like, tread carefully.

๐Ÿฆ‹ SUPPORTED DRIVERS ๐Ÿฆ‹

Okay so these drivers below offer absolute frequency control via the cpufreq interface. Usually only one of these can be active at a time, so it's giving "there can only be one" energy:

  • acpi_perf โ€” ACPI CPU performance states
  • est(4) โ€” Intel Enhanced SpeedStep
  • hwpstate โ€” AMD Cool'n'Quiet2 used in K10 through Family 17h
  • hwpstate_intel(4) โ€” Intel SpeedShift driver
  • ichss โ€” Intel SpeedStep for ICH
  • powernow โ€” AMD PowerNow! and Cool'n'Quiet for K7 and K8
  • smist โ€” Intel SMI-based SpeedStep for PIIX4

And these next ones are a different story โ€” they offer relative frequency control and have an additive effect. Teamwork makes the dream work ๐Ÿซถ:

  • acpi_throttle โ€” ACPI CPU throttling
  • p4tcc โ€” Pentium 4 Thermal Control Circuitry

๐Ÿ’œ KERNEL INTERFACE ๐Ÿ’œ

Kernel components can query and set CPU frequencies through the cpufreq kernel interface. The whole little routine goes like this:

  1. Obtain a cpufreq device.
  2. Call cpufreq_levels() to get the currently available frequency levels.
  3. Check the current level with cpufreq_get().
  4. Set a new one from the list with cpufreq_set().

Each level may actually reference more than one cpufreq driver, but kernel components do not need to be aware of this โ€” don't worry about it bestie, it's handled. ๐Ÿ’… The total_set element of struct cf_level gives you a summary of the frequency and power for that level. Unknown or irrelevant values are set to CPUFREQ_VAL_UNKNOWN.

  • cpufreq_levels() โ€” takes a cpufreq device and an empty array of levels. The count value should be set to the number of levels available, and after the function completes it will be set to the actual number of levels returned. If there are more levels than count will allow, it should return E2BIG. ๐Ÿ˜ค
  • cpufreq_get() โ€” takes a pointer to space to store a level. After successful completion, the output will be the current active level, and it's equal to one of the levels returned by cpufreq_levels(). Matching set, slay. โœจ
  • cpufreq_set() โ€” takes a pointer to a level and attempts to activate it. The priority (i.e., CPUFREQ_PRIO_KERN) tells cpufreq whether to override previous settings while activating this level. If priority is higher than the current active level, that level gets saved and overridden with the new level. If a level is already saved, the new level is set without overwriting the older saved level. If cpufreq_set() is called with a NULL level, the saved level gets restored. If there is no saved level, cpufreq_set() returns ENXIO. And if priority is lower than the current active level's priority, this method returns EPERM. Know your rank, babe. ๐Ÿ‘‘

๐ŸŒˆ DRIVER INTERFACE ๐ŸŒˆ

Kernel drivers that offer hardware-specific CPU frequency control export their individual settings through the cpufreq driver interface. That means implementing these methods:

  • cpufreq_drv_settings()
  • cpufreq_drv_type()
  • cpufreq_drv_set()
  • cpufreq_drv_get()

Plus, the driver must attach a device as a child of a CPU device so that these methods can be called by the cpufreq framework. No shortcuts, bestie. ๐Ÿซถ

๐ŸŒธ cpufreq_drv_settings()

Returns an array of currently available settings, each of type struct cf_setting. The driver should set unknown or irrelevant values to CPUFREQ_VAL_UNKNOWN. All of the following elements for each setting should be returned:

struct cf_setting {
        int     freq;   /* CPU clock in MHz or 100ths of a percent. */
        int     volts;  /* Voltage in mV. */
        int     power;  /* Power consumed in mW. */
        int     lat;    /* Transition latency in us. */
        device_t dev;   /* Driver providing this setting. */
};

On entry to this method, count contains the number of settings that can be returned. On successful completion, the driver sets it to the actual number of settings returned. If the driver offers more settings than count will allow, it should return E2BIG. ๐Ÿ’€

๐Ÿฆ‹ cpufreq_drv_type()

Indicates the type of settings it offers โ€” either CPUFREQ_TYPE_ABSOLUTE or CPUFREQ_TYPE_RELATIVE. Additionally, the driver may set the CPUFREQ_FLAG_INFO_ONLY flag if the settings it provides are information for other drivers only and cannot be passed to cpufreq_drv_set() to activate them. Look, don't touch. ๐Ÿ™ˆ

๐ŸŽ€ cpufreq_drv_set()

Takes a driver setting and makes it active. If the setting is invalid or not currently available, it should return EINVAL. Periodt.

๐Ÿ’– cpufreq_drv_get()

Returns the currently-active driver setting. The struct cf_setting returned must be valid for passing to cpufreq_drv_set(), including all elements being filled out correctly. If the driver cannot infer the current setting (even by estimating it with cpu_est_clockrate()), then it should set all elements to CPUFREQ_VAL_UNKNOWN. Honesty is the policy. โœจ


๐Ÿ‘€ SEE ALSO ๐Ÿ‘€

acpi(4), est(4), timecounters(4), powerd(8), sysctl(8)


๐Ÿซถ AUTHORS ๐Ÿซถ

Nate Lawson โ€” the originator, respect on the name. ๐Ÿ‘‘

Bruno Ducrot โ€” contributed the powernow driver. Legend behavior. ๐Ÿ”ฅ


๐Ÿ’€ BUGS ๐Ÿ’€

  • The following drivers have not yet been converted to the cpufreq interface: longrun(4). It's giving "still in the group chat but never replies." ๐Ÿ˜ญ
  • Notification of CPU and bus frequency changes is not implemented yet. Coming soon-ish. ๐Ÿ™ˆ
  • When multiple CPUs offer frequency control, they cannot be set to different levels โ€” they must all offer the same frequency settings. Everyone matches, or nobody does. ๐Ÿ’…

โœจ and that's the whole page, bestie โ€” go forth and clock those CPUs โœจ

๐Ÿ’– sign-off time: this was the CPUFREQ(4) page, source/version Debian, dated April 4, 2022 โ€” mwah, love u, bye ๐Ÿ‘‹๐Ÿ’…

๐Ÿ“ฆ where this page comes from