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 v1.2.3 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 v1.2.3 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 ++--------------------------------------------- rust/kernel/pci/io.rs | 141 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 145 insertions(+), 129 deletions(-) create mode 100644 rust/kernel/pci/io.rs (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, diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs new file mode 100644 index 000000000000..65151a0a1a41 --- /dev/null +++ b/rust/kernel/pci/io.rs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! PCI memory-mapped I/O infrastructure. + +use super::Device; +use crate::{ + bindings, device, + devres::Devres, + io::{Io, IoRaw}, + str::CStr, + sync::aref::ARef, +}; +use core::ops::Deref; +use kernel::prelude::*; + +/// 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 { + pub(super) 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] + pub(super) 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 { + /// 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) + } +} -- cgit v1.2.3 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 +---------------------------------------------- rust/kernel/pci/irq.rs | 244 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 247 insertions(+), 233 deletions(-) create mode 100644 rust/kernel/pci/irq.rs (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 { 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 v1.2.3 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 --- drivers/cpufreq/rcpufreq_dt.rs | 4 +-- drivers/gpu/drm/nova/driver.rs | 4 +-- drivers/gpu/drm/tyr/driver.rs | 4 +-- drivers/gpu/nova-core/driver.rs | 34 +++++++++------------- rust/kernel/auxiliary.rs | 6 ++-- rust/kernel/cpufreq.rs | 4 +-- rust/kernel/device.rs | 9 ++++-- rust/kernel/driver.rs | 4 +-- rust/kernel/io/mem.rs | 8 +++--- rust/kernel/pci.rs | 8 +++--- rust/kernel/platform.rs | 12 ++++---- rust/kernel/usb.rs | 8 +++--- samples/rust/rust_debugfs.rs | 21 +++++++------- samples/rust/rust_dma.rs | 37 +++++++++++------------- samples/rust/rust_driver_auxiliary.rs | 25 ++++++----------- samples/rust/rust_driver_pci.rs | 53 +++++++++++++++++------------------ samples/rust/rust_driver_platform.rs | 6 ++-- samples/rust/rust_driver_usb.rs | 5 ++-- 18 files changed, 119 insertions(+), 133 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 53923b8ef7a1..31e07f0279db 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -207,9 +207,9 @@ impl platform::Driver for CPUFreqDTDriver { fn probe( pdev: &platform::Device, _id_info: Option<&Self::IdInfo>, - ) -> Result>> { + ) -> impl PinInit { cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; - Ok(KBox::new(Self {}, GFP_KERNEL)?.into()) + Ok(Self {}) } } diff --git a/drivers/gpu/drm/nova/driver.rs b/drivers/gpu/drm/nova/driver.rs index 91b7380f83ab..2246d8e104e0 100644 --- a/drivers/gpu/drm/nova/driver.rs +++ b/drivers/gpu/drm/nova/driver.rs @@ -45,13 +45,13 @@ impl auxiliary::Driver for NovaDriver { type IdInfo = (); const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> Result>> { + fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { let data = try_pin_init!(NovaData { adev: adev.into() }); let drm = drm::Device::::new(adev.as_ref(), data)?; drm::Registration::new_foreign_owned(&drm, adev.as_ref(), 0)?; - Ok(KBox::new(Self { drm }, GFP_KERNEL)?.into()) + Ok(Self { drm }) } } diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs index d5625dd1e41c..0389c558c036 100644 --- a/drivers/gpu/drm/tyr/driver.rs +++ b/drivers/gpu/drm/tyr/driver.rs @@ -103,7 +103,7 @@ impl platform::Driver for TyrDriver { fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, - ) -> Result>> { + ) -> impl PinInit { let core_clk = Clk::get(pdev.as_ref(), Some(c_str!("core")))?; let stacks_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("stacks")))?; let coregroup_clk = OptionalClk::get(pdev.as_ref(), Some(c_str!("coregroup")))?; @@ -143,7 +143,7 @@ impl platform::Driver for TyrDriver { let tdev: ARef = drm::Device::new(pdev.as_ref(), data)?; drm::driver::Registration::new_foreign_owned(&tdev, pdev.as_ref(), 0)?; - let driver = KBox::pin_init(try_pin_init!(TyrDriver { device: tdev }), GFP_KERNEL)?; + let driver = TyrDriver { device: tdev }; // We need this to be dev_info!() because dev_dbg!() does not work at // all in Rust for now, and we need to see whether probe succeeded. diff --git a/drivers/gpu/nova-core/driver.rs b/drivers/gpu/nova-core/driver.rs index edc72052e27a..a83b86199182 100644 --- a/drivers/gpu/nova-core/driver.rs +++ b/drivers/gpu/nova-core/driver.rs @@ -51,36 +51,28 @@ impl pci::Driver for NovaCore { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + pin_init::pin_init_scope(move || { + dev_dbg!(pdev.as_ref(), "Probe Nova Core GPU driver.\n"); - pdev.enable_device_mem()?; - pdev.set_master(); + pdev.enable_device_mem()?; + pdev.set_master(); - let devres_bar = Arc::pin_init( - pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), - GFP_KERNEL, - )?; + let bar = Arc::pin_init( + pdev.iomap_region_sized::(0, c_str!("nova-core/bar0")), + GFP_KERNEL, + )?; - // Used to provided a `&Bar0` to `Gpu::new` without tying it to the lifetime of - // `devres_bar`. - let bar_clone = Arc::clone(&devres_bar); - let bar = bar_clone.access(pdev.as_ref())?; - - let this = KBox::pin_init( - try_pin_init!(Self { - gpu <- Gpu::new(pdev, devres_bar, bar), + Ok(try_pin_init!(Self { + gpu <- Gpu::new(pdev, bar.clone(), bar.access(pdev.as_ref())?), _reg: auxiliary::Registration::new( pdev.as_ref(), c_str!("nova-drm"), 0, // TODO[XARR]: Once it lands, use XArray; for now we don't use the ID. crate::MODULE_NAME )?, - }), - GFP_KERNEL, - )?; - - Ok(this) + })) + }) } fn unbind(pdev: &pci::Device, this: Pin<&Self>) { diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index e11848bbf206..4163129b4103 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -68,9 +68,9 @@ impl Adapter { let info = T::ID_TABLE.info(id.index()); from_result(|| { - let data = T::probe(adev, info)?; + let data = T::probe(adev, info); - adev.as_ref().set_drvdata(data); + adev.as_ref().set_drvdata(data)?; Ok(0) }) } @@ -184,7 +184,7 @@ pub trait Driver { /// Auxiliary driver probe. /// /// Called when an auxiliary device is matches a corresponding driver. - fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; + fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; } /// The auxiliary device representation. diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 21b5b9b8acc1..462a9ef8669e 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -894,9 +894,9 @@ pub trait Driver { /// fn probe( /// pdev: &platform::Device, /// _id_info: Option<&Self::IdInfo>, -/// ) -> Result>> { +/// ) -> impl PinInit { /// cpufreq::Registration::::new_foreign_owned(pdev.as_ref())?; -/// Ok(KBox::new(Self {}, GFP_KERNEL)?.into()) +/// Ok(Self {}) /// } /// } /// ``` diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 1321e6f0b53c..23a95324cb0f 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -6,6 +6,7 @@ use crate::{ bindings, fmt, + prelude::*, sync::aref::ARef, types::{ForeignOwnable, Opaque}, }; @@ -198,9 +199,13 @@ impl Device { impl Device { /// Store a pointer to the bound driver's private data. - pub fn set_drvdata(&self, data: impl ForeignOwnable) { + pub fn set_drvdata(&self, data: impl PinInit) -> Result { + let data = KBox::pin_init(data, GFP_KERNEL)?; + // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. - unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) } + unsafe { bindings::dev_set_drvdata(self.as_raw(), data.into_foreign().cast()) }; + + Ok(()) } /// Take ownership of the private data stored in this [`Device`]. diff --git a/rust/kernel/driver.rs b/rust/kernel/driver.rs index 279e3af20682..9beae2e3d57e 100644 --- a/rust/kernel/driver.rs +++ b/rust/kernel/driver.rs @@ -24,7 +24,7 @@ //! const ACPI_ID_TABLE: Option> = None; //! //! /// Driver probe. -//! fn probe(dev: &Device, id_info: &Self::IdInfo) -> Result>>; +//! fn probe(dev: &Device, id_info: &Self::IdInfo) -> impl PinInit; //! //! /// Driver unbind (optional). //! fn unbind(dev: &Device, this: Pin<&Self>) { @@ -35,7 +35,7 @@ //! //! For specific examples see [`auxiliary::Driver`], [`pci::Driver`] and [`platform::Driver`]. //! -//! The `probe()` callback should return a `Result>>`, i.e. the driver's private +//! The `probe()` callback should return a `impl PinInit`, i.e. the driver's private //! data. The bus abstraction should store the pointer in the corresponding bus device. The generic //! [`Device`] infrastructure provides common helpers for this purpose on its //! [`Device`] implementation. diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs index 6f99510bfc3a..59096d579644 100644 --- a/rust/kernel/io/mem.rs +++ b/rust/kernel/io/mem.rs @@ -53,7 +53,7 @@ impl<'a> IoRequest<'a> { /// fn probe( /// pdev: &platform::Device, /// info: Option<&Self::IdInfo>, - /// ) -> Result>> { + /// ) -> impl PinInit { /// let offset = 0; // Some offset. /// /// // If the size is known at compile time, use [`Self::iomap_sized`]. @@ -70,7 +70,7 @@ impl<'a> IoRequest<'a> { /// /// io.write32_relaxed(data, offset); /// - /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// # Ok(SampleDriver) /// } /// } /// ``` @@ -111,7 +111,7 @@ impl<'a> IoRequest<'a> { /// fn probe( /// pdev: &platform::Device, /// info: Option<&Self::IdInfo>, - /// ) -> Result>> { + /// ) -> impl PinInit { /// let offset = 0; // Some offset. /// /// // Unlike [`Self::iomap_sized`], here the size of the memory region @@ -128,7 +128,7 @@ impl<'a> IoRequest<'a> { /// /// io.try_write32_relaxed(data, offset)?; /// - /// # Ok(KBox::new(SampleDriver, GFP_KERNEL)?.into()) + /// # Ok(SampleDriver) /// } /// } /// ``` 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. /// diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 7205fe3416d3..043721fdb6d8 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -74,9 +74,9 @@ impl Adapter { let info = ::id_info(pdev.as_ref()); 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) }) } @@ -166,7 +166,7 @@ macro_rules! module_platform_driver { /// fn probe( /// _pdev: &platform::Device, /// _id_info: Option<&Self::IdInfo>, -/// ) -> Result>> { +/// ) -> impl PinInit { /// Err(ENODEV) /// } /// } @@ -190,8 +190,10 @@ pub trait Driver: Send { /// /// Called when a new platform device is added or discovered. /// Implementers should attempt to initialize the device here. - fn probe(dev: &Device, id_info: Option<&Self::IdInfo>) - -> Result>>; + fn probe( + dev: &Device, + id_info: Option<&Self::IdInfo>, + ) -> impl PinInit; /// Platform driver unbind. /// diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 14ddb711bab3..fa8367c0dbaa 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -67,10 +67,10 @@ impl Adapter { let id = unsafe { &*id.cast::() }; let info = T::ID_TABLE.info(id.index()); - let data = T::probe(intf, id, info)?; + let data = T::probe(intf, id, info); let dev: &device::Device = intf.as_ref(); - dev.set_drvdata(data); + dev.set_drvdata(data)?; Ok(0) }) } @@ -270,7 +270,7 @@ macro_rules! usb_device_table { /// _interface: &usb::Interface, /// _id: &usb::DeviceId, /// _info: &Self::IdInfo, -/// ) -> Result>> { +/// ) -> impl PinInit { /// Err(ENODEV) /// } /// @@ -292,7 +292,7 @@ pub trait Driver { interface: &Interface, id: &DeviceId, id_info: &Self::IdInfo, - ) -> Result>>; + ) -> impl PinInit; /// USB driver disconnect. /// diff --git a/samples/rust/rust_debugfs.rs b/samples/rust/rust_debugfs.rs index 82b61a15a34b..d3f50f344856 100644 --- a/samples/rust/rust_debugfs.rs +++ b/samples/rust/rust_debugfs.rs @@ -106,16 +106,17 @@ impl platform::Driver for RustDebugFs { fn probe( pdev: &platform::Device, _info: Option<&Self::IdInfo>, - ) -> Result>> { - let result = KBox::try_pin_init(RustDebugFs::new(pdev), GFP_KERNEL)?; - // We can still mutate fields through the files which are atomic or mutexed: - result.counter.store(91, Ordering::Relaxed); - { - let mut guard = result.inner.lock(); - guard.x = guard.y; - guard.y = 42; - } - Ok(result) + ) -> impl PinInit { + RustDebugFs::new(pdev).pin_chain(|this| { + this.counter.store(91, Ordering::Relaxed); + { + let mut guard = this.inner.lock(); + guard.x = guard.y; + guard.y = 42; + } + + Ok(()) + }) } } diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs index 4d324f06cc2a..f53bce2a73e3 100644 --- a/samples/rust/rust_dma.rs +++ b/samples/rust/rust_dma.rs @@ -55,36 +55,33 @@ impl pci::Driver for DmaSampleDriver { type IdInfo = (); const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + pin_init::pin_init_scope(move || { + dev_info!(pdev.as_ref(), "Probe DMA test driver.\n"); - let mask = DmaMask::new::<64>(); + let mask = DmaMask::new::<64>(); - // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. - unsafe { pdev.dma_set_mask_and_coherent(mask)? }; + // SAFETY: There are no concurrent calls to DMA allocation and mapping primitives. + unsafe { pdev.dma_set_mask_and_coherent(mask)? }; - let ca: CoherentAllocation = - CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; + let ca: CoherentAllocation = + CoherentAllocation::alloc_coherent(pdev.as_ref(), TEST_VALUES.len(), GFP_KERNEL)?; - for (i, value) in TEST_VALUES.into_iter().enumerate() { - kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?; - } + for (i, value) in TEST_VALUES.into_iter().enumerate() { + kernel::dma_write!(ca[i] = MyStruct::new(value.0, value.1))?; + } - let size = 4 * page::PAGE_SIZE; - let pages = VVec::with_capacity(size, GFP_KERNEL)?; + let size = 4 * page::PAGE_SIZE; + let pages = VVec::with_capacity(size, GFP_KERNEL)?; - let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL); + let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL); - let drvdata = KBox::pin_init( - try_pin_init!(Self { + Ok(try_pin_init!(Self { pdev: pdev.into(), ca, sgt <- sgt, - }), - GFP_KERNEL, - )?; - - Ok(drvdata) + })) + }) } } diff --git a/samples/rust/rust_driver_auxiliary.rs b/samples/rust/rust_driver_auxiliary.rs index 55ece336ee45..0e221abe4936 100644 --- a/samples/rust/rust_driver_auxiliary.rs +++ b/samples/rust/rust_driver_auxiliary.rs @@ -27,7 +27,7 @@ impl auxiliary::Driver for AuxiliaryDriver { const ID_TABLE: auxiliary::IdTable = &AUX_TABLE; - fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> Result>> { + fn probe(adev: &auxiliary::Device, _info: &Self::IdInfo) -> impl PinInit { dev_info!( adev.as_ref(), "Probing auxiliary driver for auxiliary device with id={}\n", @@ -36,9 +36,7 @@ impl auxiliary::Driver for AuxiliaryDriver { ParentDriver::connect(adev)?; - let this = KBox::new(Self, GFP_KERNEL)?; - - Ok(this.into()) + Ok(Self) } } @@ -58,18 +56,13 @@ impl pci::Driver for ParentDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> Result>> { - let this = KBox::new( - Self { - _reg: [ - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?, - auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?, - ], - }, - GFP_KERNEL, - )?; - - Ok(this.into()) + fn probe(pdev: &pci::Device, _info: &Self::IdInfo) -> impl PinInit { + Ok(Self { + _reg: [ + auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 0, MODULE_NAME)?, + auxiliary::Registration::new(pdev.as_ref(), AUXILIARY_NAME, 1, MODULE_NAME)?, + ], + }) } } diff --git a/samples/rust/rust_driver_pci.rs b/samples/rust/rust_driver_pci.rs index 55a683c39ed9..5823787bea8e 100644 --- a/samples/rust/rust_driver_pci.rs +++ b/samples/rust/rust_driver_pci.rs @@ -65,35 +65,34 @@ impl pci::Driver for SampleDriver { const ID_TABLE: pci::IdTable = &PCI_TABLE; - fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> Result>> { - let vendor = pdev.vendor_id(); - dev_dbg!( - pdev.as_ref(), - "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", - vendor, - pdev.device_id() - ); - - pdev.enable_device_mem()?; - pdev.set_master(); - - let drvdata = KBox::pin_init( - try_pin_init!(Self { + fn probe(pdev: &pci::Device, info: &Self::IdInfo) -> impl PinInit { + pin_init::pin_init_scope(move || { + let vendor = pdev.vendor_id(); + dev_dbg!( + pdev.as_ref(), + "Probe Rust PCI driver sample (PCI ID: {}, 0x{:x}).\n", + vendor, + pdev.device_id() + ); + + pdev.enable_device_mem()?; + pdev.set_master(); + + Ok(try_pin_init!(Self { bar <- pdev.iomap_region_sized::<{ Regs::END }>(0, c_str!("rust_driver_pci")), - pdev: pdev.into(), index: *info, - }), - GFP_KERNEL, - )?; - - let bar = drvdata.bar.access(pdev.as_ref())?; - dev_info!( - pdev.as_ref(), - "pci-testdev data-match count: {}\n", - Self::testdev(info, bar)? - ); - - Ok(drvdata) + _: { + let bar = bar.access(pdev.as_ref())?; + + dev_info!( + pdev.as_ref(), + "pci-testdev data-match count: {}\n", + Self::testdev(info, bar)? + ); + }, + pdev: pdev.into(), + })) + }) } fn unbind(pdev: &pci::Device, this: Pin<&Self>) { diff --git a/samples/rust/rust_driver_platform.rs b/samples/rust/rust_driver_platform.rs index 6473baf4f120..d63adb422ba6 100644 --- a/samples/rust/rust_driver_platform.rs +++ b/samples/rust/rust_driver_platform.rs @@ -103,7 +103,7 @@ impl platform::Driver for SampleDriver { fn probe( pdev: &platform::Device, info: Option<&Self::IdInfo>, - ) -> Result>> { + ) -> impl PinInit { let dev = pdev.as_ref(); dev_dbg!(dev, "Probe Rust Platform driver sample.\n"); @@ -116,9 +116,7 @@ impl platform::Driver for SampleDriver { Self::properties_parse(dev)?; } - let drvdata = KBox::new(Self { pdev: pdev.into() }, GFP_KERNEL)?; - - Ok(drvdata.into()) + Ok(Self { pdev: pdev.into() }) } } diff --git a/samples/rust/rust_driver_usb.rs b/samples/rust/rust_driver_usb.rs index 5c396f421de7..4eaad14867b2 100644 --- a/samples/rust/rust_driver_usb.rs +++ b/samples/rust/rust_driver_usb.rs @@ -24,12 +24,11 @@ impl usb::Driver for SampleDriver { intf: &usb::Interface, _id: &usb::DeviceId, _info: &Self::IdInfo, - ) -> Result>> { + ) -> impl PinInit { let dev: &device::Device = intf.as_ref(); dev_info!(dev, "Rust USB driver sample probed\n"); - let drvdata = KBox::new(Self { _intf: intf.into() }, GFP_KERNEL)?; - Ok(drvdata.into()) + Ok(Self { _intf: intf.into() }) } fn disconnect(intf: &usb::Interface, _data: Pin<&Self>) { -- cgit v1.2.3 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 ++-- rust/kernel/pci/io.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 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); diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 65151a0a1a41..3684276b326b 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -18,7 +18,7 @@ use kernel::prelude::*; /// # 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. +/// memory mapped PCI BAR and its size. pub struct Bar { pdev: ARef, io: IoRaw, @@ -78,7 +78,7 @@ impl Bar { /// # Safety /// - /// `ioptr` must be a valid pointer to the memory mapped PCI bar number `num`. + /// `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`. @@ -120,7 +120,7 @@ impl Deref for Bar { } impl Device { - /// Mapps an entire PCI-BAR after performing a region-request on it. I/O operation bound checks + /// Maps 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, @@ -130,7 +130,7 @@ impl Device { Devres::new(self.as_ref(), Bar::::new(self, bar, name)) } - /// Mapps an entire PCI-BAR after performing a region-request on it. + /// Maps an entire PCI BAR after performing a region-request on it. pub fn iomap_region<'a>( &'a self, bar: u32, -- cgit v1.2.3 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/auxiliary.rs | 2 +- rust/kernel/device.rs | 4 ++-- rust/kernel/pci.rs | 2 +- rust/kernel/platform.rs | 2 +- rust/kernel/usb.rs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index e12f78734606..a6a2b23befce 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -85,7 +85,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>`. - drop(unsafe { adev.as_ref().drvdata_obtain::>>() }); + drop(unsafe { adev.as_ref().drvdata_obtain::() }); } } diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 343996027c89..106aa57a6385 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -215,7 +215,7 @@ impl Device { /// - Must only be called once after a preceding call to [`Device::set_drvdata`]. /// - The type `T` must match the type of the `ForeignOwnable` previously stored by /// [`Device::set_drvdata`]. - pub unsafe fn drvdata_obtain(&self) -> T { + pub unsafe fn drvdata_obtain(&self) -> Pin> { // SAFETY: By the type invariants, `self.as_raw()` is a valid pointer to a `struct device`. let ptr = unsafe { bindings::dev_get_drvdata(self.as_raw()) }; @@ -224,7 +224,7 @@ impl Device { // `into_foreign()`. // - `dev_get_drvdata()` guarantees to return the same pointer given to `dev_set_drvdata()` // in `into_foreign()`. - unsafe { T::from_foreign(ptr.cast()) } + unsafe { Pin::>::from_foreign(ptr.cast()) } } /// Borrow the driver's private data bound to this [`Device`]. 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()); } diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index 043721fdb6d8..8f7522c4cf89 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -91,7 +91,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()); } diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index fa8367c0dbaa..92215fdc3c6a 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -87,9 +87,9 @@ impl Adapter { // SAFETY: `disconnect_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 { dev.drvdata_obtain::>>() }; + let data = unsafe { dev.drvdata_obtain::() }; - T::disconnect(intf, data.as_ref()); + T::disconnect(intf, data.data()); } } -- cgit v1.2.3 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 +++++++++++++++++++++++++++-------- rust/kernel/pci/id.rs | 5 ++++- rust/kernel/pci/io.rs | 13 ++++++++----- rust/kernel/pci/irq.rs | 14 +++++++++----- 4 files changed, 48 insertions(+), 19 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); diff --git a/rust/kernel/pci/id.rs b/rust/kernel/pci/id.rs index 7f2a7f57507f..a1de70b2176a 100644 --- a/rust/kernel/pci/id.rs +++ b/rust/kernel/pci/id.rs @@ -4,7 +4,10 @@ //! //! This module contains PCI class codes, Vendor IDs, and supporting types. -use crate::{bindings, error::code::EINVAL, error::Error, prelude::*}; +use crate::{ + bindings, + prelude::*, // +}; use core::fmt; /// PCI device class codes. diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 3684276b326b..0d55c3139b6f 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -4,14 +4,17 @@ use super::Device; use crate::{ - bindings, device, + bindings, + device, devres::Devres, - io::{Io, IoRaw}, - str::CStr, - sync::aref::ARef, + io::{ + Io, + IoRaw, // + }, + prelude::*, + sync::aref::ARef, // }; use core::ops::Deref; -use kernel::prelude::*; /// A PCI BAR to perform I/O-Operations on. /// 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 v1.2.3 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/auxiliary.rs | 7 +++++++ rust/kernel/device.rs | 33 +++++++++++++++++++++++++++++++++ rust/kernel/i2c.rs | 7 +++++++ rust/kernel/pci.rs | 7 +++++++ rust/kernel/platform.rs | 7 +++++++ rust/kernel/usb.rs | 15 ++++++++++++++- 6 files changed, 75 insertions(+), 1 deletion(-) (limited to 'rust/kernel/pci.rs') diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs index 618eeeec2bd0..56f3c180e8f6 100644 --- a/rust/kernel/auxiliary.rs +++ b/rust/kernel/auxiliary.rs @@ -16,6 +16,7 @@ use crate::{ }; use core::{ marker::PhantomData, + mem::offset_of, ptr::{addr_of_mut, NonNull}, }; @@ -245,6 +246,12 @@ impl Device { } } +// SAFETY: `auxiliary::Device` is a transparent wrapper of `struct auxiliary_device`. +// The offset is guaranteed to point to a valid device field inside `auxiliary::Device`. +unsafe impl device::AsBusDevice for Device { + const OFFSET: usize = offset_of!(bindings::auxiliary_device, 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 }); diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 1a307be953c2..660cb2b48c07 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -594,6 +594,39 @@ impl DeviceContext for Core {} impl DeviceContext for CoreInternal {} impl DeviceContext for Normal {} +/// Convert device references to bus device references. +/// +/// Bus devices can implement this trait to allow abstractions to provide the bus device in +/// class device callbacks. +/// +/// This must not be used by drivers and is intended for bus and class device abstractions only. +/// +/// # Safety +/// +/// `AsBusDevice::OFFSET` must be the offset of the embedded base `struct device` field within a +/// bus device structure. +pub unsafe trait AsBusDevice: AsRef> { + /// The relative offset to the device field. + /// + /// Use `offset_of!(bindings, field)` macro to avoid breakage. + const OFFSET: usize; + + /// Convert a reference to [`Device`] into `Self`. + /// + /// # Safety + /// + /// `dev` must be contained in `Self`. + unsafe fn from_device(dev: &Device) -> &Self + where + Self: Sized, + { + let raw = dev.as_raw(); + // SAFETY: `raw - Self::OFFSET` is guaranteed by the safety requirements + // to be a valid pointer to `Self`. + unsafe { &*raw.byte_sub(Self::OFFSET).cast::() } + } +} + /// # Safety /// /// The type given as `$device` must be a transparent wrapper of a type that doesn't depend on the diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs index aea1b44d189b..95b056cc1a71 100644 --- a/rust/kernel/i2c.rs +++ b/rust/kernel/i2c.rs @@ -24,6 +24,7 @@ use crate::{ use core::{ marker::PhantomData, + mem::offset_of, ptr::{ from_ref, NonNull, // @@ -476,6 +477,12 @@ impl I2cClient { } } +// SAFETY: `I2cClient` is a transparent wrapper of `struct i2c_client`. +// The offset is guaranteed to point to a valid device field inside `I2cClient`. +unsafe impl device::AsBusDevice for I2cClient { + const OFFSET: usize = offset_of!(bindings::i2c_client, dev); +} + // SAFETY: `I2cClient` is a transparent wrapper of a type that doesn't depend on // `I2cClient`'s generic argument. kernel::impl_device_context_deref!(unsafe { I2cClient }); 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 }); diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs index f4b617c570be..ed889f079cab 100644 --- a/rust/kernel/platform.rs +++ b/rust/kernel/platform.rs @@ -19,6 +19,7 @@ use crate::{ use core::{ marker::PhantomData, + mem::offset_of, ptr::{addr_of_mut, NonNull}, }; @@ -287,6 +288,12 @@ impl Device { } } +// SAFETY: `platform::Device` is a transparent wrapper of `struct platform_device`. +// The offset is guaranteed to point to a valid device field inside `platform::Device`. +unsafe impl device::AsBusDevice for Device { + const OFFSET: usize = offset_of!(bindings::platform_device, dev); +} + macro_rules! define_irq_accessor_by_index { ( $(#[$meta:meta])* $fn_name:ident, diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs index 534e3ded5442..d10b65e9fb6a 100644 --- a/rust/kernel/usb.rs +++ b/rust/kernel/usb.rs @@ -15,7 +15,14 @@ use crate::{ types::{AlwaysRefCounted, Opaque}, ThisModule, }; -use core::{marker::PhantomData, mem::MaybeUninit, ptr::NonNull}; +use core::{ + marker::PhantomData, + mem::{ + offset_of, + MaybeUninit, // + }, + ptr::NonNull, +}; /// An adapter for the registration of USB drivers. pub struct Adapter(T); @@ -324,6 +331,12 @@ impl Interface { } } +// SAFETY: `usb::Interface` is a transparent wrapper of `struct usb_interface`. +// The offset is guaranteed to point to a valid device field inside `usb::Interface`. +unsafe impl device::AsBusDevice for Interface { + const OFFSET: usize = offset_of!(bindings::usb_interface, dev); +} + // SAFETY: `Interface` is a transparent wrapper of a type that doesn't depend on // `Interface`'s generic argument. kernel::impl_device_context_deref!(unsafe { Interface }); -- cgit v1.2.3