From nobody Fri Dec 19 22:01:19 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 30B8933A01F; Wed, 15 Oct 2025 18:21:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552499; cv=none; b=EZWRYjD6booFGkSi8vNvV3yTCu+rsV9n+KYg2j/umUmMlHQwQ+lwqHyxLdNa2Q53A94A4T0Zg7sp/EsuZnyl9OYphkfNjbSQXNh3DOfthtAZvtrCmwQt4kMXWduZuqCgh4Y+uXrdh2Mjl8cxbSxiVZJHlahUUUgS1isvXA3BenA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552499; c=relaxed/simple; bh=0jU3SZC0kW+muc4oDf5PDo4jUG2aFggEdQM/J88JpKI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=BW/g5pN4PysCXY6G1mkQpKAwVloYP5PzBsCLwKvjTJZ6jbpQPhAshoOE9n341lfRwr3RDMACoy8sLIkz7YFlisawz1eB1FryMoKbVMMSJ56ujIXn2AMtq3DCBLHy97BYG/iFPVgfFIrwSlG9AR1+kqAVR0n04/xTFPXzdc+u5Sw= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=uvERTfaf; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="uvERTfaf" Received: by smtp.kernel.org (Postfix) with ESMTPSA id A2541C4AF54; Wed, 15 Oct 2025 18:21:35 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760552498; bh=0jU3SZC0kW+muc4oDf5PDo4jUG2aFggEdQM/J88JpKI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=uvERTfafvEQ0FQTycCGn1JqWrBBqIN478sCrtim29abCMBLVbL6O70GHA8ADCjN8I atFacmmYKYEmQAnM0thbH/9B/thxFAsAsn62kFRGGvIglNbQ7PHtweN1Mh9LL0tdq9 OFUYMz9rZU1dTEvKXhBTF+XN2uXy1x3o7uNIyZvaMmg6YoZ5uKQUEp2731xyo/Fx3e Tsm90ihX+Hy4Z70d2CVn/zp6KYMJ51n/uMkJoursthIl20vIcfekGLneIKKDI3SOzO HbmLAInrHtrBcaSqrXKKIpVsAEUKIlJGTtAnE58+wgktcF+WyatsZhwKMs23+xcboV W3/+BaJj6NRLg== From: Danilo Krummrich To: bhelgaas@google.com, kwilczynski@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: rust-for-linux@vger.kernel.org, linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, Danilo Krummrich Subject: [PATCH 1/3] rust: pci: implement TryInto> for IrqVector<'a> Date: Wed, 15 Oct 2025 20:14:29 +0200 Message-ID: <20251015182118.106604-2-dakr@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251015182118.106604-1-dakr@kernel.org> References: <20251015182118.106604-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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. Signed-off-by: Danilo Krummrich Reviewed-by: Alice Ryhl Reviewed-by: Joel Fernandes --- rust/kernel/pci.rs | 38 ++++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 20 deletions(-) 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 @@ fn index(&self) -> u32 { } } =20 +impl<'a> TryInto> for IrqVector<'a> { + type Error =3D Error; + + fn try_into(self) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_= dev`. + let irq =3D unsafe { bindings::pci_irq_vector(self.dev.as_raw(), s= elf.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 @@ pub fn iomap_region<'a>( self.iomap_region_sized::<0>(bar, name) } =20 - /// 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 =3D 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 =3D self.irq_vector(vector)?; + let request =3D vector.try_into()?; =20 Ok(irq::Registration::::new(request, flags, name, handler)) } @@ -707,12 +705,12 @@ pub fn request_irq<'a, T: crate::irq::Handler + 'stat= ic>( /// Returns a [`kernel::irq::ThreadedRegistration`] for the given IRQ = vector. pub fn request_threaded_irq<'a, T: crate::irq::ThreadedHandler + 'stat= ic>( &'a self, - vector: IrqVector<'_>, + vector: IrqVector<'a>, flags: irq::Flags, name: &'static CStr, handler: impl PinInit + 'a, ) -> Result, Error> + 'a> { - let request =3D self.irq_vector(vector)?; + let request =3D vector.try_into()?; =20 Ok(irq::ThreadedRegistration::::new( request, flags, name, handler, --=20 2.51.0 From nobody Fri Dec 19 22:01:19 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 95F2333A01F; Wed, 15 Oct 2025 18:21:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552502; cv=none; b=K+GthTCAMswtBBvLDFv95t1gYD8zEttaUni4H8APznekizzfZno5cW3CF61Q9nuhoq1TQD6SXCDOM9BREVxONfRj36lmQ39P2zyqz7eFl49TMHWwtj/NxvhQ5fx71sDaHRMyv6y9KT9fYwEZx8rGeGKBacKKGv7Mrf3USNMC5ns= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552502; c=relaxed/simple; bh=bYDYwSWhzccXxXvDuSWiQVKIv+ZMP5+x49stWJ0QSuc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=EPhMH2mGH7YUXptRnNEk1wVX73BhKq31Q9X5uZvkCsTNDdt+0/N++aqcHcz7LQtDMF1GJ1sGUZYp+BCm2V6ZuD1R3DCOAaiMP0nmWWq/dO2j6JIxgHrqXu5/8wN7uw0S/dLCBQCG3O0qyz8w9LjuGDjBDMxAQBicNHBPoCM6SII= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=BH/te9Hy; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="BH/te9Hy" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 1E588C4AF53; Wed, 15 Oct 2025 18:21:38 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760552502; bh=bYDYwSWhzccXxXvDuSWiQVKIv+ZMP5+x49stWJ0QSuc=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=BH/te9HypOYLTlRlOOdj6gnjDBp6hAgLsp0BtydQXVpDt/p2NBigiAKhdDB0nZQPN tvSru0DWFcZ0QYfbcp/yr/RUyj9EagcD9+khdrw6uVfND0mNGtx8lKBS03/PJAeD/4 nFdQpQTP7Vnbd8sWQvaeVUXEb+4CSMOo8e2Cc7EErx0f2TQasC4kseiRGCBWy/Dl8N W3rCwAKmrsH0uvo4bvGa8DLTeEnkX3d4l/+zo44ImEqQczZwyQg1GCRC6i6eRkDxZY UIUkMT9P5EC7lcUqzZjFgk4WvfGMxOt1/aRI9gX9tWhMoYAycT+NfSjPun0uTQ17Q6 7DHXrcuLYITog== From: Danilo Krummrich To: bhelgaas@google.com, kwilczynski@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: rust-for-linux@vger.kernel.org, linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, Danilo Krummrich Subject: [PATCH 2/3] rust: pci: move I/O infrastructure to separate file Date: Wed, 15 Oct 2025 20:14:30 +0200 Message-ID: <20251015182118.106604-3-dakr@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251015182118.106604-1-dakr@kernel.org> References: <20251015182118.106604-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 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 @@ 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 core::{ marker::PhantomData, - ops::{Deref, RangeInclusive}, + ops::RangeInclusive, ptr::{addr_of_mut, NonNull}, }; use kernel::prelude::*; =20 mod id; +mod io; =20 pub use self::id::{Class, ClassMask, Vendor}; +pub use self::io::Bar; =20 /// IRQ type flags for PCI interrupt allocation. #[derive(Debug, Clone, Copy)] @@ -358,112 +358,6 @@ pub struct Device( PhantomData, ); =20 -/// 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 =3D pdev.resource_len(num)?; - if len =3D=3D 0 { - return Err(ENOMEM); - } - - // Convert to `i32`, since that's what all the C bindings use. - let num =3D i32::try_from(num)?; - - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `num` is checked for validity by a previous call to `Device::re= source_len`. - // `name` is always valid. - let ret =3D unsafe { bindings::pci_request_region(pdev.as_raw(), n= um, name.as_char_ptr()) }; - if ret !=3D 0 { - return Err(EBUSY); - } - - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `num` is checked for validity by a previous call to `Device::re= source_len`. - // `name` is always valid. - let ioptr: usize =3D unsafe { bindings::pci_iomap(pdev.as_raw(), n= um, 0) } as usize; - if ioptr =3D=3D 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 =3D match IoRaw::new(ioptr, len as usize) { - Ok(io) =3D> io, - Err(err) =3D> { - // SAFETY: - // `pdev` is valid by the invariants of `Device`. - // `ioptr` is guaranteed to be the start of a valid I/O ma= pped memory region. - // `num` is checked for validity by a previous call to `De= vice::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 numbe= r `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 inva= riant 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 =3D Io; - - fn deref(&self) -> &Self::Target { - // SAFETY: By the type invariant of `Self`, the MMIO range in `sel= f.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 @@ fn drop(&mut self) { } =20 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 t= ype 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 =3D pdev.resource_len(num)?; + if len =3D=3D 0 { + return Err(ENOMEM); + } + + // Convert to `i32`, since that's what all the C bindings use. + let num =3D i32::try_from(num)?; + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::re= source_len`. + // `name` is always valid. + let ret =3D unsafe { bindings::pci_request_region(pdev.as_raw(), n= um, name.as_char_ptr()) }; + if ret !=3D 0 { + return Err(EBUSY); + } + + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `num` is checked for validity by a previous call to `Device::re= source_len`. + // `name` is always valid. + let ioptr: usize =3D unsafe { bindings::pci_iomap(pdev.as_raw(), n= um, 0) } as usize; + if ioptr =3D=3D 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 =3D match IoRaw::new(ioptr, len as usize) { + Ok(io) =3D> io, + Err(err) =3D> { + // SAFETY: + // `pdev` is valid by the invariants of `Device`. + // `ioptr` is guaranteed to be the start of a valid I/O ma= pped memory region. + // `num` is checked for validity by a previous call to `De= vice::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 numbe= r `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 inva= riant 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 =3D Io; + + fn deref(&self) -> &Self::Target { + // SAFETY: By the type invariant of `Self`, the MMIO range in `sel= f.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 t= ype 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) + } +} --=20 2.51.0 From nobody Fri Dec 19 22:01:19 2025 Received: from smtp.kernel.org (aws-us-west-2-korg-mail-1.web.codeaurora.org [10.30.226.201]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id EAE94340DA9; Wed, 15 Oct 2025 18:21:45 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=10.30.226.201 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552506; cv=none; b=q/YSJzaKLR99x2PBb/rVWgRocHBIONO8xc4cur37CmY9L80PXIpaG60QIJ/48D2kRK5b4UpoxqsOABzhIYGTsGJ2oGTss1A/uaYyFk7GrOYPrywQG3QJ4Gp0aCjgUspkcgamNpfhoqbcGueVb1/dzDen1tFw96vFd66bO/ToJQA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1760552506; c=relaxed/simple; bh=Q/flZqlx/5pQas2MrOF/QlCi1+4kadGxmZ0Uiqzy8zw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=enMMERSQTdwz0INhJypQUjpWs38zGOkM8AL70bAY8nIv96HZFTx+bo+TUwrZNuPI4VW6+N88XnkWzLMG/hD6ITc+7K40UBMkCCGoCF/bR6tDRmEI4qqzQHBBD/1t7zN9KEUlHD3bq7qnZY5vcF67GaiYDUat3ZNP+xV5J4itYBc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b=ugTwX8lv; arc=none smtp.client-ip=10.30.226.201 Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=kernel.org header.i=@kernel.org header.b="ugTwX8lv" Received: by smtp.kernel.org (Postfix) with ESMTPSA id 8D27EC4AF54; Wed, 15 Oct 2025 18:21:42 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=kernel.org; s=k20201202; t=1760552505; bh=Q/flZqlx/5pQas2MrOF/QlCi1+4kadGxmZ0Uiqzy8zw=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=ugTwX8lvmfxZ5Yox6didCOMDl+YMaEVC6hzEx2olu7gE4O9/qD0mvAEJSZGffUqRB bn2mZjXzTzjudasngxBsPR3nBreFOMqgZiRo+53k8xnP6w/DCFO0FYw/EtmWqQDUut 2YQOgHgd9206ZJ0KLbUZ25CquuRT+YsHeIpsAUxhzbgA1o1ny9c2A0kPXNWuAia1m3 AagFFLcfPdKvrkcB2Eg+fImvBdEtduv22A7Spe1SAiiP3poOnJH9vJxt/vATtDGMOK vyz8pjtDX/gelVDz34MLK41AFnxk4dxtE6BUXc2aog3yr4MrYRQuzTnpV0PK3zwlNw w1ePaXljXB0RA== From: Danilo Krummrich To: bhelgaas@google.com, kwilczynski@kernel.org, ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com, gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org, a.hindborg@kernel.org, aliceryhl@google.com, tmgross@umich.edu Cc: rust-for-linux@vger.kernel.org, linux-pci@vger.kernel.org, linux-kernel@vger.kernel.org, Danilo Krummrich Subject: [PATCH 3/3] rust: pci: move IRQ infrastructure to separate file Date: Wed, 15 Oct 2025 20:14:31 +0200 Message-ID: <20251015182118.106604-4-dakr@kernel.org> X-Mailer: git-send-email 2.51.0 In-Reply-To: <20251015182118.106604-1-dakr@kernel.org> References: <20251015182118.106604-1-dakr@kernel.org> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 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 @@ =20 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::*; =20 mod id; mod io; +mod irq; =20 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 =3D> bindings::PCI_IRQ_INTX, - IrqType::Msi =3D> bindings::PCI_IRQ_MSI, - IrqType::MsiX =3D> 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 =3D 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}; =20 /// An adapter for the registration of PCI drivers. pub struct Adapter(T); @@ -463,182 +409,6 @@ pub fn pci_class(&self) -> Class { } } =20 -/// 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 =3D Error; - - fn try_into(self) -> Result> { - // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_= dev`. - let irq =3D unsafe { bindings::pci_irq_vector(self.dev.as_raw(), s= elf.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 `stru= ct pci_dev` - // by the type invariant of `Device`. - // - `pci_alloc_irq_vectors` internally validates all other parame= ters - // and returns error codes. - let ret =3D unsafe { - bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_ve= cs, irq_types.as_raw()) - }; - - to_result(ret)?; - let count =3D ret as u32; - - // SAFETY: - // - `pci_alloc_irq_vectors` returns the number of allocated vecto= rs on success. - // - Vectors are 0-based, so valid indices are [0, count-1]. - // - `pci_alloc_irq_vectors` guarantees `count >=3D min_vecs > 0`,= so both `0` and - // `count - 1` are valid IRQ vector indices for `dev`. - let range =3D unsafe { IrqVector::new(dev, 0)..=3DIrqVector::new(d= ev, count - 1) }; - - // INVARIANT: The IRQ vector allocation for `dev` above was succes= sful. - let irq_vecs =3D 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 =3D 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 + 'stat= ic>( - &'a self, - vector: IrqVector<'a>, - flags: irq::Flags, - name: &'static CStr, - handler: impl PinInit + 'a, - ) -> Result, Error> + 'a> { - let request =3D 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 speci= fied, 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 u= nbound, 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, o= r 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 mentio= ned above. - /// let vectors =3D dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())= ?; - /// - /// // Allocate MSI or MSI-X only (no legacy interrupts). - /// let msi_only =3D pci::IrqTypes::default() - /// .with(pci::IrqType::Msi) - /// .with(pci::IrqType::MsiX); - /// let vectors =3D 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_type= s) - } -} - 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 =3D> bindings::PCI_IRQ_INTX, + IrqType::Msi =3D> bindings::PCI_IRQ_MSI, + IrqType::MsiX =3D> 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 =3D 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 =3D Error; + + fn try_into(self) -> Result> { + // SAFETY: `self.as_raw` returns a valid pointer to a `struct pci_= dev`. + let irq =3D unsafe { bindings::pci_irq_vector(self.dev.as_raw(), s= elf.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 `stru= ct pci_dev` + // by the type invariant of `Device`. + // - `pci_alloc_irq_vectors` internally validates all other parame= ters + // and returns error codes. + let ret =3D unsafe { + bindings::pci_alloc_irq_vectors(dev.as_raw(), min_vecs, max_ve= cs, irq_types.as_raw()) + }; + + to_result(ret)?; + let count =3D ret as u32; + + // SAFETY: + // - `pci_alloc_irq_vectors` returns the number of allocated vecto= rs on success. + // - Vectors are 0-based, so valid indices are [0, count-1]. + // - `pci_alloc_irq_vectors` guarantees `count >=3D min_vecs > 0`,= so both `0` and + // `count - 1` are valid IRQ vector indices for `dev`. + let range =3D unsafe { IrqVector::new(dev, 0)..=3DIrqVector::new(d= ev, count - 1) }; + + // INVARIANT: The IRQ vector allocation for `dev` above was succes= sful. + let irq_vecs =3D 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 =3D 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 + 'stat= ic>( + &'a self, + vector: IrqVector<'a>, + flags: irq::Flags, + name: &'static CStr, + handler: impl PinInit + 'a, + ) -> Result, Error> + 'a> { + let request =3D 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 speci= fied, 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 u= nbound, 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, o= r 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 mentio= ned above. + /// let vectors =3D dev.alloc_irq_vectors(1, 32, pci::IrqTypes::all())= ?; + /// + /// // Allocate MSI or MSI-X only (no legacy interrupts). + /// let msi_only =3D pci::IrqTypes::default() + /// .with(pci::IrqType::Msi) + /// .with(pci::IrqType::MsiX); + /// let vectors =3D 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_type= s) + } +} --=20 2.51.0