From 78665ede70681d318923baa5f5d3cb7403eb478b Mon Sep 17 00:00:00 2001 From: vasilito Date: Fri, 24 Jul 2026 05:38:16 +0900 Subject: [PATCH] =?UTF-8?q?driver-manager:=20QEMU=20gate=20passed=20?= =?UTF-8?q?=E2=80=94=20thread::scope=20hang=20fix,=20initfs=20scheme=20ski?= =?UTF-8?q?p,=20graphics=20split?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime validation of the cutover in QEMU (q35, e1000 + AHCI): - thread::scope's park/unpark path hangs on Redox (scoped worker completed all work but the scope join never returned — the boot stalled inside every concurrent enumeration). The concurrent probe pool now uses plain thread::spawn + JoinHandle::join (all captures were already owned/Arc); the counting semaphore is Arc-based so guards are 'static. bound map is Mutex (RwLock write was unproven on target). - initfs driver-manager no longer registers scheme:driver-manager — the transient initfs manager's registration survived into the rootfs phase and made the resident manager's registration fail EEXIST (which then exit(1)'d the rootfs manager). - config: matchless [[driver]] entries now parse (serde default) — 70-usb-class.toml's USB-class drivers (no [[driver.match]]) broke config loading entirely on the first gate. Includes a regression test for load_all with matchless entries. - graphics: 30-graphics.toml moves from the shared redbear-device-services.toml to redbear-full.toml (redox-drm is a full-only driver; mini no longer defers it every hotplug cycle). vesad removed from drivers.d — it is an init-managed service, not a spawnable driver. Gate evidence: initfs 'bound: 0000--00--1f.2 -> ahcid', switchroot, rootfs 'bound: 0000--00--02.0 -> e1000d' with ZERO deferred, scheme:driver-manager registered, resident hotplug loop (250ms), pcid-spawner dormant on both phases. --- config/redbear-device-services.toml | 21 ------- config/redbear-full.toml | 45 ++++++++++++++ local/config/drivers.d/30-graphics.toml | 11 +--- .../source/src/concurrent.rs | 59 ++++++++++--------- .../driver-manager/source/src/config.rs | 35 ++++++++++- .../system/driver-manager/source/src/main.rs | 4 +- 6 files changed, 116 insertions(+), 59 deletions(-) diff --git a/config/redbear-device-services.toml b/config/redbear-device-services.toml index 92628382b5..8cb695e400 100644 --- a/config/redbear-device-services.toml +++ b/config/redbear-device-services.toml @@ -479,27 +479,6 @@ data = """ pattern = "i915/adlp_dmc_ver2_16.bin" chain = ["i915/adlp_dmc_ver2_14.bin", "i915/adlp_dmc_ver2_12.bin"] """ -[[files]] -path = "/lib/drivers.d/30-graphics.toml" -data = """ -[[driver]] -name = "vesad" -description = "VESA BIOS display driver" -priority = 60 -command = ["/usr/lib/drivers/vesad"] - -[[driver.match]] -class = 0x03 - -[[driver]] -name = "redox-drm" -description = "DRM/KMS display driver (AMD + Intel)" -priority = 60 -command = ["/usr/bin/redox-drm"] - -[[driver.match]] -class = 0x03 -""" # Firmware fallback chain configs [[files]] diff --git a/config/redbear-full.toml b/config/redbear-full.toml index f4216f7516..c2b46f524b 100644 --- a/config/redbear-full.toml +++ b/config/redbear-full.toml @@ -587,6 +587,51 @@ members = ["greeter"] gid = 100 members = ["messagebus"] +[[files]] +path = "/lib/drivers.d/30-graphics.toml" +data = """ +[[driver]] +name = "redox-drm" +description = "DRM/KMS display driver (AMD + Intel + VirtIO)" +priority = 60 +command = ["/usr/bin/redox-drm"] + +[[driver.match]] +class = 0x03 + +[[driver]] +name = "virtio-gpud" +description = "VirtIO GPU driver" +priority = 60 +command = ["/usr/lib/drivers/virtio-gpud"] + +[[driver.match]] +vendor = 0x1AF4 +class = 0x03 + +[[driver]] +name = "redox-drm" +description = "Intel GPU display driver" +priority = 61 +command = ["/usr/bin/redox-drm"] + +[[driver.match]] +vendor = 0x8086 +class = 0x03 +subclass = 0x00 + +[[driver]] +name = "redox-drm" +description = "AMD GPU display driver" +priority = 61 +command = ["/usr/bin/redox-drm"] + +[[driver.match]] +vendor = 0x1002 +class = 0x03 +subclass = 0x00 +""" + [[files]] path = "/etc/pcid.d/ihdgd.toml" data = """ diff --git a/local/config/drivers.d/30-graphics.toml b/local/config/drivers.d/30-graphics.toml index 37356f2da9..d0e8c43d23 100644 --- a/local/config/drivers.d/30-graphics.toml +++ b/local/config/drivers.d/30-graphics.toml @@ -1,13 +1,6 @@ # Graphics and display drivers - -[[driver]] -name = "vesad" -description = "VESA BIOS display driver" -priority = 60 -command = ["/usr/lib/drivers/vesad"] - -[[driver.match]] -class = 0x03 +# vesad runs as an init-managed service from initfs (framebuffer console) and +# has no rootfs binary, so it is not spawnable here and must not be listed. [[driver]] name = "redox-drm" diff --git a/local/recipes/drivers/redox-driver-core/source/src/concurrent.rs b/local/recipes/drivers/redox-driver-core/source/src/concurrent.rs index dbfec0d9b5..4edbf89ebc 100644 --- a/local/recipes/drivers/redox-driver-core/source/src/concurrent.rs +++ b/local/recipes/drivers/redox-driver-core/source/src/concurrent.rs @@ -5,7 +5,7 @@ extern crate alloc; -use std::sync::{mpsc, Arc, Condvar, Mutex, RwLock}; +use std::sync::{mpsc, Arc, Condvar, Mutex}; use std::thread; use crate::device::{BoundDevice, DeviceId, DeviceInfo}; @@ -31,21 +31,21 @@ impl CountingSemaphore { } } - fn acquire(&self) -> SemaphoreGuard<'_> { + fn acquire(self: &Arc) -> SemaphoreGuard { let mut count = self.inner.lock().unwrap_or_else(|e| e.into_inner()); while *count == 0 { count = self.cvar.wait(count).unwrap_or_else(|e| e.into_inner()); } *count -= 1; - SemaphoreGuard { sem: self } + SemaphoreGuard { sem: self.clone() } } } -struct SemaphoreGuard<'a> { - sem: &'a CountingSemaphore, +struct SemaphoreGuard { + sem: Arc, } -impl<'a> Drop for SemaphoreGuard<'a> { +impl Drop for SemaphoreGuard { fn drop(&mut self) { if let Ok(mut count) = self.sem.inner.lock() { *count += 1; @@ -64,7 +64,7 @@ pub struct ConcurrentDeviceManager { /// Pre-events emitted in order before any probe result. pre_events: Vec, /// Bound-device map shared with workers. - bound: Arc>>, + bound: Arc>>, /// Deferred queue shared with workers. deferred: Arc>>, } @@ -132,7 +132,7 @@ impl ConcurrentDeviceManager { Self { jobs, pre_events, - bound: Arc::new(RwLock::new(mgr.bound_devices_snapshot())), + bound: Arc::new(Mutex::new(mgr.bound_devices_snapshot())), deferred: Arc::new(Mutex::new(mgr.deferred_queue_snapshot())), } } @@ -144,7 +144,7 @@ impl ConcurrentDeviceManager { /// Snapshot of the bound map (post-probe). pub fn bound_snapshot(&self) -> BTreeMap { - self.bound.read().map(|m| m.clone()).unwrap_or_default() + self.bound.lock().map(|m| m.clone()).unwrap_or_default() } /// Snapshot of the deferred queue. @@ -177,21 +177,26 @@ impl ConcurrentDeviceManager { .map(|_| semaphore.acquire()) .collect(); - thread::scope(|s| { - for ((device_id, info, candidates), permit) in - self.jobs.iter().cloned().zip(permits.drain(..)) - { - let tx = tx.clone(); - let bound = Arc::clone(&bound); - let deferred = Arc::clone(&deferred); - s.spawn(move || { - let _permit = permit; - for outcome in run_probe(bound, deferred, device_id, info, candidates) { - let _ = tx.send(outcome); - } - }); - } - }); + // Plain spawn + join: every capture is owned (Arc/moved), so + // scoped threads are unnecessary, and JoinHandle::join does not + // depend on the park/unpark path that thread::scope relies on. + let mut handles = Vec::new(); + for ((device_id, info, candidates), permit) in + self.jobs.iter().cloned().zip(permits.drain(..)) + { + let tx = tx.clone(); + let bound = Arc::clone(&bound); + let deferred = Arc::clone(&deferred); + handles.push(thread::spawn(move || { + let _permit = permit; + for outcome in run_probe(bound, deferred, device_id, info, candidates) { + let _ = tx.send(outcome); + } + })); + } + for handle in handles { + let _ = handle.join(); + } drop(tx); while let Ok(event) = rx.try_recv() { @@ -202,7 +207,7 @@ impl ConcurrentDeviceManager { } fn run_probe( - bound: Arc>>, + bound: Arc>>, deferred: Arc>>, device_id: DeviceId, info: DeviceInfo, @@ -214,7 +219,7 @@ fn run_probe( let result = driver.probe(&info); match &result { ProbeResult::Bound => { - if let Ok(mut map) = bound.write() { + if let Ok(mut map) = bound.lock() { map.insert( device_id.clone(), BoundDevice { @@ -561,7 +566,7 @@ mod tests { #[test] fn semaphore_releases_on_drop() { - let sem = CountingSemaphore::new(1); + let sem = Arc::new(CountingSemaphore::new(1)); { let _p = sem.acquire(); } diff --git a/local/recipes/system/driver-manager/source/src/config.rs b/local/recipes/system/driver-manager/source/src/config.rs index 178500136f..f9e9d77dfb 100644 --- a/local/recipes/system/driver-manager/source/src/config.rs +++ b/local/recipes/system/driver-manager/source/src/config.rs @@ -70,7 +70,7 @@ struct RawDriverMatch { #[cfg(test)] mod tests { - use super::{is_process_alive, signal_then_collect}; + use super::{is_process_alive, signal_then_collect, DriverConfig}; use std::collections::BTreeMap; use std::sync::Mutex; @@ -81,6 +81,38 @@ mod tests { body() } + #[test] + fn load_all_accepts_matchless_driver_entries() { + serialized(|| { + let dir = std::env::temp_dir().join(format!( + "rb-dm-matchless-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + std::fs::create_dir_all(&dir).unwrap(); + std::fs::write( + dir.join("70-usb-class.toml"), + r#" +[[driver]] +name = "redbear-acmd" +description = "USB CDC ACM serial driver" +priority = 70 +command = ["/usr/bin/redbear-acmd"] +"#, + ) + .unwrap(); + let configs = DriverConfig::load_all(dir.to_str().unwrap()) + .expect("matchless driver entries must load"); + assert_eq!(configs.len(), 1); + assert_eq!(configs[0].name, "redbear-acmd"); + assert!(configs[0].matches.is_empty()); + let _ = std::fs::remove_dir_all(&dir); + }); + } + #[test] fn is_process_alive_returns_false_for_unused_pid() { let dead = u32::MAX; @@ -885,6 +917,7 @@ struct RawDriverEntry { #[serde(default)] command: Vec, #[serde(rename = "match")] + #[serde(default)] r#match: Vec, #[serde(default)] depends_on: Vec, diff --git a/local/recipes/system/driver-manager/source/src/main.rs b/local/recipes/system/driver-manager/source/src/main.rs index c1d4aa0d86..7da0d60d41 100644 --- a/local/recipes/system/driver-manager/source/src/main.rs +++ b/local/recipes/system/driver-manager/source/src/main.rs @@ -452,7 +452,9 @@ fn main() { log::info!("enum complete: {} bound, {} deferred", bound, deferred); } - if let Err(err) = scheme::start_scheme_server(Arc::clone(&scheme)) { + if initfs { + log::info!("initfs mode: skipping scheme registration (transient)"); + } else if let Err(err) = scheme::start_scheme_server(Arc::clone(&scheme)) { log::error!("{err}"); process::exit(1); }