From a6e971bb7703e4c3325d7dde379f780d7622a57c Mon Sep 17 00:00:00 2001 From: ido <106871003+idobutidont@users.noreply.github.com> Date: Thu, 25 Jun 2026 00:08:47 +0700 Subject: [PATCH 1/4] Fix: Avoid unnecessary PCI bus scans and reduce GPU detection log noise --- asusd/src/ctrl_gpu.rs | 53 +++++++++++++++++-------------------- rog-platform/src/gpu_pci.rs | 16 +++++------ 2 files changed, 33 insertions(+), 36 deletions(-) diff --git a/asusd/src/ctrl_gpu.rs b/asusd/src/ctrl_gpu.rs index a58849bf..615da761 100644 --- a/asusd/src/ctrl_gpu.rs +++ b/asusd/src/ctrl_gpu.rs @@ -49,6 +49,14 @@ impl CtrlGpu { fn detect_gpu() -> (GfxPower, GfxVendor, Option) { use rog_platform::gpu_pci::Device; + // Check ASUS dgpu_disable first. If the dGPU is disabled, it won't appear on the PCI bus. + if rog_platform::gpu_pci::asus_dgpu_disable_exists() { + if let Ok(true) = rog_platform::gpu_pci::asus_dgpu_disabled() { + return (GfxPower::AsusDisabled, GfxVendor::AsusDgpuDisabled, None); + } + } + + // dGPU is not disabled, scan PCI bus to find it let devices = Device::find().unwrap_or_default(); if let Some(dgpu) = devices.iter().find(|d| d.is_dgpu()) { @@ -69,14 +77,7 @@ impl CtrlGpu { return (GfxPower::Unknown, vendor, runtime_path); } - // No dGPU devices — check ASUS-specific attributes - if rog_platform::gpu_pci::asus_dgpu_disable_exists() { - if let Ok(disabled) = rog_platform::gpu_pci::asus_dgpu_disabled() { - if disabled { - return (GfxPower::AsusDisabled, GfxVendor::AsusDgpuDisabled, None); - } - } - } + // No dGPU found, check if we're in MUX discreet mode if rog_platform::gpu_pci::asus_gpu_mux_exists() { if let Ok(discreet) = rog_platform::gpu_pci::asus_gpu_mux_discreet() { if discreet { @@ -225,26 +226,22 @@ impl CtrlGpu { warn!("CtrlGpu: runtime_status deleted, re-detecting"); break; } - // Read new power status - if let Some(dgpu) = rog_platform::gpu_pci::Device::find() - .unwrap_or_default() - .iter() - .find(|d| d.is_dgpu()) - { - if let Ok(new_power) = dgpu.get_runtime_status() { - if new_power != ctrl.power_status { - info!("CtrlGpu: power status changed to {new_power:?}"); - ctrl.power_status = new_power; - let status_str: &str = (&ctrl.power_status).into(); - let _ = signal_ctxt - .emit( - "xyz.ljones.Gpu", - "PowerStatusChanged", - &(status_str,), - ) - .await; - } - } + // Read new power status directly from sysfs + let new_power = std::fs::read_to_string(&runtime_path) + .ok() + .and_then(|s| s.parse::().ok()) + .unwrap_or(GfxPower::Unknown); + if new_power != ctrl.power_status { + info!("CtrlGpu: power status changed to {new_power:?}"); + ctrl.power_status = new_power; + let status_str: &str = (&ctrl.power_status).into(); + let _ = signal_ctxt + .emit( + "xyz.ljones.Gpu", + "PowerStatusChanged", + &(status_str,), + ) + .await; } } Err(e) => { diff --git a/rog-platform/src/gpu_pci.rs b/rog-platform/src/gpu_pci.rs index d02968f5..06010b18 100644 --- a/rog-platform/src/gpu_pci.rs +++ b/rog-platform/src/gpu_pci.rs @@ -11,7 +11,7 @@ use std::path::{Path, PathBuf}; use std::process::Command; use std::str::FromStr; -use log::{debug, info, trace, warn}; +use log::{info, trace, warn}; use serde::{Deserialize, Serialize}; use zbus::zvariant::{OwnedValue, Type, Value}; @@ -240,7 +240,7 @@ impl Device { PlatformError::Udev("scan_devices failed".into(), err) })? { let sysname = device.sysname().to_string_lossy(); - debug!("Looking at PCI device {:?}", sysname); + trace!("Looking at PCI device {:?}", sysname); if let Some(id) = device.property_value("PCI_ID") { if let Some(class) = device.property_value("PCI_CLASS") { let id = id.to_string_lossy(); @@ -254,20 +254,20 @@ impl Device { let displays = find_connected_displays(device.syspath()).unwrap_or_default(); if !displays.contains(&"eDP-1".to_string()) { - info!( + trace!( "Matched dGPU {id} at {:?} by checking display connections", device.sysname() ); dgpu = class.starts_with("30") && (id.starts_with("10DE") || id.starts_with("1002")); } else { - info!( + trace!( "Device {id} at {:?} appears to be the iGPU", device.sysname() ); } if !dgpu && id.starts_with("1002") { - debug!( + trace!( "Found dGPU Device {id} without boot_vga attribute at {:?}", device.sysname() ); @@ -278,7 +278,7 @@ impl Device { let hwmon_n_opt = match dev_path.read_dir() { Ok(mut entries) => entries.next(), Err(e) => { - debug!("Error reading hwmon directory: {}", e); + trace!("Error reading hwmon directory: {}", e); None } }; @@ -292,13 +292,13 @@ impl Device { if !dgpu { if let Some(label) = device.property_value("ID_MODEL_FROM_DATABASE") { - debug!( + trace!( "Found ID_MODEL_FROM_DATABASE property {id} at {:?} : {label:?}", device.sysname() ); dgpu = lscpi_dgpu_check(&label.to_string_lossy()); } else { - debug!( + trace!( "Didn't find dGPU with standard methods, using last resort for id:{id} at {:?}", device.sysname() ); From d512c7cb4308f65243e59f8d7ed228f873a05198 Mon Sep 17 00:00:00 2001 From: ido <106871003+idobutidont@users.noreply.github.com> Date: Thu, 25 Jun 2026 02:44:51 +0700 Subject: [PATCH 2/4] Fix: prefer inotify for dGPU power status detection when available --- asusd/src/ctrl_gpu.rs | 56 ++++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/asusd/src/ctrl_gpu.rs b/asusd/src/ctrl_gpu.rs index 615da761..1c1e72bb 100644 --- a/asusd/src/ctrl_gpu.rs +++ b/asusd/src/ctrl_gpu.rs @@ -168,24 +168,23 @@ impl CtrlGpu { /// is available (e.g. dGPU disabled via ASUS attribute), we fall back to /// periodic re-detection every 3 seconds to handle hotplug scenarios. pub async fn start_watcher(&self, signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> { - let ctrl_inotify = self.clone(); - let ctrl_fallback = self.clone(); - let path = self.dgpu_runtime_status_path.clone(); + let ctrl = self.clone(); tokio::spawn(async move { - if let Some(runtime_path) = path { - // inotify-based monitoring of the runtime_status sysfs file - info!("CtrlGpu: starting inotify watcher on {:?}", runtime_path); - let mut ctrl = ctrl_inotify; - let mut buffer = [0u8; 32]; + let mut ctrl = ctrl; + + loop { + if let Some(runtime_path) = ctrl.dgpu_runtime_status_path.clone() { + // inotify-based monitoring of the runtime_status sysfs file + info!("CtrlGpu: starting inotify watcher on {:?}", runtime_path); + let mut buffer = [0u8; 32]; - loop { let inotify = match inotify::Inotify::init() { Ok(i) => i, Err(e) => { error!("CtrlGpu: failed to init inotify: {e}"); - // Fall back to polling - break; + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + continue; } }; @@ -204,7 +203,6 @@ impl CtrlGpu { .emit("xyz.ljones.Gpu", "PowerStatusChanged", &(status_str,)) .await; } - // Wait a bit before retrying tokio::time::sleep(std::time::Duration::from_secs(3)).await; continue; } @@ -250,20 +248,30 @@ impl CtrlGpu { } } } + + if ctrl.redetect() { + let status_str: &str = (&ctrl.power_status).into(); + let _ = signal_ctxt + .emit("xyz.ljones.Gpu", "PowerStatusChanged", &(status_str,)) + .await; + } + continue; } - } - // Fallback: periodic re-detection (for when no inotify path is available, - // or when inotify loop exited) - info!("CtrlGpu: starting periodic re-detection (every 3s)"); - let mut ctrl = ctrl_fallback; - loop { - tokio::time::sleep(std::time::Duration::from_secs(3)).await; - if ctrl.redetect() { - let status_str: &str = (&ctrl.power_status).into(); - let _ = signal_ctxt - .emit("xyz.ljones.Gpu", "PowerStatusChanged", &(status_str,)) - .await; + // Fallback: periodic re-detection (for when no inotify path is available) + info!("CtrlGpu: starting periodic re-detection (every 3s)"); + loop { + tokio::time::sleep(std::time::Duration::from_secs(3)).await; + if ctrl.redetect() { + let status_str: &str = (&ctrl.power_status).into(); + let _ = signal_ctxt + .emit("xyz.ljones.Gpu", "PowerStatusChanged", &(status_str,)) + .await; + } + if ctrl.dgpu_runtime_status_path.is_some() { + info!("CtrlGpu: dGPU runtime_status restored, switching back to inotify"); + break; + } } } }); From c48eef91c6c38bb1fb15e6a28d9f66fdd2f55da3 Mon Sep 17 00:00:00 2001 From: ido <106871003+idobutidont@users.noreply.github.com> Date: Thu, 25 Jun 2026 04:36:12 +0700 Subject: [PATCH 3/4] Feat: replace periodic GPU re-detection with udev PCI hotplug monitoring --- asusd/src/ctrl_gpu.rs | 51 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/asusd/src/ctrl_gpu.rs b/asusd/src/ctrl_gpu.rs index 1c1e72bb..fe479a56 100644 --- a/asusd/src/ctrl_gpu.rs +++ b/asusd/src/ctrl_gpu.rs @@ -165,8 +165,8 @@ impl CtrlGpu { /// Spawn the inotify watcher for GPU power status changes. /// /// This watches the dGPU's `runtime_status` sysfs file. If no dGPU path - /// is available (e.g. dGPU disabled via ASUS attribute), we fall back to - /// periodic re-detection every 3 seconds to handle hotplug scenarios. + /// is available (e.g. dGPU disabled via ASUS attribute), the watcher will + /// wait for a PCI hotplug event via udev to re-detect the GPU. pub async fn start_watcher(&self, signal_ctxt: SignalEmitter<'static>) -> Result<(), RogError> { let ctrl = self.clone(); @@ -258,20 +258,51 @@ impl CtrlGpu { continue; } - // Fallback: periodic re-detection (for when no inotify path is available) - info!("CtrlGpu: starting periodic re-detection (every 3s)"); - loop { - tokio::time::sleep(std::time::Duration::from_secs(3)).await; + // No dGPU path available, wait for PCI hotplug event via udev + info!("CtrlGpu: waiting for PCI hotplug event via udev..."); + let hotplugged = tokio::task::spawn_blocking(move || { + let monitor = match udev::MonitorBuilder::new() { + Ok(builder) => match builder.match_subsystem("pci") { + Ok(builder) => match builder.listen() { + Ok(m) => m, + Err(e) => { + error!("CtrlGpu: failed to listen to udev: {e}"); + return false; + } + }, + Err(e) => { + error!("CtrlGpu: failed to match subsystem: {e}"); + return false; + } + }, + Err(e) => { + error!("CtrlGpu: failed to create MonitorBuilder: {e}"); + return false; + } + }; + + for event in monitor.iter() { + if let Some(action) = event.action() { + if action.to_str() == Some("add") { + info!("CtrlGpu: PCI device added"); + return true; + } + } + } + false + }) + .await + .unwrap_or(false); + + if hotplugged { if ctrl.redetect() { let status_str: &str = (&ctrl.power_status).into(); let _ = signal_ctxt .emit("xyz.ljones.Gpu", "PowerStatusChanged", &(status_str,)) .await; } - if ctrl.dgpu_runtime_status_path.is_some() { - info!("CtrlGpu: dGPU runtime_status restored, switching back to inotify"); - break; - } + } else { + tokio::time::sleep(std::time::Duration::from_secs(5)).await; } } }); From 9d6565fd03c62f74141af39958d6d5cae6b9c6df Mon Sep 17 00:00:00 2001 From: ido <106871003+idobutidont@users.noreply.github.com> Date: Thu, 25 Jun 2026 14:58:52 +0700 Subject: [PATCH 4/4] Fix: replace udev monitor loop with mio polling --- asusd/src/ctrl_gpu.rs | 45 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/asusd/src/ctrl_gpu.rs b/asusd/src/ctrl_gpu.rs index fe479a56..b5e37568 100644 --- a/asusd/src/ctrl_gpu.rs +++ b/asusd/src/ctrl_gpu.rs @@ -4,6 +4,7 @@ //! and exposes it over D-Bus for the tray icon and other clients. use log::{error, info, warn}; +use mio::{Events, Interest, Poll, Token}; use rog_platform::gpu_pci::{GfxPower, GfxVendor}; use zbus::object_server::SignalEmitter; use zbus::{interface, Connection}; @@ -260,8 +261,8 @@ impl CtrlGpu { // No dGPU path available, wait for PCI hotplug event via udev info!("CtrlGpu: waiting for PCI hotplug event via udev..."); - let hotplugged = tokio::task::spawn_blocking(move || { - let monitor = match udev::MonitorBuilder::new() { + let hotplugged = tokio::task::spawn_blocking(|| { + let mut monitor = match udev::MonitorBuilder::new() { Ok(builder) => match builder.match_subsystem("pci") { Ok(builder) => match builder.listen() { Ok(m) => m, @@ -281,15 +282,40 @@ impl CtrlGpu { } }; - for event in monitor.iter() { - if let Some(action) = event.action() { - if action.to_str() == Some("add") { - info!("CtrlGpu: PCI device added"); - return true; + // Block until the kernel signals that a udev event is available + let mut poll = match Poll::new() { + Ok(p) => p, + Err(e) => { + error!("CtrlGpu: failed to create mio::Poll: {e}"); + return false; + } + }; + let mut events = Events::with_capacity(1); + const UDEV: Token = Token(0); + + if let Err(e) = poll.registry().register( + &mut monitor, + UDEV, + Interest::READABLE, + ) { + error!("CtrlGpu: failed to register udev monitor with mio: {e}"); + return false; + } + + loop { + if let Err(e) = poll.poll(&mut events, None) { + error!("CtrlGpu: mio poll failed: {e}"); + return false; + } + for event in monitor.iter() { + if let Some(action) = event.action() { + if action.to_str() == Some("add") { + info!("CtrlGpu: PCI device added via hotplug"); + return true; + } } } } - false }) .await .unwrap_or(false); @@ -302,7 +328,8 @@ impl CtrlGpu { .await; } } else { - tokio::time::sleep(std::time::Duration::from_secs(5)).await; + // udev monitor setup failed, back off before retrying + tokio::time::sleep(std::time::Duration::from_secs(30)).await; } } });