From: Danilo Krummrich <dakr@kernel.org>
To: gregkh@linuxfoundation.org, rafael@kernel.org,
acourbot@nvidia.com, aliceryhl@google.com,
david.m.ertman@intel.com, ira.weiny@intel.com, leon@kernel.org,
viresh.kumar@linaro.org, m.wilczynski@samsung.com,
ukleinek@kernel.org, bhelgaas@google.com, kwilczynski@kernel.org,
abdiel.janulgue@gmail.com, robin.murphy@arm.com,
markus.probst@posteo.de, ojeda@kernel.org, boqun@kernel.org,
gary@garyguo.net, bjorn3_gh@protonmail.com, lossin@kernel.org,
a.hindborg@kernel.org, tmgross@umich.edu, igor.korotin@linux.dev,
daniel.almeida@collabora.com
Cc: driver-core@lists.linux.dev, linux-kernel@vger.kernel.org,
nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org,
linux-pm@vger.kernel.org, linux-pwm@vger.kernel.org,
linux-pci@vger.kernel.org, rust-for-linux@vger.kernel.org,
Danilo Krummrich <dakr@kernel.org>
Subject: [PATCH v2 18/25] rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized
Date: Wed, 6 May 2026 23:50:54 +0200 [thread overview]
Message-ID: <20260506215113.851360-19-dakr@kernel.org> (raw)
In-Reply-To: <20260506215113.851360-1-dakr@kernel.org>
Add a lifetime parameter to IoMem<'bound, SIZE> and
ExclusiveIoMem<'bound, SIZE>, storing a &'bound Device<Bound> reference
to tie the mapping to the device's lifetime.
This mirrors the pci::Bar<'bound, SIZE> design and enables drivers to
hold I/O memory mappings directly in their HRT private data, tied to the
device lifetime.
IoRequest::iomap_* methods now return the mapping directly instead of
wrapping it in Devres. Callers that need device-managed revocation can
call the new into_devres() method.
Acked-by: Uwe Kleine-König <ukleinek@kernel.org>
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/gpu/drm/tyr/driver.rs | 4 +-
drivers/pwm/pwm_th1520.rs | 4 +-
rust/kernel/io/mem.rs | 115 +++++++++++++++++-----------------
3 files changed, 62 insertions(+), 61 deletions(-)
diff --git a/drivers/gpu/drm/tyr/driver.rs b/drivers/gpu/drm/tyr/driver.rs
index 96d83605e4b9..295a627de2b2 100644
--- a/drivers/gpu/drm/tyr/driver.rs
+++ b/drivers/gpu/drm/tyr/driver.rs
@@ -37,7 +37,7 @@
regs, //
};
-pub(crate) type IoMem = kernel::io::mem::IoMem<SZ_2M>;
+pub(crate) type IoMem = kernel::io::mem::IoMem<'static, SZ_2M>;
pub(crate) struct TyrDrmDriver;
@@ -109,7 +109,7 @@ fn probe(
let sram_regulator = Regulator::<regulator::Enabled>::get(pdev.as_ref(), c"sram")?;
let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
- let iomem = Arc::pin_init(request.iomap_sized::<SZ_2M>(), GFP_KERNEL)?;
+ let iomem = Arc::new(request.iomap_sized::<SZ_2M>()?.into_devres()?, GFP_KERNEL)?;
issue_soft_reset(pdev.as_ref(), &iomem)?;
gpu::l2_power_on(pdev.as_ref(), &iomem)?;
diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs
index a7831b4ebe00..3deb39d8e0fc 100644
--- a/drivers/pwm/pwm_th1520.rs
+++ b/drivers/pwm/pwm_th1520.rs
@@ -92,7 +92,7 @@ struct Th1520WfHw {
#[pin_data(PinnedDrop)]
struct Th1520PwmDriverData {
#[pin]
- iomem: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>,
+ iomem: devres::Devres<IoMem<'static, TH1520_PWM_REG_SIZE>>,
clk: Clk,
}
@@ -351,7 +351,7 @@ fn probe(
dev,
TH1520_MAX_PWM_NUM,
try_pin_init!(Th1520PwmDriverData {
- iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>(),
+ iomem <- request.iomap_sized::<TH1520_PWM_REG_SIZE>()?.into_devres(),
clk <- clk,
}),
)?;
diff --git a/rust/kernel/io/mem.rs b/rust/kernel/io/mem.rs
index fd5292df5870..a4cb12ee70d3 100644
--- a/rust/kernel/io/mem.rs
+++ b/rust/kernel/io/mem.rs
@@ -23,19 +23,19 @@
};
/// An IO request for a specific device and resource.
-pub struct IoRequest<'a> {
- device: &'a Device<Bound>,
- resource: &'a Resource,
+pub struct IoRequest<'bound> {
+ device: &'bound Device<Bound>,
+ resource: &'bound Resource,
}
-impl<'a> IoRequest<'a> {
+impl<'bound> IoRequest<'bound> {
/// Creates a new [`IoRequest`] instance.
///
/// # Safety
///
/// Callers must ensure that `resource` is valid for `device` during the
- /// lifetime `'a`.
- pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> Self {
+ /// lifetime `'bound`.
+ pub(crate) unsafe fn new(device: &'bound Device<Bound>, resource: &'bound Resource) -> Self {
IoRequest { device, resource }
}
@@ -73,22 +73,19 @@ pub(crate) unsafe fn new(device: &'a Device<Bound>, resource: &'a Resource) -> S
/// //
/// // No runtime checks will apply when reading and writing.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
- /// let iomem = request.iomap_sized::<42>();
- /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
- ///
- /// let io = iomem.access(pdev.as_ref())?;
+ /// let iomem = request.iomap_sized::<42>()?;
///
/// // Read and write a 32-bit value at `offset`.
- /// let data = io.read32(offset);
+ /// let data = iomem.read32(offset);
///
- /// io.write32(data, offset);
+ /// iomem.write32(data, offset);
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
- pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>, Error> + 'a {
- IoMem::new(self)
+ pub fn iomap_sized<const SIZE: usize>(self) -> Result<IoMem<'bound, SIZE>> {
+ IoMem::ioremap(self.device, self.resource)
}
/// Same as [`Self::iomap_sized`] but with exclusive access to the
@@ -97,10 +94,8 @@ pub fn iomap_sized<const SIZE: usize>(self) -> impl PinInit<Devres<IoMem<SIZE>>,
/// This uses the [`ioremap()`] C API.
///
/// [`ioremap()`]: https://docs.kernel.org/driver-api/device-io.html#getting-access-to-the-device
- pub fn iomap_exclusive_sized<const SIZE: usize>(
- self,
- ) -> impl PinInit<Devres<ExclusiveIoMem<SIZE>>, Error> + 'a {
- ExclusiveIoMem::new(self)
+ pub fn iomap_exclusive_sized<const SIZE: usize>(self) -> Result<ExclusiveIoMem<'bound, SIZE>> {
+ ExclusiveIoMem::ioremap(self.device, self.resource)
}
/// Maps an [`IoRequest`] where the size is not known at compile time,
@@ -138,27 +133,24 @@ pub fn iomap_exclusive_sized<const SIZE: usize>(
/// // family of functions should be used, leading to runtime checks on every
/// // access.
/// let request = pdev.io_request_by_index(0).ok_or(ENODEV)?;
- /// let iomem = request.iomap();
- /// let iomem = KBox::pin_init(iomem, GFP_KERNEL)?;
- ///
- /// let io = iomem.access(pdev.as_ref())?;
+ /// let iomem = request.iomap()?;
///
- /// let data = io.try_read32(offset)?;
+ /// let data = iomem.try_read32(offset)?;
///
- /// io.try_write32(data, offset)?;
+ /// iomem.try_write32(data, offset)?;
///
/// # Ok(SampleDriver)
/// }
/// }
/// ```
- pub fn iomap(self) -> impl PinInit<Devres<IoMem<0>>, Error> + 'a {
- Self::iomap_sized::<0>(self)
+ pub fn iomap(self) -> Result<IoMem<'bound>> {
+ self.iomap_sized::<0>()
}
/// Same as [`Self::iomap`] but with exclusive access to the underlying
/// region.
- pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> + 'a {
- Self::iomap_exclusive_sized::<0>(self)
+ pub fn iomap_exclusive(self) -> Result<ExclusiveIoMem<'bound, 0>> {
+ self.iomap_exclusive_sized::<0>()
}
}
@@ -167,9 +159,9 @@ pub fn iomap_exclusive(self) -> impl PinInit<Devres<ExclusiveIoMem<0>>, Error> +
/// # Invariants
///
/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
-pub struct ExclusiveIoMem<const SIZE: usize> {
+pub struct ExclusiveIoMem<'bound, const SIZE: usize> {
/// The underlying `IoMem` instance.
- iomem: IoMem<SIZE>,
+ iomem: IoMem<'bound, SIZE>,
/// The region abstraction. This represents exclusive access to the
/// range represented by the underlying `iomem`.
@@ -178,9 +170,9 @@ pub struct ExclusiveIoMem<const SIZE: usize> {
_region: Region,
}
-impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
+impl<'bound, const SIZE: usize> ExclusiveIoMem<'bound, SIZE> {
/// Creates a new `ExclusiveIoMem` instance.
- fn ioremap(resource: &Resource) -> Result<Self> {
+ fn ioremap(dev: &'bound Device<Bound>, resource: &Resource) -> Result<Self> {
let start = resource.start();
let size = resource.size();
let name = resource.name().unwrap_or_default();
@@ -194,26 +186,29 @@ fn ioremap(resource: &Resource) -> Result<Self> {
)
.ok_or(EBUSY)?;
- let iomem = IoMem::ioremap(resource)?;
+ let iomem = IoMem::ioremap(dev, resource)?;
- let iomem = ExclusiveIoMem {
+ Ok(ExclusiveIoMem {
iomem,
_region: region,
- };
-
- Ok(iomem)
+ })
}
- /// Creates a new `ExclusiveIoMem` instance from a previously acquired [`IoRequest`].
- pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
- let dev = io_request.device;
- let res = io_request.resource;
-
- Devres::new(dev, Self::ioremap(res))
+ /// Consume the `ExclusiveIoMem` and register it as a device-managed resource.
+ ///
+ /// The returned `Devres<ExclusiveIoMem<'static, SIZE>>` can outlive the original lifetime
+ /// `'bound`. Access to the I/O memory is revoked when the device is unbound.
+ pub fn into_devres(self) -> Result<Devres<ExclusiveIoMem<'static, SIZE>>> {
+ // SAFETY: Casting to `'static` is sound because `Devres` guarantees the
+ // `ExclusiveIoMem` does not actually outlive the device -- access is revoked and the
+ // resource is released when the device is unbound.
+ let iomem: ExclusiveIoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
+ let dev = iomem.iomem.dev;
+ Devres::new(dev, iomem)
}
}
-impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
+impl<const SIZE: usize> Deref for ExclusiveIoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
fn deref(&self) -> &Self::Target {
@@ -230,12 +225,13 @@ fn deref(&self) -> &Self::Target {
///
/// [`IoMem`] always holds an [`MmioRaw`] instance that holds a valid pointer to the
/// start of the I/O memory mapped region.
-pub struct IoMem<const SIZE: usize = 0> {
+pub struct IoMem<'bound, const SIZE: usize = 0> {
+ dev: &'bound Device<Bound>,
io: MmioRaw<SIZE>,
}
-impl<const SIZE: usize> IoMem<SIZE> {
- fn ioremap(resource: &Resource) -> Result<Self> {
+impl<'bound, const SIZE: usize> IoMem<'bound, SIZE> {
+ fn ioremap(dev: &'bound Device<Bound>, resource: &Resource) -> Result<Self> {
// Note: Some ioremap() implementations use types that depend on the CPU
// word width rather than the bus address width.
//
@@ -267,28 +263,33 @@ fn ioremap(resource: &Resource) -> Result<Self> {
}
let io = MmioRaw::new(addr as usize, size)?;
- let io = IoMem { io };
- Ok(io)
+ Ok(IoMem { dev, io })
}
- /// Creates a new `IoMem` instance from a previously acquired [`IoRequest`].
- pub fn new<'a>(io_request: IoRequest<'a>) -> impl PinInit<Devres<Self>, Error> + 'a {
- let dev = io_request.device;
- let res = io_request.resource;
-
- Devres::new(dev, Self::ioremap(res))
+ /// Consume the `IoMem` and register it as a device-managed resource.
+ ///
+ /// The returned `Devres<IoMem<'static, SIZE>>` can outlive the original
+ /// lifetime `'bound`. Access to the I/O memory is revoked when the device
+ /// is unbound.
+ pub fn into_devres(self) -> Result<Devres<IoMem<'static, SIZE>>> {
+ // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `IoMem` does not
+ // actually outlive the device -- access is revoked and the resource is released when the
+ // device is unbound.
+ let iomem: IoMem<'static, SIZE> = unsafe { core::mem::transmute(self) };
+ let dev = iomem.dev;
+ Devres::new(dev, iomem)
}
}
-impl<const SIZE: usize> Drop for IoMem<SIZE> {
+impl<const SIZE: usize> Drop for IoMem<'_, SIZE> {
fn drop(&mut self) {
// SAFETY: Safe as by the invariant of `Io`.
unsafe { bindings::iounmap(self.io.addr() as *mut c_void) }
}
}
-impl<const SIZE: usize> Deref for IoMem<SIZE> {
+impl<const SIZE: usize> Deref for IoMem<'_, SIZE> {
type Target = Mmio<SIZE>;
fn deref(&self) -> &Self::Target {
--
2.54.0
next prev parent reply other threads:[~2026-05-06 21:53 UTC|newest]
Thread overview: 30+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-05-06 21:50 [PATCH v2 00/25] rust: device: Higher-Ranked Lifetime Types for device drivers Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 01/25] rust: driver core: drop drvdata before devres release Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 02/25] rust: types: add `ForLt` trait for higher-ranked lifetime support Danilo Krummrich
2026-05-06 22:14 ` Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 03/25] rust: device: generalize drvdata methods over ForLt Danilo Krummrich
2026-05-07 12:50 ` Alice Ryhl
2026-05-06 21:50 ` [PATCH v2 04/25] rust: driver: make Adapter trait lifetime-parameterized Danilo Krummrich
2026-05-07 12:24 ` Gary Guo
2026-05-06 21:50 ` [PATCH v2 05/25] rust: pci: implement Sync for Device<Bound> Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 06/25] rust: platform: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 07/25] rust: auxiliary: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 08/25] rust: usb: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 09/25] rust: device: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 10/25] rust: pci: make Driver trait lifetime-parameterized Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 11/25] rust: platform: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 12/25] rust: auxiliary: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 13/25] rust: auxiliary: generalize Registration over ForLt Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 14/25] samples: rust: rust_driver_auxiliary: showcase lifetime-bound registration data Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 15/25] rust: usb: make Driver trait lifetime-parameterized Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 16/25] rust: i2c: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 17/25] rust: pci: make Bar lifetime-parameterized Danilo Krummrich
2026-05-06 21:50 ` Danilo Krummrich [this message]
2026-05-06 21:50 ` [PATCH v2 19/25] samples: rust: rust_driver_pci: use HRT lifetime for Bar Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 20/25] rust: driver-core: rename 'a lifetime to 'bound Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 21/25] gpu: nova-core: " Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 22/25] gpu: nova-core: use HRT lifetime for Bar Danilo Krummrich
2026-05-06 21:50 ` [PATCH v2 23/25] gpu: nova-core: unregister sysmem flush page from Drop Danilo Krummrich
2026-05-06 21:51 ` [PATCH v2 24/25] gpu: nova-core: replace ARef<Device> with &'bound Device in SysmemFlush Danilo Krummrich
2026-05-06 21:51 ` [PATCH v2 25/25] gpu: drm: tyr: use HRT lifetime for IoMem Danilo Krummrich
2026-05-07 12:14 ` [PATCH v2 00/25] rust: device: Higher-Ranked Lifetime Types for device drivers Dirk Behme
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=20260506215113.851360-19-dakr@kernel.org \
--to=dakr@kernel.org \
--cc=a.hindborg@kernel.org \
--cc=abdiel.janulgue@gmail.com \
--cc=acourbot@nvidia.com \
--cc=aliceryhl@google.com \
--cc=bhelgaas@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=david.m.ertman@intel.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=driver-core@lists.linux.dev \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=igor.korotin@linux.dev \
--cc=ira.weiny@intel.com \
--cc=kwilczynski@kernel.org \
--cc=leon@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux-pwm@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=m.wilczynski@samsung.com \
--cc=markus.probst@posteo.de \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robin.murphy@arm.com \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=ukleinek@kernel.org \
--cc=viresh.kumar@linaro.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox