Set CPU governor to performance by default (FIXES #1039)

This commit is contained in:
Herbert Wolverson committed 2026-04-13 15:50:38 -05:00
1 parent 06a268f663
commit 2656aa9e65
14 files changed
+191 -32

No files matched your search

@@ -14,10 +14,14 @@ systemctl show -p WantedBy network-online.target
systemctl disable cloud-config iscsid cloud-final
```
### Set proper governor for CPU (baremetal/hypervisior host)
### CPU governor on bare metal / hypervisor hosts
LibreQoS now attempts to set the CPU governor to `performance` automatically during startup tuning.
Disable it only if needed with `[tuning].set_cpu_governor_performance = false`, or verify the active governor with:
```shell
cpupower frequency-set --governor performance
cpupower frequency-info | grep 'current policy'
```
### OSPF
+6 -2
View File
@@ -10,10 +10,14 @@
## Base de CPU e IRQ
Configure el governor de CPU en modo rendimiento para bare metal/hipervisor:
LibreQoS ahora intenta configurar automáticamente el governor de CPU en modo `performance` durante el ajuste de arranque en hosts bare metal e hipervisor.
Este comportamiento está habilitado por defecto mediante `[tuning].set_cpu_governor_performance = true`. Desactívelo solo si su plataforma requiere otra política de governor.
Si necesita verificar manualmente el governor actual:
```bash
sudo cpupower frequency-set --governor performance
cpupower frequency-info | grep 'current policy'
```
Confirme que el conteo de colas NIC y la distribución por CPU sean razonables:
+6 -2
View File
@@ -10,10 +10,14 @@
## CPU and IRQ Baseline
Set CPU frequency governor to performance on bare metal/hypervisor hosts:
LibreQoS now attempts to set the CPU frequency governor to `performance` automatically during startup tuning on bare metal and hypervisor hosts.
This behavior is enabled by default through `[tuning].set_cpu_governor_performance = true`. Disable it only if your platform requires a different governor policy.
If you need to verify the current governor manually:
```bash
sudo cpupower frequency-set --governor performance
cpupower frequency-info | grep 'current policy'
```
Confirm NIC queue count and CPU distribution are sensible for your hardware:
+1
View File
@@ -10,6 +10,7 @@ enable_site_heatmaps = true
enable_asn_heatmaps = true
[tuning]
set_cpu_governor_performance = true
stop_irq_balance = true
netdev_budget_usecs = 8000
netdev_budget_packets = 300
@@ -6,6 +6,10 @@ use thiserror::Error;
use toml_edit::{DocumentMut, value};
use tracing::{error, info};
fn default_true() -> bool {
true
}
/// Represents the top-level of the `/etc/lqos.conf` file. Serialization
/// structure.
#[derive(Serialize, Deserialize, Clone, Debug)]
@@ -55,6 +59,10 @@ pub struct EtcLqos {
/// applied (in place of the previous version's offload service)
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)]
pub struct Tunables {
/// Should LibreQoS set the CPU governor to `performance` during tuning?
#[serde(default = "default_true")]
pub set_cpu_governor_performance: bool,
/// Should the `irq_balance` system service be stopped?
pub stop_irq_balance: bool,
@@ -270,6 +278,8 @@ pub enum EtcLqosError {
#[cfg(test)]
mod test {
use crate::etc::test_data::OLD_CONFIG;
const EXAMPLE_LQOS_CONF: &str = include_str!("../../../../lqos.example");
#[test]
@@ -290,4 +300,21 @@ mod test {
let reserialized = doc.to_string();
assert!(reserialized.contains("node_id = \"test\""));
}
#[test]
fn legacy_tuning_defaults_cpu_governor_to_true_when_missing() {
let raw = OLD_CONFIG
.lines()
.filter(|line| !line.trim().starts_with("set_cpu_governor_performance"))
.collect::<Vec<_>>()
.join("\n");
let config = super::EtcLqos::load_from_string(&raw)
.expect("Unable to read legacy config fixture");
assert!(
config
.tuning
.expect("Legacy config fixture should contain tuning")
.set_cpu_governor_performance
);
}
}
+32
View File
@@ -156,6 +156,7 @@ fn migrate_top_level(old_config: &EtcLqos, new_config: &mut Config) -> Result<()
fn migrate_tunables(old_config: &EtcLqos, new_config: &mut Config) -> Result<(), MigrationError> {
if let Some(tunables) = &old_config.tuning {
new_config.tuning.set_cpu_governor_performance = tunables.set_cpu_governor_performance;
new_config.tuning.stop_irq_balance = tunables.stop_irq_balance;
new_config.tuning.netdev_budget_packets = tunables.netdev_budget_packets;
new_config.tuning.netdev_budget_usecs = tunables.netdev_budget_usecs;
@@ -335,3 +336,34 @@ fn migrate_influx(
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::etc::test_data::OLD_CONFIG;
#[test]
fn legacy_migration_defaults_cpu_governor_to_true_when_missing() {
let old_raw = OLD_CONFIG
.lines()
.filter(|line| !line.trim().starts_with("set_cpu_governor_performance"))
.collect::<Vec<_>>()
.join("\n");
let old_config = EtcLqos::load_from_string(&old_raw).expect("Legacy config should parse");
let python_config = PythonMigration {
sqm: "cake diffserv4".to_string(),
upstream_bandwidth_capacity_download_mbps: 1000,
upstream_bandwidth_capacity_upload_mbps: 1000,
generated_pndownload_mbps: 1000,
generated_pnupload_mbps: 1000,
interface_a: "veth_tointernal".to_string(),
interface_b: "veth_toexternal".to_string(),
enable_actual_shell_commands: true,
..Default::default()
};
let migrated =
do_migration_14_to_15(&old_config, &python_config).expect("Migration should succeed");
assert!(migrated.tuning.set_cpu_governor_performance);
}
}
@@ -14,6 +14,7 @@ send_anonymous = true
anonymous_server = \"127.0.0.1:9125\"
[tuning]
set_cpu_governor_performance = true
# IRQ balance breaks XDP_Redirect, which we use. Recommended to leave as true.
stop_irq_balance = false
netdev_budget_usecs = 8000
@@ -10,6 +10,7 @@ enable_site_heatmaps = true
enable_asn_heatmaps = true
[tuning]
set_cpu_governor_performance = true
stop_irq_balance = true
netdev_budget_usecs = 8000
netdev_budget_packets = 300
@@ -718,6 +718,29 @@ mod test {
assert!(!serialized.contains("enable_spylnx"));
}
#[test]
fn tuning_cpu_governor_defaults_to_true_when_missing() {
let raw = include_str!("example.toml")
.lines()
.filter(|line| !line.trim().starts_with("set_cpu_governor_performance"))
.collect::<Vec<_>>()
.join("\n");
let config = Config::load_from_string(&raw)
.expect("Config without tuning governor flag should load");
assert!(config.tuning.set_cpu_governor_performance);
}
#[test]
fn tuning_cpu_governor_explicit_false_deserializes() {
let raw = include_str!("example.toml").replace(
"set_cpu_governor_performance = true",
"set_cpu_governor_performance = false",
);
let config =
Config::load_from_string(&raw).expect("Config with tuning governor flag should load");
assert!(!config.tuning.set_cpu_governor_performance);
}
#[test]
fn rtt_thresholds_default_matches_executive_ramp() {
let d = RttThresholds::default();
@@ -3,10 +3,18 @@
use allocative::Allocative;
use serde::{Deserialize, Serialize};
fn default_true() -> bool {
true
}
/// Represents a set of `sysctl` and `ethtool` tweaks that may be
/// applied (in place of the previous version's offload service)
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Allocative)]
pub struct Tunables {
/// Should LibreQoS set the CPU governor to `performance` during tuning?
#[serde(default = "default_true")]
pub set_cpu_governor_performance: bool,
/// Should the `irq_balance` system service be stopped?
pub stop_irq_balance: bool,
@@ -36,6 +44,7 @@ pub struct Tunables {
impl Default for Tunables {
fn default() -> Self {
Self {
set_cpu_governor_performance: true,
stop_irq_balance: true,
netdev_budget_usecs: 8000,
netdev_budget_packets: 300,
@@ -32,6 +32,7 @@ function validateConfig() {
function updateConfig() {
// Update only the tuning section
window.config.tuning = {
set_cpu_governor_performance: document.getElementById("setCpuGovernorPerformance").checked,
stop_irq_balance: document.getElementById("stopIrqBalance").checked,
netdev_budget_usecs: parseInt(document.getElementById("netdevBudgetUsecs").value),
netdev_budget_packets: parseInt(document.getElementById("netdevBudgetPackets").value),
@@ -52,10 +53,12 @@ renderConfigMenu('tuning');
loadConfig(() => {
// window.config now contains the configuration.
// Populate form fields with config values
if (window.config && window.config.tuning) {
const tunables = window.config.tuning;
if (window.config) {
const tunables = window.config.tuning ?? {};
window.config.tuning = tunables;
// Boolean fields
document.getElementById("setCpuGovernorPerformance").checked = tunables.set_cpu_governor_performance ?? true;
document.getElementById("stopIrqBalance").checked = tunables.stop_irq_balance ?? false;
document.getElementById("disableRxVlan").checked = tunables.disable_rxvlan ?? false;
document.getElementById("disableTxVlan").checked = tunables.disable_txvlan ?? false;
@@ -80,6 +83,6 @@ loadConfig(() => {
}
});
} else {
console.error("Tuning configuration not found in window.config");
console.error("Configuration not found in window.config");
}
});
@@ -20,6 +20,12 @@
<h6 class="lqos-config-section-title">Interrupts and Budgets</h6>
<div class="lqos-config-section-subtitle">Control how aggressively the host services NIC interrupts and packet processing.</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="setCpuGovernorPerformance" aria-describedby="setCpuGovernorPerformanceHelp">
<label class="form-check-label" for="setCpuGovernorPerformance">Set CPU Governor to Performance</label>
<div id="setCpuGovernorPerformanceHelp" class="form-text">Runs <code>cpupower frequency-set --governor performance</code> during LibreQoS tuning. Enabled by default.</div>
</div>
<div class="mb-3 form-check">
<input type="checkbox" class="form-check-input" id="stopIrqBalance">
<label class="form-check-label" for="stopIrqBalance">Stop IRQ Balance Service</label>
+22 -21
View File
@@ -1,38 +1,39 @@
mod offloads;
use anyhow::Result;
use lqos_bus::{BusRequest, BusResponse};
use lqos_config::{Config, Tunables};
use lqos_queue_tracker::set_queue_refresh_interval;
fn apply_non_interface_tuning(tuning: &Tunables) {
offloads::bpf_sysctls();
if tuning.set_cpu_governor_performance {
offloads::set_cpu_governor_performance();
}
if tuning.stop_irq_balance {
offloads::stop_irq_balance();
}
offloads::netdev_budget(tuning.netdev_budget_usecs, tuning.netdev_budget_packets);
}
fn apply_interface_tuning(config: &Config, tuning: &Tunables) {
offloads::ethtool_tweaks(&config.internet_interface(), tuning);
offloads::ethtool_tweaks(&config.isp_interface(), tuning);
}
pub fn tune_lqosd_from_config_file() -> Result<()> {
let config = lqos_config::load_config()?;
// Disable offloading
offloads::bpf_sysctls();
if config.tuning.stop_irq_balance {
offloads::stop_irq_balance();
}
offloads::netdev_budget(
config.tuning.netdev_budget_usecs,
config.tuning.netdev_budget_packets,
);
offloads::ethtool_tweaks(&config.internet_interface(), &config.tuning);
offloads::ethtool_tweaks(&config.isp_interface(), &config.tuning);
let interval = config.queue_check_period_ms;
set_queue_refresh_interval(interval);
apply_non_interface_tuning(&config.tuning);
apply_interface_tuning(config.as_ref(), &config.tuning);
set_queue_refresh_interval(config.queue_check_period_ms);
Ok(())
}
pub fn tune_lqosd_from_bus(request: &BusRequest) -> BusResponse {
match request {
BusRequest::UpdateLqosDTuning(interval, tuning) => {
// Real-time tuning changes. Probably dangerous.
apply_non_interface_tuning(tuning);
if let Ok(config) = lqos_config::load_config() {
if tuning.stop_irq_balance {
offloads::stop_irq_balance();
}
offloads::netdev_budget(tuning.netdev_budget_usecs, tuning.netdev_budget_packets);
offloads::ethtool_tweaks(&config.internet_interface(), &config.tuning);
offloads::ethtool_tweaks(&config.isp_interface(), &config.tuning);
apply_interface_tuning(config.as_ref(), tuning);
}
set_queue_refresh_interval(*interval);
lqos_bus::BusResponse::Ack
+44 -1
View File
@@ -1,5 +1,8 @@
//! Host tuning commands for sysctls, NIC offloads, and CPU governor policy.
use lqos_config::Tunables;
use std::process::Command;
use std::{io::ErrorKind, process::Command};
use tracing::warn;
pub fn bpf_sysctls() {
let _ = Command::new("/sbin/sysctl")
@@ -7,6 +10,46 @@ pub fn bpf_sysctls() {
.output();
}
pub fn set_cpu_governor_performance() {
for cpupower in ["/usr/bin/cpupower", "/usr/sbin/cpupower", "cpupower"] {
match Command::new(cpupower)
.args(["frequency-set", "--governor", "performance"])
.output()
{
Ok(output) if output.status.success() => return,
Ok(output) => {
let stderr = String::from_utf8_lossy(&output.stderr).trim().to_string();
if stderr.is_empty() {
warn!(
command = cpupower,
status = %output.status,
"Failed to set CPU governor to performance"
);
} else {
warn!(
command = cpupower,
status = %output.status,
stderr = %stderr,
"Failed to set CPU governor to performance"
);
}
return;
}
Err(error) if error.kind() == ErrorKind::NotFound => continue,
Err(error) => {
warn!(
command = cpupower,
error = %error,
"Failed to run cpupower to set CPU governor to performance"
);
return;
}
}
}
warn!("Unable to set CPU governor to performance because cpupower was not found");
}
pub fn stop_irq_balance() {
let _ = Command::new("/bin/systemctl")
.args(["stop", "irqbalance"])