Implement ForLt for Bar<'static, SIZE> so that DevresLt can shorten the stored 'static lifetime back to the caller's borrow lifetime via ForLt::cast_ref. Without this, Devres<Bar<'static, SIZE>>::access() would return &'bound Bar<'static, SIZE>, allowing the inner 'static to leak through methods like pdev().
Add a DevresBar<SIZE> type alias for convenience. Signed-off-by: Danilo Krummrich <[email protected]> --- rust/kernel/pci.rs | 1 + rust/kernel/pci/io.rs | 30 ++++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs index 1335857cae94..265d06b18e42 100644 --- a/rust/kernel/pci.rs +++ b/rust/kernel/pci.rs @@ -48,6 +48,7 @@ ConfigSpace, ConfigSpaceKind, ConfigSpaceSize, + DevresBar, Extended, Normal, // }; diff --git a/rust/kernel/pci/io.rs b/rust/kernel/pci/io.rs index 5668394a155b..7a5b5210cf66 100644 --- a/rust/kernel/pci/io.rs +++ b/rust/kernel/pci/io.rs @@ -6,7 +6,7 @@ use crate::{ bindings, device, - devres::Devres, + devres::DevresLt, io::{ Io, IoCapable, @@ -14,7 +14,8 @@ Mmio, MmioRaw, // }, - prelude::*, // + prelude::*, + types::ForLt, // }; use core::{ marker::PhantomData, @@ -151,6 +152,18 @@ pub struct Bar<'bound, const SIZE: usize = 0> { num: i32, } +// SAFETY: `Bar<'bound, SIZE>` is covariant over `'bound` -- it holds +// `&'bound Device<Bound>`, which is covariant. Shortening the lifetime +// is therefore sound. +unsafe impl<const SIZE: usize> ForLt for Bar<'static, SIZE> { + type Of<'bound> = Bar<'bound, SIZE>; +} + +/// A device-managed PCI BAR mapping. +/// +/// See [`Bar::into_devres`]. +pub type DevresBar<const SIZE: usize> = DevresLt<Bar<'static, SIZE>>; + impl<'bound, const SIZE: usize> Bar<'bound, SIZE> { pub(super) fn new(pdev: &'bound Device<device::Bound>, num: u32, name: &CStr) -> Result<Self> { let len = pdev.resource_len(num)?; @@ -219,15 +232,16 @@ fn release(&self) { /// Consume the `Bar` and register it as a device-managed resource. /// - /// The returned `Devres<Bar<'static, SIZE>>` can outlive the original lifetime `'bound`. Access - /// to the BAR is revoked when the device is unbound. - pub fn into_devres(self) -> Result<Devres<Bar<'static, SIZE>>> { - // SAFETY: Casting to `'static` is sound because `Devres` guarantees the `Bar` does not + /// Access methods on the returned [`DevresLt`] shorten the inner lifetime via + /// [`ForLt::cast_ref`], so the transmuted `'static` is never exposed to callers. + pub fn into_devres(self) -> Result<DevresLt<Bar<'static, SIZE>>> { + // SAFETY: Casting to `'static` is sound because `DevresLt` guarantees the `Bar` does not // actually outlive the device -- access is revoked and the resource is released when the - // device is unbound. + // device is unbound. The `ForLt` encoding ensures `access()` shortens the lifetime back + // to the caller's borrow, preventing `'static` from leaking. let bar: Bar<'static, SIZE> = unsafe { core::mem::transmute(self) }; let pdev = bar.pdev; - Devres::new(pdev.as_ref(), bar) + DevresLt::new(pdev.as_ref(), bar) } } -- 2.54.0
