b9de373b31
Restore all bootprocess branch files that were overwritten by later 0.2.0 commits. This overlay brings back the complete boot infrastructure: - Configs: redbear-full, redbear-mini, redbear-device-services, driver .d files - Kernel: IRQ affinity, x2APIC, C-states, NUMA (SLIT/SRAT), MCS locks, cpuidle - Base patches: P0-P55 + new P6 (lived block_size=512) + P57 (fbbootlogd graceful init) - Driver infra: driver-manager, udev-shim, thermald, cpufreqd, iommu, redox-driver-sys/core - GPU: redox-drm with improved connector handling - System: redbear-info, redbear-hwutils phase-timer-check - Build system: fetch.rs improvements, build-iso.sh, run_full.sh - Kernel source: new ACPI (SLIT, SRAT), cpuidle, cstate, MCS lock modules 83 files changed, +3966/-1248 lines
216 lines
8.4 KiB
Rust
216 lines
8.4 KiB
Rust
use std::collections::{BTreeMap, BTreeSet};
|
|
use std::sync::{Arc, Mutex};
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
use redox_driver_core::device::DeviceId;
|
|
use redox_driver_core::driver::ProbeResult;
|
|
use redox_driver_core::manager::DeviceManager;
|
|
use redox_driver_core::manager::ProbeEvent;
|
|
|
|
use crate::scheme::{DriverManagerScheme, notify_bind, notify_unbind};
|
|
|
|
/// Maximum times a single deferred device+driver pair is retried before
|
|
/// permanently abandoning it. Deferred means a dependency scheme (e.g.
|
|
/// scheme:firmware) is not yet ready, so the device may bind later.
|
|
const MAX_DEFERRED_RETRIES: u32 = 5;
|
|
|
|
pub fn run_hotplug_loop(
|
|
manager: Arc<Mutex<DeviceManager>>,
|
|
scheme: Arc<DriverManagerScheme>,
|
|
poll_interval_ms: u64,
|
|
) {
|
|
log::info!(
|
|
"hotplug: starting event loop ({} ms poll)",
|
|
poll_interval_ms
|
|
);
|
|
|
|
let mut deferred_retries: BTreeMap<(String, String), u32> = BTreeMap::new();
|
|
|
|
loop {
|
|
thread::sleep(Duration::from_millis(poll_interval_ms));
|
|
|
|
let events = match manager.lock() {
|
|
Ok(mut mgr) => mgr.enumerate(),
|
|
Err(err) => {
|
|
log::error!("hotplug: failed to enumerate devices: manager lock poisoned: {err}");
|
|
break;
|
|
}
|
|
};
|
|
|
|
let mut seen_pci_devices = BTreeSet::new();
|
|
let mut pci_enumerated = false;
|
|
|
|
for event in &events {
|
|
match event {
|
|
ProbeEvent::BusEnumerated { bus, .. } => {
|
|
if bus == "pci" {
|
|
pci_enumerated = true;
|
|
}
|
|
}
|
|
ProbeEvent::BusEnumerationFailed { bus, error } => {
|
|
log::error!("hotplug: bus {} enumeration failed: {:?}", bus, error);
|
|
}
|
|
ProbeEvent::AlreadyBound {
|
|
device,
|
|
driver_name,
|
|
} => {
|
|
track_pci_device(device, &mut seen_pci_devices);
|
|
log::debug!("hotplug: already bound {} -> {}", device.path, driver_name);
|
|
}
|
|
ProbeEvent::ProbeCompleted {
|
|
device,
|
|
driver_name,
|
|
result,
|
|
} => {
|
|
track_pci_device(device, &mut seen_pci_devices);
|
|
let key = (device.path.clone(), driver_name.clone());
|
|
|
|
match result {
|
|
ProbeResult::Bound => {
|
|
log::info!("hotplug: bound {} -> {}", device.path, driver_name);
|
|
notify_bound_device(scheme.as_ref(), device, driver_name);
|
|
}
|
|
ProbeResult::Deferred { reason } => {
|
|
let retries = deferred_retries.entry(key).or_insert(0);
|
|
*retries += 1;
|
|
if *retries <= MAX_DEFERRED_RETRIES {
|
|
log::info!(
|
|
"hotplug: deferred {} -> {} ({})",
|
|
device.path,
|
|
driver_name,
|
|
reason
|
|
);
|
|
} else if *retries == MAX_DEFERRED_RETRIES + 1 {
|
|
log::warn!(
|
|
"hotplug: giving up on {} -> {} after {} retries: {}",
|
|
device.path,
|
|
driver_name,
|
|
MAX_DEFERRED_RETRIES,
|
|
reason
|
|
);
|
|
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
|
|
skipped.insert((
|
|
device.path.clone(),
|
|
driver_name.clone(),
|
|
));
|
|
}
|
|
}
|
|
}
|
|
ProbeResult::Fatal { reason } => {
|
|
log::error!(
|
|
"hotplug: fatal {} -> {} ({})",
|
|
device.path,
|
|
driver_name,
|
|
reason
|
|
);
|
|
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
|
|
skipped.insert(key);
|
|
}
|
|
}
|
|
ProbeResult::NotSupported => {
|
|
log::debug!(
|
|
"hotplug: not supported {} -> {}",
|
|
device.path,
|
|
driver_name
|
|
);
|
|
if let Ok(mut skipped) = crate::config::PERMANENTLY_SKIPPED.lock() {
|
|
skipped.insert(key);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
ProbeEvent::NoDriverFound { device } => {
|
|
track_pci_device(device, &mut seen_pci_devices);
|
|
log::debug!("hotplug: no driver for new device {}", device.path);
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
if pci_enumerated {
|
|
for pci_addr in scheme.bound_device_addresses() {
|
|
if !seen_pci_devices.contains(&pci_addr) {
|
|
let device_id = DeviceId {
|
|
bus: String::from("pci"),
|
|
path: pci_addr.clone(),
|
|
};
|
|
match manager.lock() {
|
|
Ok(mut mgr) => match mgr.remove_device(&device_id) {
|
|
Ok(Some(driver_name)) => {
|
|
log::info!("hotplug: removed {} from {}", pci_addr, driver_name);
|
|
notify_unbind(scheme.as_ref(), &pci_addr);
|
|
}
|
|
Ok(core::option::Option::None) => {
|
|
log::warn!(
|
|
"hotplug: {} disappeared but had no manager binding",
|
|
pci_addr
|
|
);
|
|
notify_unbind(scheme.as_ref(), &pci_addr);
|
|
}
|
|
Err(err) => {
|
|
log::error!(
|
|
"hotplug: failed to detach removed device {}: {:?}",
|
|
pci_addr,
|
|
err
|
|
);
|
|
}
|
|
},
|
|
Err(err) => {
|
|
log::error!(
|
|
"hotplug: failed to detach removed device {}: manager lock poisoned: {}",
|
|
pci_addr,
|
|
err
|
|
);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let retry_events = match manager.lock() {
|
|
Ok(mut mgr) => mgr.retry_deferred(),
|
|
Err(err) => {
|
|
log::error!(
|
|
"hotplug: failed to retry deferred probes: manager lock poisoned: {err}"
|
|
);
|
|
break;
|
|
}
|
|
};
|
|
|
|
let mut resolved = 0usize;
|
|
for event in &retry_events {
|
|
if let ProbeEvent::ProbeCompleted {
|
|
device,
|
|
driver_name,
|
|
result,
|
|
} = event
|
|
{
|
|
if *result == ProbeResult::Bound {
|
|
resolved += 1;
|
|
notify_bound_device(scheme.as_ref(), device, driver_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
if resolved > 0 {
|
|
log::info!("hotplug: resolved {} deferred probes", resolved);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn track_pci_device(device: &DeviceId, seen_pci_devices: &mut BTreeSet<String>) {
|
|
if device.bus == "pci" {
|
|
seen_pci_devices.insert(device.path.clone());
|
|
}
|
|
}
|
|
|
|
fn notify_bound_device(scheme: &DriverManagerScheme, device: &DeviceId, driver_name: &str) {
|
|
// PCI devices use the pcid-compatible bind notification.
|
|
// ACPI devices may be notified through other mechanisms in the future.
|
|
if device.bus == "pci" {
|
|
notify_bind(scheme, &device.path, driver_name);
|
|
}
|
|
}
|