feat: automatically disable conflicting power-profiles-daemon actions (#615)

* feat: automatically disable conflicting power-profiles-daemon actions

* chore: update README ppd information

* fix: random intel xe tile ordering
This commit is contained in:
Ilya Zlobintsev
2025-06-06 21:31:33 +03:00
committed by GitHub
parent 74d74e6dca
commit 3f87c6d642
6 changed files with 131 additions and 7 deletions
+5 -2
View File
@@ -127,10 +127,13 @@ more information.
## Power profiles daemon note!
If you are using `power-profiles-daemon` (which is installed by default on many
distributions), by default it will override the amdgpu performance level setting
distributions), by default it may override the amdgpu performance level setting
according to its own profile.
To avoid this, create a file at
When using LACT 0.7.5+ and power-profiles-daemon 0.30+, LACT will try to connect to power-profiles-daemon
and automatically disable the conflicting amdgpu action in ppd to avoid this conflict.
If running older versions, you can resolve this manually by creating a file at
`/etc/systemd/system/power-profiles-daemon.service.d/override.conf` with the
following contents:
+2 -2
View File
@@ -6,13 +6,13 @@ mod config;
mod server;
mod socket;
mod suspend;
mod system;
#[cfg(test)]
mod tests;
use anyhow::Context;
use config::Config;
use futures::future::select_all;
use server::system;
use server::{handle_stream, handler::Handler, Server};
use std::sync::Arc;
use std::{os::unix::net::UnixStream as StdUnixStream, time::Duration};
@@ -31,7 +31,7 @@ use tracing_subscriber::EnvFilter;
/// RDNA3, minimum family that supports the new pmfw interface
pub const AMDGPU_FAMILY_GC_11_0_0: u32 = 145;
pub use server::system::MODULE_CONF_PATH;
pub use system::MODULE_CONF_PATH;
const MIN_SYSTEM_UPTIME_SECS: f32 = 15.0;
const DRM_EVENT_TIMEOUT_PERIOD_MS: u64 = 100;
+3 -2
View File
@@ -2,11 +2,10 @@ pub mod gpu_controller;
pub mod handler;
mod opencl;
mod profiles;
pub(crate) mod system;
mod vulkan;
use self::handler::Handler;
use crate::{config::Config, socket};
use crate::{config::Config, socket, system};
use anyhow::Context;
use futures::future::join_all;
use lact_schema::{Pong, Request, Response};
@@ -39,6 +38,8 @@ impl Server {
None
};
system::power_profiles_daemon::setup().await;
let handler = Handler::new(config).await?;
socket::set_permissions(&socket_path, &handler.config.read().await.daemon)?;
@@ -89,6 +89,7 @@ impl IntelGpuController {
tile_gts.len(),
common.sysfs_path.display()
);
tile_gts.sort();
}
let drm_file = if cfg!(not(test)) {
let drm_path = format!("/dev/dri/by-path/pci-{}-render", common.pci_slot_name);
@@ -1,3 +1,5 @@
pub mod power_profiles_daemon;
use anyhow::{anyhow, ensure, Context};
use lact_schema::{InitramfsType, SystemInfo, GIT_COMMIT};
use nix::sys::socket::{
@@ -249,7 +251,7 @@ pub(crate) fn listen_netlink_kernel_event(notify: &Notify) -> anyhow::Result<()>
#[cfg(test)]
mod tests {
use crate::server::system::detect_initramfs_type;
use super::detect_initramfs_type;
use lact_schema::InitramfsType;
use os_release::OsRelease;
@@ -0,0 +1,117 @@
use anyhow::{anyhow, Context};
use tracing::{debug, error, info, warn};
use zbus::{proxy, Connection};
const CONFLICTING_ACTIONS: [&str; 1] = ["amdgpu_dpm"];
const MIN_PPD_MINOR_VERSION: u32 = 30;
pub async fn setup() {
let conn = match Connection::system().await {
Ok(conn) => conn,
Err(err) => {
warn!("could not establish DBus connection: {err}");
return;
}
};
match PowerProfilesDaemonProxy::new(&conn).await {
Ok(ppd_client) => {
if let Err(err) = disable_conflicting_actions(&ppd_client).await {
error!("power-profiles-daemon detected, but conflicting actions could not be disabled: {err:#}");
}
}
Err(err) => {
debug!("could not connect to power-profiles-daemon: {err}");
}
}
}
async fn disable_conflicting_actions(client: &PowerProfilesDaemonProxy<'_>) -> anyhow::Result<()> {
let version = client.version().await?;
debug!("connected to power-profiles-daemon {version}");
let (_major, minor) = version
.split_once('.')
.with_context(|| format!("Could not parse version string '{version}'"))?;
let minor = minor
.parse::<u32>()
.context("Could not parse minor version")?;
if minor < MIN_PPD_MINOR_VERSION {
return Err(anyhow!(
"daemon version {version} is older than minimum required for actions configuration"
));
}
let current_actions = client
.actions_info()
.await
.context("Could not list actions")?;
for action_map in current_actions {
if let Some(name) = action_map
.get("Name")
.and_then(|value| value.downcast_ref::<String>().ok())
{
if CONFLICTING_ACTIONS.contains(&name.as_str()) {
match action_map
.get("Enabled")
.and_then(|enabled| enabled.downcast_ref::<bool>().ok())
{
Some(enabled) => {
if enabled {
match client.set_action_enabled(&name, false).await {
Ok(()) => {
info!(
"disabled conflicting power-profiles-daemon action {name}"
);
}
Err(err) => {
error!("could not disable conflicting power-profiles-daemon action {name}: {err}");
}
}
} else {
info!("conflicting power-profiles-daemon action {name} is already disabled");
}
}
None => {
error!("could not check status for power-profiles-daemon action {name}: {action_map:?}");
}
}
}
}
}
Ok(())
}
#[proxy(
interface = "org.freedesktop.UPower.PowerProfiles",
default_service = "org.freedesktop.UPower.PowerProfiles",
default_path = "/org/freedesktop/UPower/PowerProfiles"
)]
trait PowerProfilesDaemon {
/// SetActionEnabled method
fn set_action_enabled(&self, action: &str, enabled: bool) -> zbus::Result<()>;
/// Actions property
#[zbus(property)]
fn actions(&self) -> zbus::Result<Vec<String>>;
/// ActionsInfo property
#[zbus(property)]
fn actions_info(
&self,
) -> zbus::Result<Vec<std::collections::HashMap<String, zbus::zvariant::OwnedValue>>>;
/// ActiveProfile property
#[zbus(property)]
fn active_profile(&self) -> zbus::Result<String>;
#[zbus(property)]
fn set_active_profile(&self, value: &str) -> zbus::Result<()>;
/// Version property
#[zbus(property)]
fn version(&self) -> zbus::Result<String>;
}