From 340ccc973544a6e7e331729bc4944603085cafab Mon Sep 17 00:00:00 2001 From: Joel Fernandes Date: Wed, 8 Oct 2025 15:07:26 -0400 Subject: rust: pci: Allocate and manage PCI interrupt vectors Add support to PCI rust module to allocate, free and manage IRQ vectors. Integrate with devres for managing the allocated resources. Signed-off-by: Joel Fernandes [ Add links in doc-comments; add missing invariant comment; re-format multiple safety requirements as list and fix missing backticks; refactor the example of alloc_irq_vectors() to compile. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 214 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 201 insertions(+), 13 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 7fcc5f6022c1..d91ec9f008ae 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -6,8 +6,9 @@ use crate::{ bindings, container_of, device, + device::Bound, device_id::{RawDeviceId, RawDeviceIdIndex}, - devres::Devres, + devres::{self, Devres}, driver, error::{from_result, to_result, Result}, io::{Io, IoRaw}, @@ -19,7 +20,7 @@ use crate::{ }; use core::{ marker::PhantomData, - ops::Deref, + ops::{Deref, RangeInclusive}, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; @@ -28,6 +29,58 @@ mod id; pub use self::id::{Class, ClassMask, Vendor}; +/// 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 + } +} + /// An adapter for the registration of PCI drivers. pub struct Adapter(T); @@ -516,6 +569,92 @@ impl Device { } } +/// 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 + } +} + +/// 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 { /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks /// can be performed on compile time for offsets (plus the requested type size) < SIZE. @@ -536,10 +675,15 @@ impl Device { self.iomap_region_sized::<0>(bar, name) } - /// Returns an [`IrqRequest`] for the IRQ vector at the given index, if any. - pub fn irq_vector(&self, index: u32) -> Result> { + /// Returns an [`IrqRequest`] for the given IRQ vector. + pub fn irq_vector(&self, vector: IrqVector<'_>) -> Result> { + // Verify that the vector belongs to this device. + if !core::ptr::eq(vector.dev.as_raw(), self.as_raw()) { + return Err(EINVAL); + } + // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. - let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), index) }; + let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), vector.index()) }; if irq < 0 { return Err(crate::error::Error::from_errno(irq)); } @@ -547,35 +691,79 @@ impl Device { Ok(unsafe { IrqRequest::new(self.as_ref(), irq as u32) }) } - /// Returns a [`kernel::irq::Registration`] for the IRQ vector at the given - /// index. + /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn request_irq<'a, T: crate::irq::Handler + 'static>( &'a self, - index: u32, + vector: IrqVector<'_>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> Result, Error> + 'a> { - let request = self.irq_vector(index)?; + let request = self.irq_vector(vector)?; Ok(irq::Registration::::new(request, flags, name, handler)) } - /// Returns a [`kernel::irq::ThreadedRegistration`] for the IRQ vector at - /// the given index. + /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ vector. pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'static>( &'a self, - index: u32, + vector: IrqVector<'_>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> Result, Error> + 'a> { - let request = self.irq_vector(index)?; + let request = self.irq_vector(vector)?; 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) + } } impl Device { -- cgit From 651692d32c21a9165db60a9bc74600074cd4ed99 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 15 Oct 2025 20:14:29 +0200 Subject: rust: pci: implement TryInto> for IrqVector<'a> Implement TryInto> for IrqVector<'a> to directly convert a pci::IrqVector into a generic IrqRequest, instead of taking the indirection via an unrelated pci::Device method. Reviewed-by: Alice Ryhl Reviewed-by: Joel Fernandes Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index d91ec9f008ae..c6b750047b2e 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -596,6 +596,20 @@ impl<'a> IrqVector<'a> { } } +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 @@ -675,31 +689,15 @@ impl Device { self.iomap_region_sized::<0>(bar, name) } - /// Returns an [`IrqRequest`] for the given IRQ vector. - pub fn irq_vector(&self, vector: IrqVector<'_>) -> Result> { - // Verify that the vector belongs to this device. - if !core::ptr::eq(vector.dev.as_raw(), self.as_raw()) { - return Err(EINVAL); - } - - // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_dev`. - let irq = unsafe { crate::bindings::pci_irq_vector(self.as_raw(), vector.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.as_ref(), irq as u32) }) - } - /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn request_irq<'a, T: crate::irq::Handler + 'static>( &'a self, - vector: IrqVector<'_>, + vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> Result, Error> + 'a> { - let request = self.irq_vector(vector)?; + let request = vector.try_into()?; Ok(irq::Registration::::new(request, flags, name, handler)) } @@ -707,12 +705,12 @@ impl Device { /// 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<'_>, + vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> Result, Error> + 'a> { - let request = self.irq_vector(vector)?; + let request = vector.try_into()?; Ok(irq::ThreadedRegistration::::new( request, flags, name, handler, -- cgit From 3c2e31d717ac89c59e35e98a7910a6daa9f15a06 Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Wed, 15 Oct 2025 20:14:30 +0200 Subject: rust: pci: move I/O infrastructure to separate file Move the PCI I/O infrastructure to a separate sub-module in order to keep things organized. Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 133 ++--------------------------------------------------- 1 file changed, 4 insertions(+), 129 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index c6b750047b2e..ed9d8619f944 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -8,10 +8,8 @@ use crate::{ bindings, container_of, device, device::Bound, device_id::{RawDeviceId, RawDeviceIdIndex}, - devres::{self, Devres}, - driver, + devres, driver, error::{from_result, to_result, Result}, - io::{Io, IoRaw}, irq::{self, IrqRequest}, str::CStr, sync::aref::ARef, @@ -20,14 +18,16 @@ use crate::{ }; use core::{ marker::PhantomData, - ops::{Deref, RangeInclusive}, + ops::RangeInclusive, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; mod id; +mod io; pub use self::id::{Class, ClassMask, Vendor}; +pub use self::io::Bar; /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] @@ -358,112 +358,6 @@ pub struct Device( PhantomData, ); -/// A PCI BAR to perform I/O-Operations on. -/// -/// # Invariants -/// -/// `Bar` always holds an `IoRaw` inststance that holds a valid pointer to the start of the I/O -/// memory mapped PCI bar and its size. -pub struct Bar { - pdev: ARef, - io: IoRaw, - num: i32, -} - -impl Bar { - fn new(pdev: &Device, num: u32, name: &CStr) -> Result { - let len = pdev.resource_len(num)?; - if len == 0 { - return Err(ENOMEM); - } - - // Convert to `i32`, since that's what all the C bindings use. - let num = i32::try_from(num)?; - - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `num` is checked for validity by a previous call to `Device::resource_len`. - // `name` is always valid. - let ret = unsafe { bindings::pci_request_region(pdev.as_raw(), num, name.as_char_ptr()) }; - if ret != 0 { - return Err(EBUSY); - } - - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `num` is checked for validity by a previous call to `Device::resource_len`. - // `name` is always valid. - let ioptr: usize = unsafe { bindings::pci_iomap(pdev.as_raw(), num, 0) } as usize; - if ioptr == 0 { - // SAFETY: - // `pdev` valid by the invariants of `Device`. - // `num` is checked for validity by a previous call to `Device::resource_len`. - unsafe { bindings::pci_release_region(pdev.as_raw(), num) }; - return Err(ENOMEM); - } - - let io = match IoRaw::new(ioptr, len as usize) { - Ok(io) => io, - Err(err) => { - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `ioptr` is guaranteed to be the start of a valid I/O mapped memory region. - // `num` is checked for validity by a previous call to `Device::resource_len`. - unsafe { Self::do_release(pdev, ioptr, num) }; - return Err(err); - } - }; - - Ok(Bar { - pdev: pdev.into(), - io, - num, - }) - } - - /// # Safety - /// - /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. - unsafe fn do_release(pdev: &Device, ioptr: usize, num: i32) { - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `ioptr` is valid by the safety requirements. - // `num` is valid by the safety requirements. - unsafe { - bindings::pci_iounmap(pdev.as_raw(), ioptr as *mut c_void); - bindings::pci_release_region(pdev.as_raw(), num); - } - } - - fn release(&self) { - // SAFETY: The safety requirements are guaranteed by the type invariant of `self.pdev`. - unsafe { Self::do_release(&self.pdev, self.io.addr(), self.num) }; - } -} - -impl Bar { - #[inline] - fn index_is_valid(index: u32) -> bool { - // A `struct pci_dev` owns an array of resources with at most `PCI_NUM_RESOURCES` entries. - index < bindings::PCI_NUM_RESOURCES - } -} - -impl Drop for Bar { - fn drop(&mut self) { - self.release(); - } -} - -impl Deref for Bar { - type Target = Io; - - fn deref(&self) -> &Self::Target { - // SAFETY: By the type invariant of `Self`, the MMIO range in `self.io` is properly mapped. - unsafe { Io::from_raw(&self.io) } - } -} - impl Device { #[inline] fn as_raw(&self) -> *mut bindings::pci_dev { @@ -670,25 +564,6 @@ impl Drop for IrqVectorRegistration { } impl Device { - /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks - /// can be performed on compile time for offsets (plus the requested type size) < SIZE. - pub fn iomap_region_sized<'a, const SIZE: usize>( - &'a self, - bar: u32, - name: &'a CStr, - ) -> impl PinInit>, Error> + 'a { - Devres::new(self.as_ref(), Bar::::new(self, bar, name)) - } - - /// Mapps an entire PCI-BAR after performing a region-request on it. - pub fn iomap_region<'a>( - &'a self, - bar: u32, - name: &'a CStr, - ) -> impl PinInit, Error> + 'a { - self.iomap_region_sized::<0>(bar, name) - } - /// Returns a [`kernel::irq::Registration`] for the given IRQ vector. pub fn request_irq<'a, T: crate::irq::Handler + 'static>( &'a self, -- cgit 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.rs | 236 +---------------------------------------------------- 1 file changed, 3 insertions(+), 233 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index ed9d8619f944..ce612c9b7b56 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -6,80 +6,26 @@ use crate::{ bindings, container_of, device, - device::Bound, device_id::{RawDeviceId, RawDeviceIdIndex}, - devres, driver, + driver, error::{from_result, to_result, Result}, - irq::{self, IrqRequest}, str::CStr, - sync::aref::ARef, types::Opaque, ThisModule, }; use core::{ marker::PhantomData, - ops::RangeInclusive, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; mod id; mod io; +mod irq; pub use self::id::{Class, ClassMask, Vendor}; pub use self::io::Bar; - -/// 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 - } -} +pub use self::irq::{IrqType, IrqTypes, IrqVector}; /// An adapter for the registration of PCI drivers. pub struct Adapter(T); @@ -463,182 +409,6 @@ impl Device { } } -/// 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) - } -} - impl Device { /// Enable memory resources for this device. pub fn enable_device_mem(&self) -> Result { -- cgit From 0242623384c767b1156b61b67894b4ecf6682b8b Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Thu, 16 Oct 2025 14:55:28 +0200 Subject: rust: driver: let probe() return impl PinInit The driver model defines the lifetime of the private data stored in (and owned by) a bus device to be valid from when the driver is bound to a device (i.e. from successful probe()) until the driver is unbound from the device. This is already taken care of by the Rust implementation of the driver model. However, we still ask drivers to return a Result>> from probe(). Unlike in C, where we do not have the concept of initializers, but rather deal with uninitialized memory, drivers can just return an impl PinInit instead. This contributes to more clarity to the fact that a driver returns it's device private data in probe() and the Rust driver model owns the data, manages the lifetime and - considering the lifetime - provides (safe) accessors for the driver. Hence, let probe() functions return an impl PinInit instead of Result>>. Reviewed-by: Alice Ryhl Acked-by: Viresh Kumar Reviewed-by: Alexandre Courbot Acked-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index ce612c9b7b56..0d5b4b394bbf 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -77,9 +77,9 @@ impl Adapter { let info = T::ID_TABLE.info(id.index()); from_result(|| { - let data = T::probe(pdev, info)?; + let data = T::probe(pdev, info); - pdev.as_ref().set_drvdata(data); + pdev.as_ref().set_drvdata(data)?; Ok(0) }) } @@ -248,7 +248,7 @@ macro_rules! pci_device_table { /// fn probe( /// _pdev: &pci::Device, /// _id_info: &Self::IdInfo, -/// ) -> Result>> { +/// ) -> impl PinInit { /// Err(ENODEV) /// } /// } @@ -271,7 +271,7 @@ pub trait Driver: Send { /// /// Called when a new pci device is added or discovered. Implementers should /// attempt to initialize the device here. - fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; + fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; /// PCI driver unbind. /// -- cgit From 26c1a20bf7ce76e9afe4030f25bec20e3c63dcf8 Mon Sep 17 00:00:00 2001 From: Peter Colberg Date: Mon, 20 Oct 2025 17:02:23 +0000 Subject: rust: pci: normalise spelling of PCI BAR Consistently refer to PCI base address register as PCI BAR. Fix spelling mistake "Mapps" -> "Maps". Link: https://lore.kernel.org/rust-for-linux/20251015225827.GA960157@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.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 0d5b4b394bbf..039a0d81d363 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -377,7 +377,7 @@ impl Device { unsafe { (*self.as_raw()).subsystem_device } } - /// Returns the start of the given PCI bar resource. + /// Returns the start of the given PCI BAR resource. pub fn resource_start(&self, bar: u32) -> Result { if !Bar::index_is_valid(bar) { return Err(EINVAL); @@ -389,7 +389,7 @@ impl Device { Ok(unsafe { bindings::pci_resource_start(self.as_raw(), bar.try_into()?) }) } - /// Returns the size of the given PCI bar resource. + /// Returns the size of the given PCI BAR resource. pub fn resource_len(&self, bar: u32) -> Result { if !Bar::index_is_valid(bar) { return Err(EINVAL); -- cgit From 6bbaa93912bfdfd5ffdc804275cc6a444c9400af Mon Sep 17 00:00:00 2001 From: Danilo Krummrich Date: Tue, 21 Oct 2025 00:34:23 +0200 Subject: rust: device: narrow the generic of drvdata_obtain() Let T be the actual private driver data type without the surrounding box, as it leaves less room for potential bugs. Reviewed-by: Alice Ryhl Reviewed-by: Greg Kroah-Hartman Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 039a0d81d363..b68ef4e575fc 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -94,7 +94,7 @@ impl Adapter { // SAFETY: `remove_callback` is only ever called after a successful call to // `probe_callback`, hence it's guaranteed that `Device::set_drvdata()` has been called // and stored a `Pin>`. - let data = unsafe { pdev.as_ref().drvdata_obtain::>>() }; + let data = unsafe { pdev.as_ref().drvdata_obtain::() }; T::unbind(pdev, data.as_ref()); } -- 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.rs | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index b68ef4e575fc..410b79d46632 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -5,27 +5,46 @@ //! C header: [`include/linux/pci.h`](srctree/include/linux/pci.h) use crate::{ - bindings, container_of, device, - device_id::{RawDeviceId, RawDeviceIdIndex}, + bindings, + container_of, + device, + device_id::{ + RawDeviceId, + RawDeviceIdIndex, // + }, driver, - error::{from_result, to_result, Result}, + error::{ + from_result, + to_result, // + }, + prelude::*, str::CStr, types::Opaque, - ThisModule, + ThisModule, // }; use core::{ marker::PhantomData, - ptr::{addr_of_mut, NonNull}, + ptr::{ + addr_of_mut, + NonNull, // + }, }; -use kernel::prelude::*; mod id; mod io; mod irq; -pub use self::id::{Class, ClassMask, Vendor}; +pub use self::id::{ + Class, + ClassMask, + Vendor, // +}; pub use self::io::Bar; -pub use self::irq::{IrqType, IrqTypes, IrqVector}; +pub use self::irq::{ + IrqType, + IrqTypes, + IrqVector, // +}; /// An adapter for the registration of PCI drivers. pub struct Adapter(T); -- cgit From e4addc7cc2dfcc19f1c8c8e47f3834b22cb21559 Mon Sep 17 00:00:00 2001 From: Markus Probst Date: Mon, 27 Oct 2025 20:06:03 +0000 Subject: rust: Add trait to convert a device reference to a bus device reference Implement the `AsBusDevice` trait for converting a `Device` reference to a bus device reference for all bus devices. The `AsBusDevice` trait allows abstractions to provide the bus device in class device callbacks. It must not be used by drivers and is intended for bus and class device abstractions only. Signed-off-by: Markus Probst Link: https://patch.msgid.link/20251027200547.1038967-2-markus.probst@posteo.de [ * Remove unused import. * Change visibility of AsBusDevice to public. * Fix build for USB. * Add impl for I2cClient. - Danilo ] Signed-off-by: Danilo Krummrich --- rust/kernel/pci.rs | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 410b79d46632..82e128431f08 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -24,6 +24,7 @@ use crate::{ }; use core::{ marker::PhantomData, + mem::offset_of, ptr::{ addr_of_mut, NonNull, // @@ -443,6 +444,12 @@ impl Device { } } +// SAFETY: `pci::Device` is a transparent wrapper of `struct pci_dev`. +// The offset is guaranteed to point to a valid device field inside `pci::Device`. +unsafe impl device::AsBusDevice for Device { + const OFFSET: usize = offset_of!(bindings::pci_dev, dev); +} + // SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic // argument. kernel::impl_device_context_deref!(unsafe { Device }); -- cgit