rust/kernel/pci/irq.rs

Source file repositories/reference/linux-study-clean/rust/kernel/pci/irq.rs

File Facts

System
Linux kernel
Corpus path
rust/kernel/pci/irq.rs
Extension
.rs
Size
8173 bytes
Lines
253
Domain
Rust Kernel Layer
Bucket
Rust API Membrane
Inferred role
Rust Kernel Layer: implementation source
Status
source implementation candidate

Why This File Exists

Rust-side wrappers and abstractions around kernel C APIs, ownership contracts, allocation, synchronization, and module integration.

Dependency Surface

Detected Declarations

Annotated Snippet

struct IrqVectorRegistration {
    dev: ARef<Device>,
}

impl IrqVectorRegistration {
    /// Allocate and register IRQ vectors for the given PCI device.
    ///
    /// Allocates IRQ vectors and registers them with devres for automatic cleanup.
    /// Returns a range of valid IRQ vectors.
    fn register<'a>(
        dev: &'a Device<Bound>,
        min_vecs: u32,
        max_vecs: u32,
        irq_types: IrqTypes,
    ) -> Result<RangeInclusive<IrqVector<'a>>> {
        // SAFETY:
        // - `dev.as_raw()` is guaranteed to be a valid pointer to a `struct pci_dev`
        //   by the type invariant of `Device`.
        // - `pci_alloc_irq_vectors` internally validates all other parameters
        //   and returns error codes.
        let ret = unsafe {
            bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_vecs, irq_types.as_raw())
        };

        to_result(ret)?;
        let count = ret as u32;

        // SAFETY:
        // - `pci_alloc_irq_vectors` returns the number of allocated vectors on success.
        // - Vectors are 0-based, so valid indices are [0, count-1].
        // - `pci_alloc_irq_vectors` guarantees `count >= min_vecs > 0`, so both `0` and
        //   `count - 1` are valid IRQ vector indices for `dev`.
        let range = unsafe { IrqVector::new(dev, 0)..=IrqVector::new(dev, count - 1) };

        // INVARIANT: The IRQ vector allocation for `dev` above was successful.
        let irq_vecs = Self { dev: dev.into() };
        devres::register(dev.as_ref(), irq_vecs, GFP_KERNEL)?;

        Ok(range)
    }
}

impl Drop for IrqVectorRegistration {
    fn drop(&mut self) {
        // SAFETY:
        // - By the type invariant, `self.dev.as_raw()` is a valid pointer to a `struct pci_dev`.
        // - `self.dev` has successfully allocated IRQ vectors.
        unsafe { bindings::pci_free_irq_vectors(self.dev.as_raw()) };
    }
}

impl Device<device::Bound> {
    /// Returns a [`kernel::irq::Registration`] for the given IRQ vector.
    pub fn request_irq<'a, T: crate::irq::Handler + 'static>(
        &'a self,
        vector: IrqVector<'a>,
        flags: irq::Flags,
        name: &'static CStr,
        handler: impl PinInit<T, Error> + 'a,
    ) -> impl PinInit<irq::Registration<T>, Error> + 'a {
        pin_init::pin_init_scope(move || {
            let request = vector.try_into()?;

            Ok(irq::Registration::<T>::new(request, flags, name, handler))
        })
    }

    /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector.
    pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>(
        &'a self,
        vector: IrqVector<'a>,
        flags: irq::Flags,
        name: &'static CStr,
        handler: impl PinInit<T, Error> + 'a,
    ) -> impl PinInit<irq::ThreadedRegistration<T>, Error> + 'a {
        pin_init::pin_init_scope(move || {
            let request = vector.try_into()?;

            Ok(irq::ThreadedRegistration::<T>::new(
                request, flags, name, handler,
            ))
        })
    }

    /// Allocate IRQ vectors for this PCI device with automatic cleanup.
    ///
    /// Allocates between `min_vecs` and `max_vecs` interrupt vectors for the device.
    /// The allocation will use MSI-X, MSI, or INTx interrupts based on the `irq_types`
    /// parameter and hardware capabilities. When multiple types are specified, the kernel
    /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts.

Annotation

Implementation Notes