mirror of
https://github.com/torvalds/linux
synced 2026-07-22 18:20:51 +09:00
rust: io: make IoMem and ExclusiveIoMem lifetime-parameterized
Add a lifetime parameter to IoMem<'a, SIZE> and ExclusiveIoMem<'a, SIZE>, storing a &'a Device<Bound> reference to tie the mapping to the device's lifetime. This mirrors the pci::Bar<'a, 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> Reviewed-by: Eliot Courtney <ecourtney@nvidia.com> Reviewed-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Reviewed-by: Gary Guo <gary@garyguo.net> Link: https://patch.msgid.link/20260525202921.124698-20-dakr@kernel.org Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
@@ -37,7 +37,7 @@ use crate::{
|
||||
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;
|
||||
|
||||
@@ -110,7 +110,7 @@ impl platform::Driver for TyrPlatformDriverData {
|
||||
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)?;
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -352,7 +352,7 @@ impl platform::Driver for Th1520PwmPlatformDriver {
|
||||
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,
|
||||
}),
|
||||
)?;
|
||||
|
||||
@@ -74,22 +74,19 @@ impl<'a> IoRequest<'a> {
|
||||
/// //
|
||||
/// // 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<'a, SIZE>> {
|
||||
IoMem::ioremap(self.device, self.resource)
|
||||
}
|
||||
|
||||
/// Same as [`Self::iomap_sized`] but with exclusive access to the
|
||||
@@ -98,10 +95,8 @@ impl<'a> IoRequest<'a> {
|
||||
/// 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<'a, SIZE>> {
|
||||
ExclusiveIoMem::ioremap(self.device, self.resource)
|
||||
}
|
||||
|
||||
/// Maps an [`IoRequest`] where the size is not known at compile time,
|
||||
@@ -140,27 +135,24 @@ impl<'a> IoRequest<'a> {
|
||||
/// // 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 iomem = request.iomap()?;
|
||||
///
|
||||
/// let io = iomem.access(pdev.as_ref())?;
|
||||
/// let data = iomem.try_read32(offset)?;
|
||||
///
|
||||
/// let data = io.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<'a>> {
|
||||
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<'a, 0>> {
|
||||
self.iomap_exclusive_sized::<0>()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,9 +161,9 @@ impl<'a> IoRequest<'a> {
|
||||
/// # Invariants
|
||||
///
|
||||
/// - [`ExclusiveIoMem`] has exclusive access to the underlying [`IoMem`].
|
||||
pub struct ExclusiveIoMem<const SIZE: usize> {
|
||||
pub struct ExclusiveIoMem<'a, const SIZE: usize> {
|
||||
/// The underlying `IoMem` instance.
|
||||
iomem: IoMem<SIZE>,
|
||||
iomem: IoMem<'a, SIZE>,
|
||||
|
||||
/// The region abstraction. This represents exclusive access to the
|
||||
/// range represented by the underlying `iomem`.
|
||||
@@ -180,9 +172,9 @@ pub struct ExclusiveIoMem<const SIZE: usize> {
|
||||
_region: Region,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
|
||||
impl<'a, const SIZE: usize> ExclusiveIoMem<'a, SIZE> {
|
||||
/// Creates a new `ExclusiveIoMem` instance.
|
||||
fn ioremap(resource: &Resource) -> Result<Self> {
|
||||
fn ioremap(dev: &'a Device<Bound>, resource: &Resource) -> Result<Self> {
|
||||
let start = resource.start();
|
||||
let size = resource.size();
|
||||
let name = resource.name().unwrap_or_default();
|
||||
@@ -196,26 +188,29 @@ impl<const SIZE: usize> ExclusiveIoMem<SIZE> {
|
||||
)
|
||||
.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
|
||||
/// `'a`. 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 {
|
||||
@@ -232,12 +227,13 @@ impl<const SIZE: usize> Deref for ExclusiveIoMem<SIZE> {
|
||||
///
|
||||
/// [`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<'a, const SIZE: usize = 0> {
|
||||
dev: &'a Device<Bound>,
|
||||
io: MmioRaw<SIZE>,
|
||||
}
|
||||
|
||||
impl<const SIZE: usize> IoMem<SIZE> {
|
||||
fn ioremap(resource: &Resource) -> Result<Self> {
|
||||
impl<'a, const SIZE: usize> IoMem<'a, SIZE> {
|
||||
fn ioremap(dev: &'a 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.
|
||||
//
|
||||
@@ -269,28 +265,33 @@ impl<const SIZE: usize> IoMem<SIZE> {
|
||||
}
|
||||
|
||||
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 `'a`. 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 {
|
||||
|
||||
Reference in New Issue
Block a user