From 0c226a58617a5ca9dfc30bc6419c58dce361cea6 Mon Sep 17 00:00:00 2001 From: xiaohui Date: Fri, 10 Jul 2026 14:00:45 +0800 Subject: [PATCH 1/3] opt: optimize the irq dep Signed-off-by: xiaohui --- arch/khal/Cargo.toml | 2 +- arch/khal/src/irq/desc.rs | 53 +++------- arch/khal/src/irq/manager.rs | 28 +++-- drivers/console/src/lib.rs | 2 +- drivers/device-res/src/irq.rs | 8 +- drivers/kdevice/src/device/resource.rs | 13 +-- drivers/kdevice/src/lib.rs | 2 +- drivers/kdriver/docs/design.md | 47 +++++---- drivers/kdriver/docs/security.md | 14 +-- drivers/kdriver/src/resource.rs | 138 ++++++------------------- drivers/virtio/src/net.rs | 4 +- drivers/virtio/src/pci.rs | 2 +- 12 files changed, 109 insertions(+), 204 deletions(-) diff --git a/arch/khal/Cargo.toml b/arch/khal/Cargo.toml index ed92598fd..c591bfbe5 100644 --- a/arch/khal/Cargo.toml +++ b/arch/khal/Cargo.toml @@ -45,7 +45,7 @@ acpi = { workspace = true } of = { workspace = true } unittest.workspace = true bitflags = { workspace = true } -handler_table = "0.1.2" +device_res = { workspace = true } [target.'cfg(target_arch = "x86_64")'.dependencies] diff --git a/arch/khal/src/irq/desc.rs b/arch/khal/src/irq/desc.rs index 696768237..11e01be04 100644 --- a/arch/khal/src/irq/desc.rs +++ b/arch/khal/src/irq/desc.rs @@ -5,6 +5,14 @@ //! OS-visible IRQ descriptors. use bitflags::bitflags; +// The trigger / controller / domain vocabulary is owned by the OS-agnostic +// `device_res` crate and re-exported here under its historical `khal::irq` +// names, so existing call sites compile unchanged. The composite `IrqDesc` and +// the arch-core state it carries (polarity / source / affinity / flags / the +// virq namespace) stay in `khal`. +pub use device_res::{ + IrqController as IrqControllerKind, IrqDomainId, IrqTriggerMode as IrqTrigger, +}; /// OS-visible logical interrupt number managed by `khal::irq`. pub type Virq = usize; @@ -21,24 +29,10 @@ pub enum IrqSource { Unknown, } -/// IRQ domain identifier used to distinguish logical interrupt namespaces. -#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] -pub struct IrqDomainId(pub u32); - pub const GIC_ROOT_DOMAIN: IrqDomainId = IrqDomainId(1); pub const PLIC_ROOT_DOMAIN: IrqDomainId = IrqDomainId(2); pub const IO_APIC_DOMAIN: IrqDomainId = IrqDomainId(3); -/// Interrupt controller family associated with an IRQ resource. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IrqControllerKind { - Gic, - IoApic, - Plic, - LoongArchExtioi, - Unknown, -} - /// Signal polarity described for an interrupt resource. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IrqPolarity { @@ -47,29 +41,12 @@ pub enum IrqPolarity { Unknown, } -/// Trigger semantics described for an interrupt resource. -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IrqTrigger { - EdgeRising, - EdgeFalling, - LevelHigh, - LevelLow, - Unknown(u32), -} - -impl IrqTrigger { - /// Returns whether the interrupt trigger is edge-triggered. - pub const fn is_edge(self) -> bool { - matches!(self, Self::EdgeRising | Self::EdgeFalling) - } - - /// Returns the implied signal polarity when one is encoded by the trigger. - pub const fn polarity(self) -> IrqPolarity { - match self { - Self::EdgeRising | Self::LevelHigh => IrqPolarity::High, - Self::EdgeFalling | Self::LevelLow => IrqPolarity::Low, - Self::Unknown(_) => IrqPolarity::Unknown, - } +/// Returns the implied signal polarity when one is encoded by the trigger. +pub const fn trigger_polarity(trigger: IrqTrigger) -> IrqPolarity { + match trigger { + IrqTrigger::EdgeRising | IrqTrigger::LevelHigh => IrqPolarity::High, + IrqTrigger::EdgeFalling | IrqTrigger::LevelLow => IrqPolarity::Low, + IrqTrigger::Unknown(_) => IrqPolarity::Unknown, } } @@ -114,7 +91,7 @@ impl IrqDesc { virq: None, hwirq, trigger, - polarity: trigger.polarity(), + polarity: trigger_polarity(trigger), source: IrqSource::Unknown, controller: IrqControllerKind::Unknown, domain: None, diff --git a/arch/khal/src/irq/manager.rs b/arch/khal/src/irq/manager.rs index 8c8aa03b0..17238a339 100644 --- a/arch/khal/src/irq/manager.rs +++ b/arch/khal/src/irq/manager.rs @@ -4,7 +4,7 @@ //! IRQ manager and OS-visible handler dispatch state. -use alloc::collections::BTreeMap; +use alloc::{collections::BTreeMap, sync::Arc}; use core::marker::PhantomData; use crate_interface::{call_interface, def_interface}; @@ -20,8 +20,13 @@ use super::{ IrqSource, IrqTrigger, Virq, }; -/// IRQ handler type. -pub type Handler = handler_table::Handler; +/// IRQ handler invoked on dispatch. +/// +/// Carried as an `Arc` so each registration owns its +/// own device context (the Rust-native counterpart of Linux's `dev_id`), +/// instead of a bare `fn()` that has to recover context indirectly through a +/// side table. +pub type Handler = Arc; /// Target CPU(s) for inter-processor interrupts. pub enum TargetCpu { @@ -209,7 +214,7 @@ struct WakeSubscription { handler: WakeHandler, } -#[derive(Clone, Copy)] +#[derive(Clone)] struct IrqStateDesc { desc: IrqDesc, handler: Option, @@ -341,7 +346,7 @@ fn dispatch_subscribers(virq: Virq) { return; }; let desc = entry.desc; - let regular_handler = entry.handler; + let regular_handler = entry.handler.clone(); let wake_subscription = match entry.wake_subscription { Some(subscription) if subscription.mode == WakeupMode::Persistent => Some(subscription), Some(subscription) if subscription.armed => { @@ -414,10 +419,15 @@ pub fn descriptor(virq: Virq) -> Option { /// Register the regular OS IRQ handler for an IRQ line. /// +/// Accepts any non-capturing or capturing `Fn()` closure (or `fn` item): it is +/// boxed into the erased [`Handler`] (`Arc`) internally, +/// so each registration carries its own context — the Rust-native counterpart of +/// Linux's `dev_id` — with no side table. +/// /// This is different from wakeup subscription: the registered handler is invoked /// directly on dispatch, while wakeup subscribers only participate in the wakeup /// notification path. -pub fn register(desc: impl IntoIrqDesc, handler: Handler) -> bool { +pub fn register(desc: impl IntoIrqDesc, handler: F) -> bool { let mut state = IRQ_STATE.lock(); let desc = state.resolve_desc(desc.into_irq_desc()); let virq = desc.logical_irq().unwrap(); @@ -429,6 +439,7 @@ pub fn register(desc: impl IntoIrqDesc, handler: Handler) -> bool { warn!("register handler for IRQ {virq} failed"); return false; } + let handler: Handler = Arc::new(handler); entry.handler = Some(handler); let desc = entry.desc; drop(state); @@ -552,6 +563,7 @@ pub fn irq_handler(vector: usize) -> bool { #[cfg(unittest)] #[allow(missing_docs)] pub mod tests_irq { + use alloc::sync::Arc; use core::sync::atomic::{AtomicUsize, Ordering}; use unittest::def_test; @@ -598,7 +610,7 @@ pub mod tests_irq { virq, IrqStateDesc { desc: IrqDesc::from_virq(virq), - handler: Some(test_handler), + handler: Some(Arc::new(test_handler)), wake_subscription: Some(WakeSubscription { mode: WakeupMode::Persistent, armed: true, @@ -624,7 +636,7 @@ pub mod tests_irq { virq, IrqStateDesc { desc: IrqDesc::from_virq(virq), - handler: Some(test_handler), + handler: Some(Arc::new(test_handler)), wake_subscription: Some(WakeSubscription { mode: WakeupMode::OneShot, armed: true, diff --git a/drivers/console/src/lib.rs b/drivers/console/src/lib.rs index 3cc989c8a..1e46e8a97 100644 --- a/drivers/console/src/lib.rs +++ b/drivers/console/src/lib.rs @@ -231,7 +231,7 @@ pub fn register_input_irq_handler() { { return; } - let resource = IrqResource::new(virq, IrqTriggerMode::Unspecified); + let resource = IrqResource::new(virq, IrqTriggerMode::Unknown(0)); match Irq::request(resource, Arc::new(handle_input_irq)) { Ok(guard) => *INPUT_IRQ.lock() = Some(guard), Err(err) => { diff --git a/drivers/device-res/src/irq.rs b/drivers/device-res/src/irq.rs index 04572b2aa..a5fe72a0d 100644 --- a/drivers/device-res/src/irq.rs +++ b/drivers/device-res/src/irq.rs @@ -16,15 +16,17 @@ use crate::{ /// Interrupt trigger mode. /// /// This is intentionally OS-neutral. Host kernels convert their own trigger -/// representation into this enum at discovery time. +/// representation into this enum at discovery time. `Unknown` carries the raw +/// flag bits the host preserved (0 when truly unknown) so downstream layers +/// never lose firmware-described trigger information. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum IrqTriggerMode { EdgeRising, EdgeFalling, LevelHigh, LevelLow, - /// Trigger mode not described by firmware. - Unspecified, + /// Trigger mode not described by firmware; carries raw flag bits (0 if none). + Unknown(u32), } /// The interrupt controller family that owns an IRQ line. diff --git a/drivers/kdevice/src/device/resource.rs b/drivers/kdevice/src/device/resource.rs index b4a56c53f..de9335699 100644 --- a/drivers/kdevice/src/device/resource.rs +++ b/drivers/kdevice/src/device/resource.rs @@ -22,17 +22,6 @@ pub fn irq_trigger_from_firmware( khal::firmware::devices::InterruptTrigger::EdgeFalling => IrqTriggerMode::EdgeFalling, khal::firmware::devices::InterruptTrigger::LevelHigh => IrqTriggerMode::LevelHigh, khal::firmware::devices::InterruptTrigger::LevelLow => IrqTriggerMode::LevelLow, - khal::firmware::devices::InterruptTrigger::Unknown(_) => IrqTriggerMode::Unspecified, - } -} - -/// Convert a `khal` IRQ trigger into an [`IrqTriggerMode`]. -pub fn irq_trigger_from_khal(trigger: khal::irq::IrqTrigger) -> IrqTriggerMode { - match trigger { - khal::irq::IrqTrigger::EdgeRising => IrqTriggerMode::EdgeRising, - khal::irq::IrqTrigger::EdgeFalling => IrqTriggerMode::EdgeFalling, - khal::irq::IrqTrigger::LevelHigh => IrqTriggerMode::LevelHigh, - khal::irq::IrqTrigger::LevelLow => IrqTriggerMode::LevelLow, - khal::irq::IrqTrigger::Unknown(_) => IrqTriggerMode::Unspecified, + khal::firmware::devices::InterruptTrigger::Unknown(f) => IrqTriggerMode::Unknown(f), } } diff --git a/drivers/kdevice/src/lib.rs b/drivers/kdevice/src/lib.rs index fb63f82c8..1f5512993 100644 --- a/drivers/kdevice/src/lib.rs +++ b/drivers/kdevice/src/lib.rs @@ -72,7 +72,7 @@ pub use device::{ object::{DeviceObject, DeviceUse}, resource::{ DmaSpec, IoPortRange, IrqResource, IrqTriggerMode, MmioRegion, ResourceDesc, ResourceSet, - irq_trigger_from_firmware, irq_trigger_from_khal, + irq_trigger_from_firmware, }, }; pub use driver::{ diff --git a/drivers/kdriver/docs/design.md b/drivers/kdriver/docs/design.md index a28cd3890..f3ad23461 100644 --- a/drivers/kdriver/docs/design.md +++ b/drivers/kdriver/docs/design.md @@ -258,26 +258,29 @@ VirtIO 驱动在 PCI 和 MMIO 两条传输路径上共享同一激活入口: `resource.rs` 提供 device-managed 资源分配,绑定到 `DeviceObject` 的 devres 清理链表: - **`devm_iomap`**:通过 `memspace::iomap_device` 映射 MMIO,probe 失败或设备 remove 时自动 `iounmap`。 -- **`devm_request_irq`**:注册中断处理函数到 `khal::irq`,通过 IRQ slot table 桥接上下文 handler。 +- **`devm_request_irq`**:注册中断处理函数到 `khal::irq`,handler 以捕获了 `Arc` 的闭包形式直接注册(Linux `dev_id` 风格),无需中间 slot 表。 - **`devm_alloc_coherent`**:通过 `kdma::allocate_dma_memory` 分配一致性 DMA 缓冲区,release 时调用 `kdma::deallocate_dma_memory`。 释放顺序与申请顺序相反(LIFO),避免资源依赖错乱。 ### IRQ 分发机制 -x-kernel 的 `khal::irq::register` 只接受裸 `fn()` 函数指针,不支持上下文传递。 -`HostResourceProvider` 通过以下方案桥接: +`khal::irq::register` 接受任意 `Fn() + Send + Sync` 闭包,内部擦除为 +`Arc` 存储。`HostResourceProvider` 借此把设备 handler 直接 +注册为一个捕获了 `Arc` 的闭包——这是 Linux `dev_id` 的 Rust 原生对应物 +(闭包即"函数指针 + 私有上下文"的合体),无需任何 slot 表或 trampoline: -1. 预分配 64 个 `IrqSlot`,每个 slot 持有一个 `SpinNoIrq>`。 -2. 编译期通过宏生成 64 个互不相同的 `fn()` trampoline,每个 trampoline 转发到对应 slot 的 `dispatch_slot`。 -3. 注册 IRQ 时:先在线程上下文中把 `Arc` 写入空闲 slot,再调用 `khal::irq::register` 绑定 trampoline。 -4. 中断到达时:trampoline 被调用 → 从对应 slot 取出 handler → 调用 `handler.handle()`。 +1. `request_irq`:`khal::irq::register(irq.number, move || handler.handle())`, + 闭包直接拥有该 `Arc`。 +2. 中断到达时:`khal::irq` 调用存储的闭包 → 执行 `handler.handle()`。 +3. `release_irq`:`khal::irq::unregister` 丢弃存储的 `Arc`, + 连带释放其捕获的 `Arc`。 ## 并发模型 - `DeviceManager::bus_mgr` 使用 `SpinNoPreempt`:总线枚举、重扫描、quiesce、remove 操作在 process context 中执行,互斥访问。 - `EnumerationContext` 自身没有 interior locking:它是总线后端和 probe 之间的单线程桥梁,仅在 `bus_mgr` 锁内被填充。 -- `IrqSlot::state` 使用 `SpinNoIrq`:中断分发和注册路径可能并发,用关中断锁保护。 +- IRQ handler 的并发与生命周期由 `khal::irq` 单一掌握(`register`/`unregister`),`kdriver` 不再维护独立的 slot 锁。 - `PCI_BAR_ALLOCATOR` 使用 `SpinNoPreempt`:BAR 分配仅在 process context(枚举或 probe)中发生。 - `kdevice` 共享核心的内部锁由 `kdevice` crate 自行管理,`kdriver` 不直接持有其锁。 @@ -349,30 +352,30 @@ x-kernel 的 `khal::irq::register` 只接受裸 `fn()` 函数指针,不支持 该方案减少了描述符数量,但要求匹配器同时比较 bus type 和 VirtIO type, 且 `probe_device` 入口需要分支处理 PCI 和 MMIO 两种传输初始化逻辑。 -### IRQ 静态槽位而非动态分配 +### IRQ handler 以闭包直接注册,而非 slot + trampoline -**选择**:64 个 slot 的静态数组,编译期生成对应 trampoline。 +**选择**:`khal::irq` 把 handler 存为 `Arc`, +`HostResourceProvider` 注册一个捕获了 `Arc` 的闭包。 -**Trade-off**:硬编码上限(64)限制了同时使用中断的设备数量, -但换取了以下好处: +**Trade-off**:每次 `register` 需要一次 `Arc` 分配(闭包 + 捕获的 handler 引用计数), +但换取以下好处: -- IRQ 注册路径零动态内存分配(避免在 `request_irq` 中调用 `alloc`, - 后者可能在中断上下文的调用链中被触发); -- trampoline 函数指针在编译期确定,无需运行时 JIT 或 thunk 分配; -- slot 的 `SpinNoIrq` 保护足够轻量,满足中断上下文的锁约束。 +- 设备 handler 自带上下文,无需中间 slot 表或按 slot 生成 trampoline, + 也消除了槽位数量上限; +- 注册/释放语义由 `khal::irq` 单一掌握(`register` 原子地装入 handler, + `unregister` 丢弃闭包即释放 handler),不存在"trampoline 已绑定但 slot 尚空"的竞态窗口; +- 代码量显著减少,`request_irq`/`release_irq` 各仅数行。 -**拒绝的方案**:动态 `Vec` + 运行时生成 trampoline。 -该方案消除了设备数量上限,但引入了 IRQ 路径的内存分配、 -更复杂的并发控制(slot 扩容时需要迁移 handler), -且运行时生成可执行代码在当前 `#![no_std]` 环境中不可行。 -64 个 slot 覆盖了当前所有 VirtIO 设备类型 + 平台设备的中断需求。 +**拒绝的方案**:预分配静态 `IrqSlot` 数组 + 编译期为每个 slot 生成独立 `fn()` trampoline +(早期实现)。该方案注册路径零分配,但需要维护 slot 表与 trampoline 的身份映射、 +硬编码设备上限,并承担"先写 slot 再绑 trampoline"的注册/释放顺序约束。 ## Drop / 资源释放 - devres 资源在 `DeviceObject` 的 remove 路径中按 LIFO 顺序释放。 - `PciBackend` 不持有需在 drop 中释放的持久资源(`PciBus` 在 `enumerate` 返回时释放)。 - `PlatformBackend` 仅持有 `LocalIdAlloc`(栈上 u16),无需显式释放。 -- 中断释放通过 `khal::irq::unregister` 解绑 trampoline,随后清空 slot 复用。 +- 中断释放通过 `khal::irq::unregister` 丢弃存储的闭包,连带释放其捕获的 `Arc`。 - 共享 DMA buffer 在 last handle drop 后由 `kdma::deallocate_dma_memory` 回收。 ## Feature 门控关系 diff --git a/drivers/kdriver/docs/security.md b/drivers/kdriver/docs/security.md index eddae3e30..2c7a6c784 100644 --- a/drivers/kdriver/docs/security.md +++ b/drivers/kdriver/docs/security.md @@ -33,7 +33,7 @@ firmware (DT / ACPI) / PCI config space │ (raw MMIO register read)│ └──────────────┬──────────────┘ │ - │ validated mappings, IRQ trampolines, + │ validated mappings, IRQ handler closures, │ DMA buffers v khal / kdma / memspace / driver crates @@ -331,8 +331,8 @@ unsafe impl IxgbeHal for IxgbeHalImpl { ... } 1. **MMIO vaddr 生命周期**:`devm_iomap` 返回的 `NonNull` 仅在 `DeviceObject` 存活期间有效,probe 失败或设备 remove 时 `iounmap` 释放。 2. **DMA buffer 独占所有权**:`devm_alloc_coherent` 返回的 `DmaAllocation` 由 devres 独占持有,无公开 clone/复制接口。 3. **DMA alloc/free 配对**:`alloc_coherent` 和 `free_coherent` 使用相同的 `DmaSpec` 重建 `Layout`,保证 size/align 一致。 -4. **IRQ slot 注册顺序**:先存储 handler 到 slot,再 `khal::irq::register` trampoline;`register` 失败时回滚清空 slot。 -5. **IRQ slot 释放顺序**:先 `khal::irq::unregister` trampoline,再清空 slot,保证中断到达时要么找到有效 handler,要么 slot 为空。 +4. **IRQ handler 原子注册**:`request_irq` 把捕获了 `Arc` 的闭包交给 `khal::irq::register`,由后者原子地装入;不存在"分发路径已激活但 handler 尚未就绪"的窗口。 +5. **IRQ handler 释放**:`release_irq` 调用 `khal::irq::unregister`,丢弃存储的闭包并连带释放其捕获的 `Arc`。 6. **VirtIO DMA 清零**:`dma_alloc` 分配后用 `write_bytes(0)` 清零,防止设备读到内核残留数据。 7. **VirtIO share/unshare 配对**:`share` 和 `unshare` 成对调用,方向一致,由 `virtio` crate 传输层保证。 8. **PCI BAR 零地址拒绝**:枚举阶段分配后仍为 0 的 BAR 被跳过,不注册为有效资源。 @@ -360,7 +360,7 @@ unsafe impl IxgbeHal for IxgbeHalImpl { ... } |------|----------|----------|----------|----------| | T-01 | firmware 提供非法物理地址,MMIO 映射覆盖内核关键数据结构 | 高 | DT/ACPI 描述恶意物理地址且 `memspace::iomap_device` 未拒绝 | `iomap_device` 校验地址是否在平台 MMIO 窗口内;非法范围返回 `InvalidRange` | | T-02 | PCI BAR 分配后仍为零地址导致 page zero 映射 | 高 | BAR 分配器耗尽或分配范围未初始化,且 BAR 配置逻辑未拒绝零地址 | `configure_pci_device_if_needed` 分配失败返回 `NoMemory`;枚举 pass 3 跳过 `address == 0` 的 BAR | -| T-03 | IRQ slot 注册与中断到达竞态,handler 在未就绪时被调用 | 高 | 中断在 `register` trampoline 绑定后、handler 写入 slot 前到达 | handler 在 trampoline 绑定前写入 slot;`register` 失败时回滚清空 slot | +| T-03 | IRQ 注册与中断到达竞态,handler 在未就绪时被调用 | 高 | 中断在 handler 装入前到达该 virq | `khal::irq::register` 原子地装入闭包 handler,分发只会在装入完成后命中它;无中间 slot/trampoline 窗口 | | T-04 | DMA double-free 导致内存破坏 | 高 | `free_coherent` 被多次调用或 layout 不匹配 | `DmaAllocation` 无 Clone,devres 独占所有权;`alloc`/`free` 使用相同 `DmaSpec` 重建 `Layout` | | T-05 | VirtIO 设备通过恶意 DMA 描述符访问非授权内核内存 | 高 | 恶意或故障 VirtIO 设备构造错误描述符链 | 当前无 IOMMU 隔离单个 VirtIO 设备;`dma_alloc` 清零防止信息泄露;`share`/`unshare` 通过 `kdma` 管理 | | T-06 | firmware 伪造设备 compatible 导致错误驱动绑定 | 中 | DT 提供虚假 compatible string 且恰好命中已注册的 `FirmwareMatchSpec` | 驱动 probe 会因硬件无响应而失败,设备进入 unclaimed 列表 | @@ -388,7 +388,7 @@ unsafe impl IxgbeHal for IxgbeHalImpl { ... } | F-03 | 单个设备 probe 失败 | 驱动 `probe_device` 返回错误 | 该设备不可用 | 同总线其他设备正常激活 | 4 | probe 错误记录 warn 并进入 unclaimed 列表,不阻断后续设备 | | F-04 | PCI BAR 分配器未初始化 | `pci_bar_allocation_range()` 返回 None 且设备有未分配 MEM BAR | 该 PCI 设备被跳过 | 单个 PCI 设备不可用 | 3 | `configure_pci_device_if_needed` 返回 `NoMemory`,设备跳过 | | F-05 | VirtIO MMIO 探测返回空设备 | MMIO 区域不存在 VirtIO 设备或 MagicValue 不匹配 | 该 MMIO 区域跳过 | 不影响其他 platform 设备 | 4 | `probe_mmio_device` 返回 None → `virtio_mmio_registration` 返回 None,记录 trace 后跳过 | -| F-06 | IRQ trampoline 未注册 | slot 分配失败(64 槽位满) | 设备无法接收中断 | 该设备功能不可用或降级到轮询 | 3 | `request_irq` 返回 `NoMemory` 或 `Busy`,驱动 probe 返回错误 | +| F-06 | IRQ handler 注册失败 | 该 virq 已有 handler(`khal::irq::register` 返回 false) | 设备无法接收中断 | 该设备功能不可用或降级到轮询 | 3 | `request_irq` 返回 `Busy`,驱动 probe 返回错误 | | F-07 | PCI host bridge adoption 失败 | platform 总线未注册或 `adopt_active_device` 错误 | PCI 设备无 host bridge parent | PCI 端点仍被枚举但设备树不完整 | 3 | adoption 失败记录 warn,枚举继续(parentless 布局) | | F-08 | 静态设备 MMIO 映射失败 | `kbuild_config` 地址非法或硬件不存在 | 该静态设备不可用 | 同总线其他设备正常 | 3 | `iomap_first_mmio` 返回错误,probe 失败 | | F-09 | 驱动注册时 bus type matcher 未就绪 | `register_bus_type` 在 driver 注册后调用 | 驱动匹配不到设备 | 设备进入 unclaimed 列表 | 2 | `default_bus_manager` 先注册 bus type matcher,再注册 bus backend,再在 `DeviceManager::new` 中注册 driver | @@ -443,8 +443,8 @@ trace 日志会输出 firmware 遍历的 compatible string 和 VirtIO MMIO 探 - 每个 `unsafe` 块均有 `SAFETY:` 注释。 - 新增 MMIO 映射路径使用 `devm_iomap` 或 `iomap_mmio`(内部校验物理地址合法性)。 - 新增 DMA 分配路径通过 `devm_alloc_coherent` 或 `kdma::allocate_dma_memory`,且 `free` 时 layout 一致。 -- 新增 IRQ 注册遵循「先写 slot → 再 bind trampoline → 失败回滚」顺序。 -- 新增 IRQ 释放遵循「先 unbind trampoline → 再清空 slot」顺序。 +- 新增 IRQ 注册通过 `khal::irq::register(virq, move || handler.handle())` 把闭包 handler 原子装入;不再维护 slot 表或 trampoline。 +- 新增 IRQ 释放通过 `khal::irq::unregister(virq)`,由 `khal::irq` 丢弃闭包并释放捕获的 handler。 - 新增总线后端实现 `BusBackend` 时,`enumerate` 中对每个设备的错误不应阻断其他设备枚举。 - 新增 PCI device ID 到 VirtIO 类型的映射在 `pci_device_id_to_virtio_type` 中添加。 - 新增 firmware compatible 匹配规格在 `firmware_specs.rs` 中声明,并注册对应的 platform driver。 diff --git a/drivers/kdriver/src/resource.rs b/drivers/kdriver/src/resource.rs index 45c828c20..81cd9907e 100644 --- a/drivers/kdriver/src/resource.rs +++ b/drivers/kdriver/src/resource.rs @@ -19,73 +19,20 @@ use core::{alloc::Layout, ptr::NonNull}; use device_res::{ DmaAllocation, DmaDirection, DmaMapping, DmaOp, DmaSpec, IrqController, IrqHandler, IrqOp, - IrqResource, IrqRouteDesc, IrqTriggerMode, MmioMapping, MmioOp, MmioRegion, ResError, - ResResult, + IrqResource, IrqRouteDesc, MmioMapping, MmioOp, MmioRegion, ResError, ResResult, }; use driver_base::{DriverError, DriverResult}; use kdevice::DeviceObject; -use kspin::SpinNoIrq; /// x-kernel implementation of the OS-agnostic resource provider. struct HostResourceProvider; static HOST_PROVIDER: HostResourceProvider = HostResourceProvider; -/// Number of interrupt handlers that can be registered through the resource -/// provider at once. `khal` dispatches the regular IRQ handler as a bare -/// `fn()` carrying no identity, so each registration is bridged to a distinct -/// trampoline that recovers a context-carrying [`IrqHandler`] from this table. -const MAX_IRQ_SLOTS: usize = 64; - -/// A single bridged interrupt registration. -struct IrqSlot { - state: SpinNoIrq>, -} - -struct IrqSlotState { - virq: usize, - handler: Arc, -} - -impl IrqSlot { - const fn new() -> Self { - Self { - state: SpinNoIrq::new(None), - } - } -} - -static IRQ_SLOTS: [IrqSlot; MAX_IRQ_SLOTS] = [const { IrqSlot::new() }; MAX_IRQ_SLOTS]; - -/// Invoke the context handler bound to `slot`, if any. -/// -/// The owning `Arc` is cloned out from under the slot lock so the lock is not -/// held while the (potentially re-entrant) handler runs. -fn dispatch_slot(slot: usize) { - let handler = IRQ_SLOTS[slot] - .state - .lock() - .as_ref() - .map(|state| state.handler.clone()); - if let Some(handler) = handler { - let _ = handler.handle(); - } -} - -/// Distinct `fn()` trampolines, one per slot, each forwarding to its slot. -macro_rules! irq_trampolines { - ($($slot:literal),* $(,)?) => { - [ $( || dispatch_slot($slot) ),* ] - }; -} - -#[rustfmt::skip] -static IRQ_TRAMPOLINES: [fn(); MAX_IRQ_SLOTS] = irq_trampolines!( - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, - 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -); +// Device IRQ handlers are registered with `khal::irq` as closures that capture +// their `Arc` directly — the Rust-native counterpart of Linux's +// `dev_id`. No slot table or per-slot trampoline is needed: the closure owns its +// context, and unregistering the line drops the closure (and thus the handler). impl MmioOp for HostResourceProvider { fn map_mmio(&self, region: MmioRegion, name: &'static str) -> ResResult { @@ -173,35 +120,22 @@ impl DmaOp for HostResourceProvider { impl IrqOp for HostResourceProvider { fn request_irq(&self, irq: IrqResource, handler: Arc) -> ResResult<()> { - for (slot, trampoline) in IRQ_SLOTS.iter().zip(IRQ_TRAMPOLINES.iter()) { - let mut guard = slot.state.lock(); - if guard.is_some() { - continue; - } - // Store the handler before enabling the line so an interrupt that - // fires immediately after `register` finds its handler in place. - *guard = Some(IrqSlotState { - virq: irq.number, - handler, - }); - if khal::irq::register(irq.number, *trampoline) { - return Ok(()); - } - *guard = None; - return Err(ResError::Busy); + // The closure captures the `Arc`, so `khal::irq` stores a + // self-contained handler with no side table. The handler is in place + // before the line is enabled inside `register`. + if khal::irq::register(irq.number, move || { + let _ = handler.handle(); + }) { + Ok(()) + } else { + Err(ResError::Busy) } - Err(ResError::NoMemory) } fn release_irq(&self, irq: IrqResource) { - for slot in IRQ_SLOTS.iter() { - let mut guard = slot.state.lock(); - if guard.as_ref().is_some_and(|state| state.virq == irq.number) { - let _ = khal::irq::unregister(irq.number); - *guard = None; - return; - } - } + // Dropping the stored `Arc` releases the `Arc` + // it captured. + let _ = khal::irq::unregister(irq.number); } fn set_irq_enabled(&self, irq: IrqResource, enabled: bool) { @@ -209,21 +143,20 @@ impl IrqOp for HostResourceProvider { } fn map_irq(&self, route: IrqRouteDesc) -> ResResult { - let trigger = map_khal_trigger(route.trigger); - let desc = match route.controller { - IrqController::Gic => khal::irq::IrqDesc::new(route.hwirq, trigger) - .with_controller(khal::irq::IrqControllerKind::Gic) - .with_domain(khal::irq::GIC_ROOT_DOMAIN), - IrqController::Plic => khal::irq::IrqDesc::new(route.hwirq, trigger) - .with_controller(khal::irq::IrqControllerKind::Plic) - .with_domain(khal::irq::PLIC_ROOT_DOMAIN), - IrqController::IoApic => khal::irq::IrqDesc::new(route.hwirq, trigger) - .with_controller(khal::irq::IrqControllerKind::IoApic) - .with_domain(khal::irq::IO_APIC_DOMAIN), - IrqController::LoongArchExtioi => khal::irq::IrqDesc::new(route.hwirq, trigger) - .with_controller(khal::irq::IrqControllerKind::LoongArchExtioi), - IrqController::Unknown => khal::irq::IrqDesc::new(route.hwirq, trigger), + // `route.trigger` / `route.controller` already use the shared `device_res` + // vocabulary that `khal::irq` re-exports, so no translation is needed — + // only the controller → domain wiring. + let domain = match route.controller { + IrqController::Gic => Some(khal::irq::GIC_ROOT_DOMAIN), + IrqController::Plic => Some(khal::irq::PLIC_ROOT_DOMAIN), + IrqController::IoApic => Some(khal::irq::IO_APIC_DOMAIN), + IrqController::LoongArchExtioi | IrqController::Unknown => None, }; + let mut desc = + khal::irq::IrqDesc::new(route.hwirq, route.trigger).with_controller(route.controller); + if let Some(domain) = domain { + desc = desc.with_domain(domain); + } let virq = khal::irq::map(desc); Ok(IrqResource::new(virq, route.trigger) .with_controller(route.controller) @@ -269,17 +202,6 @@ fn map_dma_direction(d: DmaDirection) -> kdma::DmaDirection { } } -/// Translate a device-res IRQ trigger mode into the matching `khal` trigger. -fn map_khal_trigger(t: IrqTriggerMode) -> khal::irq::IrqTrigger { - match t { - IrqTriggerMode::EdgeRising => khal::irq::IrqTrigger::EdgeRising, - IrqTriggerMode::EdgeFalling => khal::irq::IrqTrigger::EdgeFalling, - IrqTriggerMode::LevelHigh => khal::irq::IrqTrigger::LevelHigh, - IrqTriggerMode::LevelLow => khal::irq::IrqTrigger::LevelLow, - IrqTriggerMode::Unspecified => khal::irq::IrqTrigger::Unknown(0), - } -} - fn map_iomap_err(err: memspace::IoMapError) -> ResError { match err { memspace::IoMapError::NoMemory => ResError::NoMemory, diff --git a/drivers/virtio/src/net.rs b/drivers/virtio/src/net.rs index 74249a502..d9d1bf81e 100644 --- a/drivers/virtio/src/net.rs +++ b/drivers/virtio/src/net.rs @@ -86,7 +86,7 @@ fn register_virtio_net_irq NET_IRQ_GUARDS.lock().push(guard), Err(_) => { @@ -109,7 +109,7 @@ fn unregister_virtio_net_irq(irq: usize, handle_id: usize) { if !irq_still_used { REGISTERED_NET_IRQS.lock().remove(&irq); if let Some(p) = try_irq_provider() { - p.release_irq(IrqResource::new(irq, IrqTriggerMode::Unspecified)); + p.release_irq(IrqResource::new(irq, IrqTriggerMode::Unknown(0))); } } } diff --git a/drivers/virtio/src/pci.rs b/drivers/virtio/src/pci.rs index a06f992ba..df04a46fb 100644 --- a/drivers/virtio/src/pci.rs +++ b/drivers/virtio/src/pci.rs @@ -272,7 +272,7 @@ fn fw_trigger_to_mode(t: InterruptTrigger) -> IrqTriggerMode { InterruptTrigger::EdgeFalling => IrqTriggerMode::EdgeFalling, InterruptTrigger::LevelHigh => IrqTriggerMode::LevelHigh, InterruptTrigger::LevelLow => IrqTriggerMode::LevelLow, - InterruptTrigger::Unknown(_) => IrqTriggerMode::Unspecified, + InterruptTrigger::Unknown(f) => IrqTriggerMode::Unknown(f), } } -- Gitee From 69f21544b086b7759f01252311869d45221198c3 Mon Sep 17 00:00:00 2001 From: xiaohui Date: Mon, 13 Jul 2026 11:27:56 +0800 Subject: [PATCH 2/3] opt: unified the name Signed-off-by: xiaohui --- arch/khal/src/irq/desc.rs | 28 ++++++++++++-------------- arch/khal/src/irq/manager.rs | 8 ++++---- arch/khal/src/irq/mod.rs | 2 +- drivers/console/src/lib.rs | 4 ++-- drivers/device-res/src/irq.rs | 8 ++++---- drivers/device-res/src/lib.rs | 8 ++++---- drivers/kdevice/src/device/resource.rs | 18 ++++++++--------- drivers/kdevice/src/lib.rs | 2 +- drivers/virtio/src/net.rs | 6 +++--- drivers/virtio/src/pci.rs | 18 ++++++++--------- drivers/virtio/src/pci/x86_64.rs | 4 ++-- platforms/x86_64-qemu-virt/src/init.rs | 2 +- 12 files changed, 52 insertions(+), 56 deletions(-) diff --git a/arch/khal/src/irq/desc.rs b/arch/khal/src/irq/desc.rs index 11e01be04..6971c5e89 100644 --- a/arch/khal/src/irq/desc.rs +++ b/arch/khal/src/irq/desc.rs @@ -6,13 +6,11 @@ use bitflags::bitflags; // The trigger / controller / domain vocabulary is owned by the OS-agnostic -// `device_res` crate and re-exported here under its historical `khal::irq` -// names, so existing call sites compile unchanged. The composite `IrqDesc` and -// the arch-core state it carries (polarity / source / affinity / flags / the -// virq namespace) stay in `khal`. -pub use device_res::{ - IrqController as IrqControllerKind, IrqDomainId, IrqTriggerMode as IrqTrigger, -}; +// `device_res` crate and re-exported here so `khal::irq` remains the single +// import path for IRQ types. The composite `IrqDesc` and the arch-core state +// it carries (polarity / source / affinity / flags / the virq namespace) stay +// in `khal`. +pub use device_res::{IrqController, IrqDomainId, IrqTrigger}; /// OS-visible logical interrupt number managed by `khal::irq`. pub type Virq = usize; @@ -78,7 +76,7 @@ pub struct IrqDesc { pub trigger: IrqTrigger, pub polarity: IrqPolarity, pub source: IrqSource, - pub controller: IrqControllerKind, + pub controller: IrqController, pub domain: Option, pub affinity: IrqAffinity, pub flags: IrqFlags, @@ -93,7 +91,7 @@ impl IrqDesc { trigger, polarity: trigger_polarity(trigger), source: IrqSource::Unknown, - controller: IrqControllerKind::Unknown, + controller: IrqController::Unknown, domain: None, affinity: IrqAffinity::Any, flags: IrqFlags::empty(), @@ -113,7 +111,7 @@ impl IrqDesc { trigger: IrqTrigger::Unknown(0), polarity: IrqPolarity::Unknown, source: IrqSource::Unknown, - controller: IrqControllerKind::Unknown, + controller: IrqController::Unknown, domain: None, affinity: IrqAffinity::Any, flags: IrqFlags::empty(), @@ -126,7 +124,7 @@ impl IrqDesc { } /// Marks which controller family owns this descriptor. - pub const fn with_controller(self, controller: IrqControllerKind) -> Self { + pub const fn with_controller(self, controller: IrqController) -> Self { Self { controller, ..self } } @@ -190,7 +188,7 @@ impl IrqDesc { _ => newer.source, }, controller: match newer.controller { - IrqControllerKind::Unknown => self.controller, + IrqController::Unknown => self.controller, _ => newer.controller, }, domain: newer.domain.or(self.domain), @@ -205,7 +203,7 @@ impl IrqDesc { pub const fn gic_irq_desc(hwirq: Hwirq, trigger: IrqTrigger) -> IrqDesc { IrqDesc::new(hwirq, trigger) - .with_controller(IrqControllerKind::Gic) + .with_controller(IrqController::Gic) .with_domain(GIC_ROOT_DOMAIN) } @@ -219,13 +217,13 @@ pub const fn gic_edge_irq_desc(hwirq: Hwirq) -> IrqDesc { pub const fn plic_irq_desc(hwirq: Hwirq) -> IrqDesc { IrqDesc::from_hwirq(hwirq) - .with_controller(IrqControllerKind::Plic) + .with_controller(IrqController::Plic) .with_domain(PLIC_ROOT_DOMAIN) } pub const fn io_apic_irq_desc(hwirq: Hwirq) -> IrqDesc { IrqDesc::from_hwirq(hwirq) - .with_controller(IrqControllerKind::IoApic) + .with_controller(IrqController::IoApic) .with_domain(IO_APIC_DOMAIN) } diff --git a/arch/khal/src/irq/manager.rs b/arch/khal/src/irq/manager.rs index 17238a339..1021667f2 100644 --- a/arch/khal/src/irq/manager.rs +++ b/arch/khal/src/irq/manager.rs @@ -16,8 +16,8 @@ use kspin::SpinNoIrq; #[cfg(feature = "ipi")] pub use self::TargetCpu as IpiTarget; use super::{ - Hwirq, IntoIrqDesc, IrqAffinity, IrqControllerKind, IrqDesc, IrqDomainId, IrqPolarity, - IrqSource, IrqTrigger, Virq, + Hwirq, IntoIrqDesc, IrqAffinity, IrqController, IrqDesc, IrqDomainId, IrqPolarity, IrqSource, + IrqTrigger, Virq, }; /// IRQ handler invoked on dispatch. @@ -150,7 +150,7 @@ fn needs_platform_binding(desc: IrqDesc) -> bool { || !matches!(desc.trigger, IrqTrigger::Unknown(_)) || desc.polarity != IrqPolarity::Unknown || desc.source != IrqSource::Unknown - || desc.controller != IrqControllerKind::Unknown + || desc.controller != IrqController::Unknown || desc.affinity != IrqAffinity::Any || !desc.flags.is_empty() } @@ -271,7 +271,7 @@ impl IrqState { && matches!(desc.trigger, IrqTrigger::Unknown(_)) && desc.polarity == IrqPolarity::Unknown && desc.source == IrqSource::Unknown - && desc.controller == IrqControllerKind::Unknown + && desc.controller == IrqController::Unknown && desc.affinity == IrqAffinity::Any && desc.flags.is_empty() && let Some(existing) = self.stored_desc(virq) diff --git a/arch/khal/src/irq/mod.rs b/arch/khal/src/irq/mod.rs index 7ac56b5c7..570bbb02d 100644 --- a/arch/khal/src/irq/mod.rs +++ b/arch/khal/src/irq/mod.rs @@ -8,7 +8,7 @@ mod desc; mod manager; pub use desc::{ - GIC_ROOT_DOMAIN, Hwirq, IO_APIC_DOMAIN, IntoIrqDesc, IrqAffinity, IrqControllerKind, IrqDesc, + GIC_ROOT_DOMAIN, Hwirq, IO_APIC_DOMAIN, IntoIrqDesc, IrqAffinity, IrqController, IrqDesc, IrqDomainId, IrqFlags, IrqPolarity, IrqSource, IrqTrigger, PLIC_ROOT_DOMAIN, Virq, gic_edge_irq_desc, gic_irq_desc, gic_level_irq_desc, io_apic_irq_desc, plic_irq_desc, }; diff --git a/drivers/console/src/lib.rs b/drivers/console/src/lib.rs index 1e46e8a97..1ae7dcae4 100644 --- a/drivers/console/src/lib.rs +++ b/drivers/console/src/lib.rs @@ -12,7 +12,7 @@ pub mod serial; use alloc::sync::Arc; use core::sync::atomic::{AtomicBool, Ordering}; -use device_res::{Irq, IrqEvent, IrqResource, IrqTriggerMode}; +use device_res::{Irq, IrqEvent, IrqResource, IrqTrigger}; use khal::irq::IrqDesc; #[cfg(any(feature = "pl011", feature = "ns16550-mmio"))] use khal::mem::PhysAddr; @@ -231,7 +231,7 @@ pub fn register_input_irq_handler() { { return; } - let resource = IrqResource::new(virq, IrqTriggerMode::Unknown(0)); + let resource = IrqResource::new(virq, IrqTrigger::Unknown(0)); match Irq::request(resource, Arc::new(handle_input_irq)) { Ok(guard) => *INPUT_IRQ.lock() = Some(guard), Err(err) => { diff --git a/drivers/device-res/src/irq.rs b/drivers/device-res/src/irq.rs index a5fe72a0d..2bf792655 100644 --- a/drivers/device-res/src/irq.rs +++ b/drivers/device-res/src/irq.rs @@ -20,7 +20,7 @@ use crate::{ /// flag bits the host preserved (0 when truly unknown) so downstream layers /// never lose firmware-described trigger information. #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum IrqTriggerMode { +pub enum IrqTrigger { EdgeRising, EdgeFalling, LevelHigh, @@ -57,7 +57,7 @@ pub struct IrqResource { /// IRQ number visible to the OS (virtual IRQ after domain translation). pub number: usize, /// Trigger mode. - pub trigger: IrqTriggerMode, + pub trigger: IrqTrigger, /// Controller family that owns this IRQ, if known. pub controller: Option, /// Domain this IRQ belongs to, if the host partitions IRQ number space. @@ -71,7 +71,7 @@ pub struct IrqResource { impl IrqResource { /// Construct a minimal IRQ resource with just a number and trigger. - pub const fn new(number: usize, trigger: IrqTriggerMode) -> Self { + pub const fn new(number: usize, trigger: IrqTrigger) -> Self { Self { number, trigger, @@ -117,7 +117,7 @@ pub struct IrqRouteDesc { /// Hardware IRQ number as discovered (e.g. GIC SPI number, PLIC source). pub hwirq: usize, /// Trigger mode. - pub trigger: IrqTriggerMode, + pub trigger: IrqTrigger, /// Controller family that owns this route. pub controller: IrqController, /// Domain id, when the host partitions IRQ number space. diff --git a/drivers/device-res/src/lib.rs b/drivers/device-res/src/lib.rs index 720f28268..a23e743d0 100644 --- a/drivers/device-res/src/lib.rs +++ b/drivers/device-res/src/lib.rs @@ -103,7 +103,7 @@ mod tests { use crate::{ DeviceResource, DmaAllocation, DmaCoherent, DmaOp, DmaSpec, Io, Irq, IrqEvent, IrqHandler, - IrqOp, IrqResource, IrqTriggerMode, MmioMapping, MmioOp, MmioRegion, ResError, ResResult, + IrqOp, IrqResource, IrqTrigger, MmioMapping, MmioOp, MmioRegion, ResError, ResResult, ResourceDesc, devm_alloc_coherent, devm_iomap, devm_request_irq, provider_installed, reset_providers, set_dma_provider, set_irq_provider, set_mmio_provider, try_dma_provider, try_irq_provider, try_mmio_provider, @@ -341,7 +341,7 @@ mod tests { ); assert_eq!( Irq::request( - IrqResource::new(1, IrqTriggerMode::EdgeRising), + IrqResource::new(1, IrqTrigger::EdgeRising), Arc::new(|| IrqEvent::HANDLED), ) .unwrap_err(), @@ -388,7 +388,7 @@ mod tests { let _serial = TEST_SERIAL.lock(); let _g = ProviderGuard::new(); - let irq = IrqResource::new(7, IrqTriggerMode::LevelHigh); + let irq = IrqResource::new(7, IrqTrigger::LevelHigh); let spec = DmaSpec::new(24, 8); let guard = Irq::request(irq, Arc::new(|| IrqEvent::HANDLED)).unwrap(); @@ -460,7 +460,7 @@ mod tests { assert_eq!( devm_request_irq( &device, - IrqResource::new(9, IrqTriggerMode::EdgeRising), + IrqResource::new(9, IrqTrigger::EdgeRising), Arc::new(|| IrqEvent::NOT_HANDLED), ) .unwrap_err(), diff --git a/drivers/kdevice/src/device/resource.rs b/drivers/kdevice/src/device/resource.rs index de9335699..3bb7855c0 100644 --- a/drivers/kdevice/src/device/resource.rs +++ b/drivers/kdevice/src/device/resource.rs @@ -10,18 +10,16 @@ //! OS-neutral crate without coupling it to `khal`. pub use device_res::{ - DmaSpec, IoPortRange, IrqResource, IrqTriggerMode, MmioRegion, ResourceDesc, ResourceSet, + DmaSpec, IoPortRange, IrqResource, IrqTrigger, MmioRegion, ResourceDesc, ResourceSet, }; -/// Convert a firmware-described interrupt trigger into an [`IrqTriggerMode`]. -pub fn irq_trigger_from_firmware( - trigger: khal::firmware::devices::InterruptTrigger, -) -> IrqTriggerMode { +/// Convert a firmware-described interrupt trigger into an [`IrqTrigger`]. +pub fn irq_trigger_from_firmware(trigger: khal::firmware::devices::InterruptTrigger) -> IrqTrigger { match trigger { - khal::firmware::devices::InterruptTrigger::EdgeRising => IrqTriggerMode::EdgeRising, - khal::firmware::devices::InterruptTrigger::EdgeFalling => IrqTriggerMode::EdgeFalling, - khal::firmware::devices::InterruptTrigger::LevelHigh => IrqTriggerMode::LevelHigh, - khal::firmware::devices::InterruptTrigger::LevelLow => IrqTriggerMode::LevelLow, - khal::firmware::devices::InterruptTrigger::Unknown(f) => IrqTriggerMode::Unknown(f), + khal::firmware::devices::InterruptTrigger::EdgeRising => IrqTrigger::EdgeRising, + khal::firmware::devices::InterruptTrigger::EdgeFalling => IrqTrigger::EdgeFalling, + khal::firmware::devices::InterruptTrigger::LevelHigh => IrqTrigger::LevelHigh, + khal::firmware::devices::InterruptTrigger::LevelLow => IrqTrigger::LevelLow, + khal::firmware::devices::InterruptTrigger::Unknown(f) => IrqTrigger::Unknown(f), } } diff --git a/drivers/kdevice/src/lib.rs b/drivers/kdevice/src/lib.rs index 1f5512993..6031147b7 100644 --- a/drivers/kdevice/src/lib.rs +++ b/drivers/kdevice/src/lib.rs @@ -71,7 +71,7 @@ pub use device::{ handles::{BusHandle, DeviceCore, DriverCore}, object::{DeviceObject, DeviceUse}, resource::{ - DmaSpec, IoPortRange, IrqResource, IrqTriggerMode, MmioRegion, ResourceDesc, ResourceSet, + DmaSpec, IoPortRange, IrqResource, IrqTrigger, MmioRegion, ResourceDesc, ResourceSet, irq_trigger_from_firmware, }, }; diff --git a/drivers/virtio/src/net.rs b/drivers/virtio/src/net.rs index d9d1bf81e..84ea2050e 100644 --- a/drivers/virtio/src/net.rs +++ b/drivers/virtio/src/net.rs @@ -6,7 +6,7 @@ use alloc::{collections::BTreeSet, sync::Arc, vec::Vec}; use core::sync::atomic::{AtomicUsize, Ordering}; -use device_res::{Irq, IrqEvent, IrqEventSource, IrqResource, IrqTriggerMode, try_irq_provider}; +use device_res::{Irq, IrqEvent, IrqEventSource, IrqResource, IrqTrigger, try_irq_provider}; use driver_base::{Device, DeviceKind, DriverError, DriverResult}; use driver_net::{MacAddress, NetBuf, NetBufBox, NetBufHandle, NetBufPool, NetDevice}; use kspin::{SpinNoIrq, SpinNoPreempt}; @@ -86,7 +86,7 @@ fn register_virtio_net_irq NET_IRQ_GUARDS.lock().push(guard), Err(_) => { @@ -109,7 +109,7 @@ fn unregister_virtio_net_irq(irq: usize, handle_id: usize) { if !irq_still_used { REGISTERED_NET_IRQS.lock().remove(&irq); if let Some(p) = try_irq_provider() { - p.release_irq(IrqResource::new(irq, IrqTriggerMode::Unknown(0))); + p.release_irq(IrqResource::new(irq, IrqTrigger::Unknown(0))); } } } diff --git a/drivers/virtio/src/pci.rs b/drivers/virtio/src/pci.rs index df04a46fb..196deb689 100644 --- a/drivers/virtio/src/pci.rs +++ b/drivers/virtio/src/pci.rs @@ -5,7 +5,7 @@ //! VirtIO PCI transport integration. #[cfg(not(target_arch = "x86_64"))] -use device_res::{IrqController, IrqRouteDesc, IrqTriggerMode, irq_provider}; +use device_res::{IrqController, IrqRouteDesc, IrqTrigger, irq_provider}; use driver_base::DeviceKind; use pci::PciConfigAccess; #[cfg(not(target_arch = "x86_64"))] @@ -266,13 +266,13 @@ pub fn probe_pci_device( } #[cfg(not(target_arch = "x86_64"))] -fn fw_trigger_to_mode(t: InterruptTrigger) -> IrqTriggerMode { +fn fw_trigger_to_mode(t: InterruptTrigger) -> IrqTrigger { match t { - InterruptTrigger::EdgeRising => IrqTriggerMode::EdgeRising, - InterruptTrigger::EdgeFalling => IrqTriggerMode::EdgeFalling, - InterruptTrigger::LevelHigh => IrqTriggerMode::LevelHigh, - InterruptTrigger::LevelLow => IrqTriggerMode::LevelLow, - InterruptTrigger::Unknown(f) => IrqTriggerMode::Unknown(f), + InterruptTrigger::EdgeRising => IrqTrigger::EdgeRising, + InterruptTrigger::EdgeFalling => IrqTrigger::EdgeFalling, + InterruptTrigger::LevelHigh => IrqTrigger::LevelHigh, + InterruptTrigger::LevelLow => IrqTrigger::LevelLow, + InterruptTrigger::Unknown(f) => IrqTrigger::Unknown(f), } } @@ -316,7 +316,7 @@ fn legacy_irq_for_bdf(config: &PciConfigAccess, bdf: DeviceFunction) -> Option { let desc = IrqRouteDesc { hwirq: 0x23 + (bdf.device & 3) as usize, - trigger: IrqTriggerMode::LevelHigh, + trigger: IrqTrigger::LevelHigh, controller: IrqController::Gic, domain: None, }; @@ -325,7 +325,7 @@ fn legacy_irq_for_bdf(config: &PciConfigAccess, bdf: DeviceFunction) -> Option { let desc = IrqRouteDesc { hwirq: 0x20 + (bdf.device & 3) as usize, - trigger: IrqTriggerMode::LevelHigh, + trigger: IrqTrigger::LevelHigh, controller: IrqController::Plic, domain: None, }; diff --git a/drivers/virtio/src/pci/x86_64.rs b/drivers/virtio/src/pci/x86_64.rs index 83e44a757..fe49667fd 100644 --- a/drivers/virtio/src/pci/x86_64.rs +++ b/drivers/virtio/src/pci/x86_64.rs @@ -2,7 +2,7 @@ // Copyright 2025 KylinSoft Co., Ltd. // See LICENSES for license details. -use device_res::{IrqController, IrqRouteDesc, IrqTriggerMode, irq_provider, try_irq_provider}; +use device_res::{IrqController, IrqRouteDesc, IrqTrigger, irq_provider, try_irq_provider}; use pci::{ PciConfigAccess, msix::{self, MsixCapability, MsixTable, MsixTableEntry, PCI_BAR_COUNT}, @@ -438,7 +438,7 @@ pub(super) fn legacy_irq_for_bdf(config: &PciConfigAccess, bdf: DeviceFunction) let desc = IrqRouteDesc { hwirq: irq_line, - trigger: IrqTriggerMode::LevelLow, + trigger: IrqTrigger::LevelLow, controller: IrqController::IoApic, domain: None, }; diff --git a/platforms/x86_64-qemu-virt/src/init.rs b/platforms/x86_64-qemu-virt/src/init.rs index 6e6dc9b6c..a133fc496 100644 --- a/platforms/x86_64-qemu-virt/src/init.rs +++ b/platforms/x86_64-qemu-virt/src/init.rs @@ -36,7 +36,7 @@ impl BootHandler for BootHandlerImpl { Some( khal::irq::IrqDesc::new(4, khal::irq::IrqTrigger::EdgeRising) .with_source(khal::irq::IrqSource::PlatformStatic) - .with_controller(khal::irq::IrqControllerKind::IoApic) + .with_controller(khal::irq::IrqController::IoApic) .with_domain(khal::irq::IO_APIC_DOMAIN), ), ); -- Gitee From 5d3d42bbbfb35037e8b3e2a9b0c68278e2cc24fa Mon Sep 17 00:00:00 2001 From: xiaohui Date: Mon, 13 Jul 2026 14:16:54 +0800 Subject: [PATCH 3/3] fixup: fix the review Signed-off-by: xiaohui --- arch/khal/src/irq/desc.rs | 2 +- arch/khal/src/irq/manager.rs | 27 ++++++++---------- arch/khal/src/irq/mod.rs | 5 ++-- core/kruntime/src/lib.rs | 50 +++++++++++++++++++++------------ drivers/kdriver/src/resource.rs | 18 ++++-------- drivers/virtio/src/pci.rs | 4 +-- 6 files changed, 56 insertions(+), 50 deletions(-) diff --git a/arch/khal/src/irq/desc.rs b/arch/khal/src/irq/desc.rs index 6971c5e89..e94b41ca4 100644 --- a/arch/khal/src/irq/desc.rs +++ b/arch/khal/src/irq/desc.rs @@ -10,7 +10,7 @@ use bitflags::bitflags; // import path for IRQ types. The composite `IrqDesc` and the arch-core state // it carries (polarity / source / affinity / flags / the virq namespace) stay // in `khal`. -pub use device_res::{IrqController, IrqDomainId, IrqTrigger}; +pub use device_res::{IrqController, IrqDomainId, IrqEvent, IrqHandler, IrqTrigger}; /// OS-visible logical interrupt number managed by `khal::irq`. pub type Virq = usize; diff --git a/arch/khal/src/irq/manager.rs b/arch/khal/src/irq/manager.rs index 1021667f2..6433e978e 100644 --- a/arch/khal/src/irq/manager.rs +++ b/arch/khal/src/irq/manager.rs @@ -16,17 +16,15 @@ use kspin::SpinNoIrq; #[cfg(feature = "ipi")] pub use self::TargetCpu as IpiTarget; use super::{ - Hwirq, IntoIrqDesc, IrqAffinity, IrqController, IrqDesc, IrqDomainId, IrqPolarity, IrqSource, - IrqTrigger, Virq, + Hwirq, IntoIrqDesc, IrqAffinity, IrqController, IrqDesc, IrqDomainId, IrqHandler, IrqPolarity, + IrqSource, IrqTrigger, Virq, }; /// IRQ handler invoked on dispatch. /// -/// Carried as an `Arc` so each registration owns its -/// own device context (the Rust-native counterpart of Linux's `dev_id`), -/// instead of a bare `fn()` that has to recover context indirectly through a -/// side table. -pub type Handler = Arc; +/// Each registration owns its `Arc` — the Rust-native +/// counterpart of Linux's `dev_id` — with no side table or trampoline. +pub type Handler = Arc; /// Target CPU(s) for inter-processor interrupts. pub enum TargetCpu { @@ -367,7 +365,7 @@ fn dispatch_subscribers(virq: Virq) { let has_regular_handler = regular_handler.is_some(); if let Some(handler) = regular_handler { - handler(); + let _ = handler.handle(); } if let Some(wake_subscription) = wake_subscription { @@ -419,15 +417,13 @@ pub fn descriptor(virq: Virq) -> Option { /// Register the regular OS IRQ handler for an IRQ line. /// -/// Accepts any non-capturing or capturing `Fn()` closure (or `fn` item): it is -/// boxed into the erased [`Handler`] (`Arc`) internally, -/// so each registration carries its own context — the Rust-native counterpart of -/// Linux's `dev_id` — with no side table. +/// Each registration carries its own `Arc` — the Rust-native +/// counterpart of Linux's `dev_id` — with no side table or trampoline. /// /// This is different from wakeup subscription: the registered handler is invoked /// directly on dispatch, while wakeup subscribers only participate in the wakeup /// notification path. -pub fn register(desc: impl IntoIrqDesc, handler: F) -> bool { +pub fn register(desc: impl IntoIrqDesc, handler: Handler) -> bool { let mut state = IRQ_STATE.lock(); let desc = state.resolve_desc(desc.into_irq_desc()); let virq = desc.logical_irq().unwrap(); @@ -439,7 +435,6 @@ pub fn register(desc: impl IntoIrqDesc, handler warn!("register handler for IRQ {virq} failed"); return false; } - let handler: Handler = Arc::new(handler); entry.handler = Some(handler); let desc = entry.desc; drop(state); @@ -566,6 +561,7 @@ pub mod tests_irq { use alloc::sync::Arc; use core::sync::atomic::{AtomicUsize, Ordering}; + use device_res::IrqEvent; use unittest::def_test; use super::{ @@ -576,8 +572,9 @@ pub mod tests_irq { static REGULAR_CALLS: AtomicUsize = AtomicUsize::new(0); static WAKE_CALLS: AtomicUsize = AtomicUsize::new(0); - fn test_handler() { + fn test_handler() -> IrqEvent { REGULAR_CALLS.fetch_add(1, Ordering::Relaxed); + IrqEvent::HANDLED } fn test_wake_handler(_irq: usize) { diff --git a/arch/khal/src/irq/mod.rs b/arch/khal/src/irq/mod.rs index 570bbb02d..b20cd57c9 100644 --- a/arch/khal/src/irq/mod.rs +++ b/arch/khal/src/irq/mod.rs @@ -9,7 +9,8 @@ mod manager; pub use desc::{ GIC_ROOT_DOMAIN, Hwirq, IO_APIC_DOMAIN, IntoIrqDesc, IrqAffinity, IrqController, IrqDesc, - IrqDomainId, IrqFlags, IrqPolarity, IrqSource, IrqTrigger, PLIC_ROOT_DOMAIN, Virq, - gic_edge_irq_desc, gic_irq_desc, gic_level_irq_desc, io_apic_irq_desc, plic_irq_desc, + IrqDomainId, IrqEvent, IrqFlags, IrqHandler, IrqPolarity, IrqSource, IrqTrigger, + PLIC_ROOT_DOMAIN, Virq, gic_edge_irq_desc, gic_irq_desc, gic_level_irq_desc, io_apic_irq_desc, + plic_irq_desc, }; pub use manager::*; diff --git a/core/kruntime/src/lib.rs b/core/kruntime/src/lib.rs index f72fce69c..fdd73599a 100644 --- a/core/kruntime/src/lib.rs +++ b/core/kruntime/src/lib.rs @@ -24,6 +24,8 @@ #![cfg_attr(not(test), no_std)] +extern crate alloc; + #[macro_use] extern crate klogger; @@ -476,28 +478,40 @@ fn init_interrupt() { khal::time::arm_timer(deadline); } - khal::irq::register(khal::time::interrupt_id(), || { - let now_ns = khal::time::monotonic_time_nanos(); - update_timer(now_ns); - ktask::on_timer_tick(); - }); + khal::irq::register( + khal::time::interrupt_id(), + alloc::sync::Arc::new(|| { + let now_ns = khal::time::monotonic_time_nanos(); + update_timer(now_ns); + ktask::on_timer_tick(); + khal::irq::IrqEvent::HANDLED + }), + ); #[cfg(feature = "ipi")] - khal::irq::register(kbuild_config::IPI_IRQ, || { - #[cfg(feature = "arm-timer-resume-fixup")] - timer_driver::arm_generic::handle_ipi_fixup(); - #[cfg(feature = "ipi")] - kipi::ipi_handler(); - }); + khal::irq::register( + kbuild_config::IPI_IRQ, + alloc::sync::Arc::new(|| { + #[cfg(feature = "arm-timer-resume-fixup")] + timer_driver::arm_generic::handle_ipi_fixup(); + #[cfg(feature = "ipi")] + kipi::ipi_handler(); + khal::irq::IrqEvent::HANDLED + }), + ); #[cfg(feature = "pmu")] - khal::irq::register(kbuild_config::PMU_IRQ, || { - debug!( - "PMU interrupt received on cpu {}", - khal::percpu::this_cpu_id().as_usize() - ); - khal::pmu::dispatch_irq_overflows(); - }); + khal::irq::register( + kbuild_config::PMU_IRQ, + alloc::sync::Arc::new(|| { + debug!( + "PMU interrupt received on cpu {}", + khal::percpu::this_cpu_id().as_usize() + ); + khal::pmu::dispatch_irq_overflows(); + khal::irq::IrqEvent::HANDLED + }), + ); // Enable IRQs before starting app karch::enable_local_irq(); diff --git a/drivers/kdriver/src/resource.rs b/drivers/kdriver/src/resource.rs index 81cd9907e..3ff09c3b4 100644 --- a/drivers/kdriver/src/resource.rs +++ b/drivers/kdriver/src/resource.rs @@ -29,10 +29,9 @@ struct HostResourceProvider; static HOST_PROVIDER: HostResourceProvider = HostResourceProvider; -// Device IRQ handlers are registered with `khal::irq` as closures that capture -// their `Arc` directly — the Rust-native counterpart of Linux's -// `dev_id`. No slot table or per-slot trampoline is needed: the closure owns its -// context, and unregistering the line drops the closure (and thus the handler). +// Device IRQ handlers are registered with `khal::irq` as `Arc` +// directly — the Rust-native counterpart of Linux's `dev_id`. No slot table, +// trampoline, or wrapper closure is needed. impl MmioOp for HostResourceProvider { fn map_mmio(&self, region: MmioRegion, name: &'static str) -> ResResult { @@ -120,12 +119,9 @@ impl DmaOp for HostResourceProvider { impl IrqOp for HostResourceProvider { fn request_irq(&self, irq: IrqResource, handler: Arc) -> ResResult<()> { - // The closure captures the `Arc`, so `khal::irq` stores a - // self-contained handler with no side table. The handler is in place - // before the line is enabled inside `register`. - if khal::irq::register(irq.number, move || { - let _ = handler.handle(); - }) { + // `khal::irq::register` stores the `Arc` directly — + // no wrapper closure, no side table, no trampoline. + if khal::irq::register(irq.number, handler) { Ok(()) } else { Err(ResError::Busy) @@ -133,8 +129,6 @@ impl IrqOp for HostResourceProvider { } fn release_irq(&self, irq: IrqResource) { - // Dropping the stored `Arc` releases the `Arc` - // it captured. let _ = khal::irq::unregister(irq.number); } diff --git a/drivers/virtio/src/pci.rs b/drivers/virtio/src/pci.rs index 196deb689..6327da550 100644 --- a/drivers/virtio/src/pci.rs +++ b/drivers/virtio/src/pci.rs @@ -266,7 +266,7 @@ pub fn probe_pci_device( } #[cfg(not(target_arch = "x86_64"))] -fn fw_trigger_to_mode(t: InterruptTrigger) -> IrqTrigger { +fn fw_trigger_to_kind(t: InterruptTrigger) -> IrqTrigger { match t { InterruptTrigger::EdgeRising => IrqTrigger::EdgeRising, InterruptTrigger::EdgeFalling => IrqTrigger::EdgeFalling, @@ -294,7 +294,7 @@ fn legacy_irq_for_bdf(config: &PciConfigAccess, bdf: DeviceFunction) -> Option