From: Danilo Krummrich <dakr@kernel.org>
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 <dakr@kernel.org>
Subject: [PATCH 2/3] rust: pci: move I/O infrastructure to separate file
Date: Wed, 15 Oct 2025 20:14:30 +0200 [thread overview]
Message-ID: <20251015182118.106604-3-dakr@kernel.org> (raw)
In-Reply-To: <20251015182118.106604-1-dakr@kernel.org>
Move the PCI I/O infrastructure to a separate sub-module in order to
keep things organized.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
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::*;
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<Ctx: device::DeviceContext = device::Normal>(
PhantomData<Ctx>,
);
-/// 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<const SIZE: usize = 0> {
- pdev: ARef<Device>,
- io: IoRaw<SIZE>,
- num: i32,
-}
-
-impl<const SIZE: usize> Bar<SIZE> {
- fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
- 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<const SIZE: usize> Drop for Bar<SIZE> {
- fn drop(&mut self) {
- self.release();
- }
-}
-
-impl<const SIZE: usize> Deref for Bar<SIZE> {
- type Target = Io<SIZE>;
-
- 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<Ctx: device::DeviceContext> Device<Ctx> {
#[inline]
fn as_raw(&self) -> *mut bindings::pci_dev {
@@ -670,25 +564,6 @@ fn drop(&mut self) {
}
impl Device<device::Bound> {
- /// 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<Devres<Bar<SIZE>>, Error> + 'a {
- Devres::new(self.as_ref(), Bar::<SIZE>::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<Devres<Bar>, 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<const SIZE: usize = 0> {
+ pdev: ARef<Device>,
+ io: IoRaw<SIZE>,
+ num: i32,
+}
+
+impl<const SIZE: usize> Bar<SIZE> {
+ pub(super) fn new(pdev: &Device, num: u32, name: &CStr) -> Result<Self> {
+ 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<const SIZE: usize> Drop for Bar<SIZE> {
+ fn drop(&mut self) {
+ self.release();
+ }
+}
+
+impl<const SIZE: usize> Deref for Bar<SIZE> {
+ type Target = Io<SIZE>;
+
+ 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<device::Bound> {
+ /// 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<Devres<Bar<SIZE>>, Error> + 'a {
+ Devres::new(self.as_ref(), Bar::<SIZE>::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<Devres<Bar>, Error> + 'a {
+ self.iomap_region_sized::<0>(bar, name)
+ }
+}
--
2.51.0
next prev parent reply other threads:[~2025-10-15 18:21 UTC|newest]
Thread overview: 15+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-15 18:14 [PATCH 0/3] Rust PCI housekeeping Danilo Krummrich
2025-10-15 18:14 ` [PATCH 1/3] rust: pci: implement TryInto<IrqRequest<'a>> for IrqVector<'a> Danilo Krummrich
2025-10-16 15:01 ` Alice Ryhl
2025-10-16 17:04 ` Danilo Krummrich
2025-10-16 22:24 ` Joel Fernandes
2025-10-16 22:57 ` Danilo Krummrich
2025-10-16 23:02 ` Joel Fernandes
2025-10-15 18:14 ` Danilo Krummrich [this message]
2025-10-15 22:58 ` [PATCH 2/3] rust: pci: move I/O infrastructure to separate file Bjorn Helgaas
2025-10-16 12:34 ` Danilo Krummrich
2025-10-16 15:52 ` Bjorn Helgaas
2025-10-16 18:54 ` Miguel Ojeda
2025-10-15 18:14 ` [PATCH 3/3] rust: pci: move IRQ " Danilo Krummrich
2025-10-15 23:02 ` Bjorn Helgaas
2025-10-20 11:39 ` [PATCH 0/3] Rust PCI housekeeping Danilo Krummrich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251015182118.106604-3-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=gary@garyguo.net \
--cc=kwilczynski@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.