From e6901808a3b28d8bdabfa98a618b2eab6f8798e8 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 15 Oct 2025 20:14:31 +0200 Subject: rust: pci: move IRQ infrastructure to separate file Move the PCI interrupt infrastructure to a separate sub-module in order to keep things organized. Signed-off-by: Danilo Krummrich --- rust/kernel/pci/irq.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 244 insertions(+) create mode 100644 rust/kernel/pci/irq.rs (limited to 'rust/kernel/pci/irq.rs') diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs new file mode 100644 index 000000000000..77235c271876 --- /dev/null +++ b/rust/kernel/pci/irq.rs @@ -0,0 +1,244 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI interrupt infrastructure. + +use super::Device; +use crate::{ + bindings, device, + device::Bound, + devres, + error::{to_result, Result}, + irq::{self, IrqRequest}, + str::CStr, + sync::aref::ARef, +}; +use core::ops::RangeInclusive; +use kernel::prelude::*; + +/// IRQ type flags for PCI interrupt allocation. +#[derive(Debug, Clone, Copy)] +pub enum IrqType { + /// INTx interrupts. + Intx, + /// Message Signaled Interrupts (MSI). + Msi, + /// Extended Message Signaled Interrupts (MSI-X). + MsiX, +} + +impl IrqType { + /// Convert to the corresponding kernel flags. + const fn as_raw(self) -> u32 { + match self { + IrqType::Intx => bindings::PCI_IRQ_INTX, + IrqType::Msi => bindings::PCI_IRQ_MSI, + IrqType::MsiX => bindings::PCI_IRQ_MSIX, + } + } +} + +/// Set of IRQ types that can be used for PCI interrupt allocation. +#[derive(Debug, Clone, Copy, Default)] +pub struct IrqTypes(u32); + +impl IrqTypes { + /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). + pub const fn all() -> Self { + Self(bindings::PCI_IRQ_ALL_TYPES) + } + + /// Build a set of IRQ types. + /// + /// # Examples + /// + /// ```ignore + /// // Create a set with only MSI and MSI-X (no legacy interrupts). + /// let msi_only = IrqTypes::default() + /// .with(IrqType::Msi) + /// .with(IrqType::MsiX); + /// ``` + pub const fn with(self, irq_type: IrqType) -> Self { + Self(self.0 | irq_type.as_raw()) + } + + /// Get the raw flags value. + const fn as_raw(self) -> u32 { + self.0 + } +} + +/// Represents an allocated IRQ vector for a specific PCI device. +/// +/// This type ties an IRQ vector to the device it was allocated for, +/// ensuring the vector is only used with the correct device. +#[derive(Clone, Copy)] +pub struct IrqVector<'a> { + dev: &'a Device, + index: u32, +} + +impl<'a> IrqVector<'a> { + /// Creates a new [`IrqVector`] for the given device and index. + /// + /// # Safety + /// + /// - `index` must be a valid IRQ vector index for `dev`. + /// - `dev` must point to a [`Device`] that has successfully allocated IRQ vectors. + unsafe fn new(dev: &'a Device, index: u32) -> Self { + Self { dev, index } + } + + /// Returns the raw vector index. + fn index(&self) -> u32 { + self.index + } +} + +impl<'a> TryInto> for IrqVector<'a> { + type Error = Error; + + fn try_into(self) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. + let irq = unsafe { bindings::pci_irq_vector(self.dev.as_raw(), self.index()) }; + if irq < 0 { + return Err(crate::error::Error::from_errno(irq)); + } + // SAFETY: `irq` is guaranteed to be a valid IRQ number for `&self`. + Ok(unsafe { IrqRequest::new(self.dev.as_ref(), irq as u32) }) + } +} + +/// Represents an IRQ vector allocation for a PCI device. +/// +/// This type ensures that IRQ vectors are properly allocated and freed by +/// tying the allocation to the lifetime of this registration object. +/// +/// # Invariants +/// +/// The [`Device`] has successfully allocated IRQ vectors. +struct IrqVectorRegistration { + dev: ARef, +} + +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, + min_vecs: u32, + max_vecs: u32, + irq_types: IrqTypes, + ) -> Result>> { + // 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 { + /// 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 + 'a, + ) -> Result, Error> + 'a> { + let request = vector.try_into()?; + + Ok(irq::Registration::::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 + 'a, + ) -> Result, Error> + 'a> { + let request = vector.try_into()?; + + Ok(irq::ThreadedRegistration::::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 legacy 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 legacy interrupts. + /// + /// The allocated vectors are automatically freed when the device is unbound, using the + /// devres (device resource management) system. + /// + /// # Arguments + /// + /// * `min_vecs` - Minimum number of vectors required. + /// * `max_vecs` - Maximum number of vectors to allocate. + /// * `irq_types` - Types of interrupts that can be used. + /// + /// # Returns + /// + /// Returns a range of IRQ vectors that were successfully allocated, or an error if the + /// allocation fails or cannot meet the minimum requirement. + /// + /// # Examples + /// + /// ``` + /// # use kernel::{ device::Bound, pci}; + /// # fn no_run(dev: &pci::Device) -> Result { + /// // Allocate using any available interrupt type in the order mentioned above. + /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; + /// + /// // Allocate MSI or MSI-X only (no legacy interrupts). + /// let msi_only = pci::IrqTypes::default() + /// .with(pci::IrqType::Msi) + /// .with(pci::IrqType::MsiX); + /// let vectors = dev.alloc_irq_vectors(4, 16, msi_only)?; + /// # Ok(()) + /// # } + /// ``` + pub fn alloc_irq_vectors( + &self, + min_vecs: u32, + max_vecs: u32, + irq_types: IrqTypes, + ) -> Result>> { + IrqVectorRegistration::register(self, min_vecs, max_vecs, irq_types) + } +} -- cgit From c7f6d5380f6ef5c328b63807c4e9b73eaef9aeda Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Mon, 20 Oct 2025 17:02:22 +0000 Subject: rust: pci: refer to legacy as INTx interrupts Consistently use INTx, as in the description of IrqType::Intx, to refer to the four legacy PCI interrupts, INTA#, INTB#, INTC#, and INTD#. Link: https://lore.kernel.org/rust-for-linux/20251015230209.GA960343@bhelgaas/ Link: https://github.com/Rust-for-Linux/linux/issues/1196 Suggested-by: Bjorn Helgaas Signed-off-by: Peter Colberg Signed-off-by: Danilo Krummrich --- rust/kernel/pci/irq.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'rust/kernel/pci/irq.rs') diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs index 77235c271876..782a524fe11c 100644 --- a/rust/kernel/pci/irq.rs +++ b/rust/kernel/pci/irq.rs @@ -42,7 +42,7 @@ impl IrqType { pub struct IrqTypes(u32); impl IrqTypes { - /// Create a set containing all IRQ types (MSI-X, MSI, and Legacy). + /// Create a set containing all IRQ types (MSI-X, MSI, and INTx). pub const fn all() -> Self { Self(bindings::PCI_IRQ_ALL_TYPES) } @@ -52,7 +52,7 @@ impl IrqTypes { /// # Examples /// /// ```ignore - /// // Create a set with only MSI and MSI-X (no legacy interrupts). + /// // Create a set with only MSI and MSI-X (no INTx interrupts). /// let msi_only = IrqTypes::default() /// .with(IrqType::Msi) /// .with(IrqType::MsiX); @@ -199,9 +199,9 @@ impl Device { /// 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 legacy interrupts based on the `irq_types` + /// 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 legacy interrupts. + /// will try them in order of preference: MSI-X first, then MSI, then INTx interrupts. /// /// The allocated vectors are automatically freed when the device is unbound, using the /// devres (device resource management) system. @@ -225,7 +225,7 @@ impl Device { /// // Allocate using any available interrupt type in the order mentioned above. /// let vectors = dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())?; /// - /// // Allocate MSI or MSI-X only (no legacy interrupts). + /// // Allocate MSI or MSI-X only (no INTx interrupts). /// let msi_only = pci::IrqTypes::default() /// .with(pci::IrqType::Msi) /// .with(pci::IrqType::MsiX); -- cgit From 1f7b01661f5f62211ca079afee922e1b44e3ae5d Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Mon, 3 Nov 2025 21:30:12 +0100 Subject: rust: pci: get rid of redundant Result in IRQ methods Currently request_irq() returns Result, Error> + 'a> which may carry an error in the Result or the initializer; the same is true for request_threaded_irq(). Use pin_init::pin_init_scope() to get rid of this redundancy. Reviewed-by: Alice Ryhl Link: https://patch.msgid.link/20251103203053.2348783-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci/irq.rs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) (limited to 'rust/kernel/pci/irq.rs') diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs index 782a524fe11c..03f2de559a8a 100644 --- a/rust/kernel/pci/irq.rs +++ b/rust/kernel/pci/irq.rs @@ -175,10 +175,12 @@ impl Device { flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, - ) -> Result, Error> + 'a> { - let request = vector.try_into()?; + ) -> impl PinInit, Error> + 'a { + pin_init::pin_init_scope(move || { + let request = vector.try_into()?; - Ok(irq::Registration::::new(request, flags, name, handler)) + Ok(irq::Registration::::new(request, flags, name, handler)) + }) } /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. @@ -188,12 +190,14 @@ impl Device { flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, - ) -> Result, Error> + 'a> { - let request = vector.try_into()?; - - Ok(irq::ThreadedRegistration::::new( - request, flags, name, handler, - )) + ) -> impl PinInit, Error> + 'a { + pin_init::pin_init_scope(move || { + let request = vector.try_into()?; + + Ok(irq::ThreadedRegistration::::new( + request, flags, name, handler, + )) + }) } /// Allocate IRQ vectors for this PCI device with automatic cleanup. -- cgit From d8407396f128d8bf4d06282b636df3f26db208c1 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 5 Nov 2025 13:03:28 +0100 Subject: rust: pci: use "kernel vertical" style for imports Convert all imports in the PCI Rust module to use "kernel vertical" style. With this subsequent patches neither introduce unrelated changes nor leave an inconsistent import pattern. While at it, drop unnecessary imports covered by prelude::*. Link: https://docs.kernel.org/rust/coding-guidelines.html#imports Reviewed-by: Zhi Wang Link: https://patch.msgid.link/20251105120352.77603-1-dakr@kernel.org Signed-off-by: Danilo Krummrich --- rust/kernel/pci/irq.rs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'rust/kernel/pci/irq.rs') diff --git a/rust/kernel/pci/irq.rs b/rust/kernel/pci/irq.rs index 03f2de559a8a..d9230e105541 100644 --- a/rust/kernel/pci/irq.rs +++ b/rust/kernel/pci/irq.rs @@ -4,16 +4,20 @@ use super::Device; use crate::{ - bindings, device, + bindings, + device, device::Bound, devres, - error::{to_result, Result}, - irq::{self, IrqRequest}, + error::to_result, + irq::{ + self, + IrqRequest, // + }, + prelude::*, str::CStr, - sync::aref::ARef, + sync::aref::ARef, // }; use core::ops::RangeInclusive; -use kernel::prelude::*; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] -- cgit