aboutsummaryrefslogtreecommitdiff
path: root/rust/kernel
diff options
context:
space:
mode:
authorDave Airlie <airlied@redhat.com>2026-10-13 17:59:46 +1000
committerDave Airlie <airlied@redhat.com>2026-10-13 18:01:37 +1000
commita56b45995b1e6b9ee671485b1fe68b0c7f754a67 (patch)
treea6f83d97cf7acca6eed91b794435e1966b827ab6 /rust/kernel
parent22739ca2a80c9c2bbeace4075249efdc950d3ac1 (diff)
parentd511064b2e732b7152a542d589d07a4814c1b674 (diff)
Merge tag 'drm-rust-next-2026-10-11' of https://gitlab.freedesktop.org/drm/rust/kernel into drm-next
DRM Rust changes for v7.4-rc1 - Cross-subsystem: - I/O (shared from driver-core tree via signed tag rust-io-7.4-rc1): - Reimplement register!() as a procedural macro, require matching base types for register access, and replace relative registers with typed I/O projections. Convert Nova, Tyr and the PCI sample - Add AsRepr/AsReprMut conversions for I/O on compatible wrapper types and transmute helpers with deferred size checks - DMA (shared from driver-core tree via signed tag rust-dma-7.4-rc1): - Tie Coherent, CoherentBox and CoherentHandle to the device's bound lifetime, and allow scoped debugfs files backed by non-'static data - Allow auxiliary registration-data closures to return covariant sub-fields and add UserSliceWriter::write_truncated() - Implement Send and Sync for MapleTree - Add PCI resource_flags() and IORESOURCE_MEM_64, and convert PCI IRQ flags to impl_flags!() - Add I/O view cast() with compile-time size and alignment checks - DRM: - Add Rust dma_fence abstractions with separate producer (DriverFence) and consumer (Fence) types, per-context sequencing, signal callbacks and preallocation. Additional backend operations remain follow-up work. - Initialize DRM registrations and their data in place, removing a separate allocation - Drop the unused flags argument from drm::Registration::new() - Nova: - UAPI and Cross Module API: - Add NovaCoreApi and a lifetime-bound handle for auxiliary child drivers, carried in nova-drm registration data - Expose a render node and extensible INFO ioctl reporting GPU architecture, opaque chip ID, usable VRAM and VRAM BAR sizes, full and short GPU names, and GID, with size discovery and truncation to the userspace buffer size - Move BAR-size queries into GPU info, removing GETPARAM and changing the UAPI - PRAMIN access: - Add typed CPU access to VRAM through the movable 1 MiB BAR0 PRAMIN window, with per-architecture support from Turing through Blackwell - Add GpuMm centralized memory manager, VramAddress type, aperture documentation and optional probe-time self-tests under CONFIG_NOVA_CORE_SELFTESTS - GPU virtual memory and BAR1: - Add VRAM buddy allocation, TLB flushing, MMU v2/v3 page-table types with trait-based generics (PteOps, PdeOps, DualPdeOps, MmuConfig) and a generic walker - Add maple-tree virtual address tracking and multi-page mapping, separating allocation from mapping execution - Add CPU access to mapped VRAM through BAR1 and memory-management self-tests; handle 64-bit BAR0 when locating BAR1 - GPU interrupts and GSP events: - Add GIN (GPU Interrupt and Notification) interrupt-tree support with MSI-X, MSI fallback and per-architecture rearming - Drain GSP events between commands via a threaded SWGEN0 handler - Report OS errors, robust-channel notifications and unknown messages without aborting command waits; hold the queue lock and use one deadline per wait - Disable GSP event interrupts on persistent falcon faults or drain errors - Add a delivery/rearm self-test, interrupt-tree KUnit coverage and interrupt-controller documentation - GSP messaging fixes: - Queue setup RPCs before GSP-RM boot - Correct DMA barriers in both queue directions - Firmware and transport: - Extract a reusable radix3 buffer mapper; zero-pad page-table levels to whole pages to prevent booter reads of stale padding - Correct GSP-FMC naming and boot-flow documentation - Set MCTP header version 1 for FSP messages and retain the command queue's BAR0 reference - Resource handling: - Replace unnecessary BarUser reference counting with in-place ownership and borrowed access; consolidate probe initialization - Declare an unlimited DMA maximum segment size - Tyr: - Use bitfield!() for firmware section flags and memory attributes that do not describe registers - Zero unsupported and newly added GPU-info fields, and limit the doorbell array to 17 entries to fit the mapped register region Signed-off-by: Dave Airlie <airlied@redhat.com> From: "Danilo Krummrich" <dakr@kernel.org> Link: https://patch.msgid.link/DM371PN4TBJT.3LY1B0EYWSI4@kernel.org
Diffstat (limited to 'rust/kernel')
-rw-r--r--rust/kernel/auxiliary.rs17
-rw-r--r--rust/kernel/bitfield.rs9
-rw-r--r--rust/kernel/debugfs.rs26
-rw-r--r--rust/kernel/debugfs/entry.rs4
-rw-r--r--rust/kernel/debugfs/file_ops.rs29
-rw-r--r--rust/kernel/device_id.rs3
-rw-r--r--rust/kernel/dma.rs141
-rw-r--r--rust/kernel/dma_buf/dma_fence.rs1022
-rw-r--r--rust/kernel/dma_buf/mod.rs14
-rw-r--r--rust/kernel/drm/driver.rs77
-rw-r--r--rust/kernel/io.rs220
-rw-r--r--rust/kernel/io/register.rs759
-rw-r--r--rust/kernel/io/resource.rs8
-rw-r--r--rust/kernel/lib.rs3
-rw-r--r--rust/kernel/maple_tree.rs30
-rw-r--r--rust/kernel/mem.rs234
-rw-r--r--rust/kernel/pci.rs14
-rw-r--r--rust/kernel/pci/irq.rs68
-rw-r--r--rust/kernel/sync/atomic.rs4
-rw-r--r--rust/kernel/uaccess.rs18
20 files changed, 1835 insertions, 865 deletions
diff --git a/rust/kernel/auxiliary.rs b/rust/kernel/auxiliary.rs
index 60dfbec8f330..06f816420790 100644
--- a/rust/kernel/auxiliary.rs
+++ b/rust/kernel/auxiliary.rs
@@ -305,6 +305,10 @@ impl Device<device::Bound> {
/// `F` is the [`ForLt`](trait@ForLt) encoding of the data type. The closure receives a pinned
/// reference to the registration data.
///
+ /// The outer reference carries the `&self` lifetime while the inner type carries the HRTB
+ /// lifetime `'a`, implying `'a` outlives `&self`. This allows the closure to coerce covariant
+ /// sub-fields (e.g. `&'a T` to the caller's lifetime) and return them directly in `R`.
+ ///
/// For covariant types that implement [`trait@CovariantForLt`], prefer
/// [`registration_data`](Self::registration_data) which returns a direct reference.
///
@@ -314,13 +318,14 @@ impl Device<device::Bound> {
/// Returns [`ENOENT`] if no registration data has been set, e.g. when the device was
/// registered by a C driver.
#[inline]
- pub fn registration_data_with<F: ForLt + 'static, R>(
- &self,
- f: impl for<'a> FnOnce(Pin<&'a F::Of<'a>>) -> R,
+ pub fn registration_data_with<'this, F: ForLt + 'static, R>(
+ &'this self,
+ f: impl for<'a> FnOnce(Pin<&'this F::Of<'a>>) -> R,
) -> Result<R> {
- // SAFETY: The HRTB closure prevents the caller from smuggling in references with a
- // concrete short lifetime, making the round-trip from `'static` sound regardless of
- // variance.
+ // SAFETY: The HRTB on the inner type prevents the caller from exploiting a specific
+ // choice of `'a`. Covariant sub-fields can be safely coerced to `'this`, while
+ // invariant fields cannot be coerced and thus cannot escape with an incorrect
+ // lifetime.
let pinned = unsafe { self.registration_data_pinned::<F>()? };
Ok(f(pinned))
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index a0d089423f21..15c78790e151 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -346,6 +346,15 @@ macro_rules! bitfield {
Self::from_raw(val)
}
}
+
+ // SAFETY: `$name` is transparent over `$storage` and `$storage` has no interior mutability.
+ unsafe impl $crate::mem::AsRepr for $name {
+ // Normalize `$storage` to the canonical repr type in case it is signed.
+ type Repr = <$storage as $crate::mem::AsRepr>::Repr;
+ }
+
+ // SAFETY: `$name` is transparent over `$storage`.
+ unsafe impl $crate::mem::AsReprMut for $name {}
};
// Definitions requiring knowledge of individual fields: private and public field accessors,
diff --git a/rust/kernel/debugfs.rs b/rust/kernel/debugfs.rs
index d7b8014a6474..2beb55d444ca 100644
--- a/rust/kernel/debugfs.rs
+++ b/rust/kernel/debugfs.rs
@@ -538,7 +538,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
}
}
- fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &'static FileOps<T>) {
+ fn create_file<T: Sync>(&self, name: &CStr, data: &'data T, vtable: &FileOps<T>) {
#[cfg(CONFIG_DEBUG_FS)]
core::mem::forget(Entry::file(name, &self.entry, data, vtable));
}
@@ -550,7 +550,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn read_only_file<T: Writer + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
+ pub fn read_only_file<T: Writer + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
@@ -560,11 +560,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn read_binary_file<T: BinaryWriter + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn read_binary_file<T: BinaryWriter + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
@@ -596,11 +592,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn read_write_file<T: Writer + Reader + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn read_write_file<T: Writer + Reader + Send + Sync>(&self, name: &CStr, data: &'data T) {
let vtable = &<T as ReadWriteFile<_>>::FILE_OPS;
self.create_file(name, data, vtable)
}
@@ -612,7 +604,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync + 'static>(
+ pub fn read_write_binary_file<T: BinaryWriter + BinaryReader + Send + Sync>(
&self,
name: &CStr,
data: &'data T,
@@ -655,7 +647,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
/// This function does not produce an owning handle to the file. The created
/// file is removed when the [`Scope`] that this directory belongs
/// to is dropped.
- pub fn write_only_file<T: Reader + Send + Sync + 'static>(&self, name: &CStr, data: &'data T) {
+ pub fn write_only_file<T: Reader + Send + Sync>(&self, name: &CStr, data: &'data T) {
let vtable = &<T as WriteFile<_>>::FILE_OPS;
self.create_file(name, data, vtable)
}
@@ -666,11 +658,7 @@ impl<'data, 'dir> ScopedDir<'data, 'dir> {
///
/// This function does not produce an owning handle to the file. The created file is removed
/// when the [`Scope`] that this directory belongs to is dropped.
- pub fn write_binary_file<T: BinaryReader + Send + Sync + 'static>(
- &self,
- name: &CStr,
- data: &'data T,
- ) {
+ pub fn write_binary_file<T: BinaryReader + Send + Sync>(&self, name: &CStr, data: &'data T) {
self.create_file(name, data, &T::FILE_OPS)
}
diff --git a/rust/kernel/debugfs/entry.rs b/rust/kernel/debugfs/entry.rs
index 46aad64896ec..88a870d8c295 100644
--- a/rust/kernel/debugfs/entry.rs
+++ b/rust/kernel/debugfs/entry.rs
@@ -74,7 +74,7 @@ impl Entry<'static> {
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
- &**file_ops,
+ file_ops.fops(),
)
};
@@ -127,7 +127,7 @@ impl<'a> Entry<'a> {
parent.as_ptr(),
core::ptr::from_ref(data) as *mut c_void,
core::ptr::null(),
- &**file_ops,
+ file_ops.fops(),
)
};
diff --git a/rust/kernel/debugfs/file_ops.rs b/rust/kernel/debugfs/file_ops.rs
index f15908f71c4a..47acc4851d47 100644
--- a/rust/kernel/debugfs/file_ops.rs
+++ b/rust/kernel/debugfs/file_ops.rs
@@ -20,9 +20,6 @@ use crate::{
use core::marker::PhantomData;
-#[cfg(CONFIG_DEBUG_FS)]
-use core::ops::Deref;
-
/// # Invariant
///
/// `FileOps<T>` will always contain an `operations` which is safe to use for a file backed
@@ -30,7 +27,7 @@ use core::ops::Deref;
/// into a reference.
pub(super) struct FileOps<T> {
#[cfg(CONFIG_DEBUG_FS)]
- operations: bindings::file_operations,
+ operations: &'static bindings::file_operations,
#[cfg(CONFIG_DEBUG_FS)]
mode: u16,
_phantom: PhantomData<T>,
@@ -41,7 +38,7 @@ impl<T> FileOps<T> {
///
/// The caller asserts that the provided `operations` is safe to use for a file whose
/// inode has a pointer to `T` in its private data that is safe to convert into a reference.
- const unsafe fn new(operations: bindings::file_operations, mode: u16) -> Self {
+ const unsafe fn new(operations: &'static bindings::file_operations, mode: u16) -> Self {
Self {
#[cfg(CONFIG_DEBUG_FS)]
operations,
@@ -65,11 +62,11 @@ impl<T: Adapter> FileOps<T> {
}
#[cfg(CONFIG_DEBUG_FS)]
-impl<T> Deref for FileOps<T> {
- type Target = bindings::file_operations;
-
- fn deref(&self) -> &Self::Target {
- &self.operations
+impl<T> FileOps<T> {
+ /// Returns a `'static` reference to the inner `file_operations`.
+ #[inline]
+ pub(crate) fn fops(&self) -> &'static bindings::file_operations {
+ self.operations
}
}
@@ -130,7 +127,7 @@ pub(crate) trait ReadFile<T> {
impl<T: Writer + Sync> ReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
read: Some(bindings::seq_read),
llseek: Some(bindings::seq_lseek),
release: Some(bindings::single_release),
@@ -181,7 +178,7 @@ pub(crate) trait ReadWriteFile<T> {
impl<T: Writer + Reader + Sync> ReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
open: Some(writer_open::<T>),
read: Some(bindings::seq_read),
write: Some(write::<T>),
@@ -238,7 +235,7 @@ pub(crate) trait WriteFile<T> {
impl<T: Reader + Sync> WriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
open: Some(write_only_open),
write: Some(write_only_write::<T>),
llseek: Some(bindings::noop_llseek),
@@ -290,7 +287,7 @@ pub(crate) trait BinaryReadFile<T> {
impl<T: BinaryWriter + Sync> BinaryReadFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
read: Some(blob_read::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
@@ -344,7 +341,7 @@ pub(crate) trait BinaryWriteFile<T> {
impl<T: BinaryReader + Sync> BinaryWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
open: Some(bindings::simple_open),
@@ -368,7 +365,7 @@ pub(crate) trait BinaryReadWriteFile<T> {
impl<T: BinaryWriter + BinaryReader + Sync> BinaryReadWriteFile<T> for T {
const FILE_OPS: FileOps<T> = {
- let operations = bindings::file_operations {
+ let operations = &bindings::file_operations {
read: Some(blob_read::<T>),
write: Some(blob_write::<T>),
llseek: Some(bindings::default_llseek),
diff --git a/rust/kernel/device_id.rs b/rust/kernel/device_id.rs
index c81fca5b4986..f0b9cb84e58e 100644
--- a/rust/kernel/device_id.rs
+++ b/rust/kernel/device_id.rs
@@ -146,8 +146,7 @@ impl<T: RawDeviceId, const N: usize> IdArray<T, (), N> {
/// If the device implements [`RawDeviceIdIndex`], consider using [`IdArray::new`] instead.
pub const fn new_without_index(ids: [T; N]) -> Self {
// SAFETY: `T` is layout-wise compatible with `T::RawType`, so is the array of them.
- let raw_ids: [MaybeUninit<T::RawType>; N] = unsafe { core::mem::transmute_copy(&ids) };
- core::mem::forget(ids);
+ let raw_ids: [MaybeUninit<T::RawType>; N] = unsafe { crate::mem::transmute(ids) };
Self {
ids: raw_ids,
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
index 2ce09f8e90c6..4ce914b7d1da 100644
--- a/rust/kernel/dma.rs
+++ b/rust/kernel/dma.rs
@@ -24,7 +24,6 @@ use crate::{
},
prelude::*,
ptr::KnownSize,
- sync::aref::ARef,
transmute::{
AsBytes,
FromBytes, //
@@ -223,7 +222,7 @@ impl DmaMask {
///
/// # fn test(dev: &Device<Bound>) -> Result {
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
-/// let c: Coherent<[u64]> =
+/// let c: Coherent<'_, [u64]> =
/// Coherent::zeroed_slice_with_attrs(dev, 4, GFP_KERNEL, attribs)?;
/// # Ok::<(), Error>(()) }
/// ```
@@ -390,9 +389,9 @@ impl From<DataDirection> for bindings::dma_data_direction {
/// };
///
/// # fn test(dev: &Device<Bound>) -> Result {
-/// let mut dmem: CoherentBox<u64> = CoherentBox::zeroed(dev, GFP_KERNEL)?;
+/// let mut dmem: CoherentBox<'_, u64> = CoherentBox::zeroed(dev, GFP_KERNEL)?;
/// *dmem = 42;
-/// let dmem: Coherent<u64> = dmem.into();
+/// let dmem: Coherent<'_, u64> = dmem.into();
/// # Ok::<(), Error>(()) }
/// ```
///
@@ -410,18 +409,18 @@ impl From<DataDirection> for bindings::dma_data_direction {
/// };
///
/// # fn test(dev: &Device<Bound>) -> Result {
-/// let mut dmem: CoherentBox<[u64]> = CoherentBox::zeroed_slice(dev, 4, GFP_KERNEL)?;
+/// let mut dmem: CoherentBox<'_, [u64]> = CoherentBox::zeroed_slice(dev, 4, GFP_KERNEL)?;
/// dmem.fill(42);
-/// let dmem: Coherent<[u64]> = dmem.into();
+/// let dmem: Coherent<'_, [u64]> = dmem.into();
/// # Ok::<(), Error>(()) }
/// ```
-pub struct CoherentBox<T: KnownSize + ?Sized>(Coherent<T>);
+pub struct CoherentBox<'a, T: KnownSize + ?Sized>(Coherent<'a, T>);
-impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
+impl<'a, T: AsBytes + FromBytes> CoherentBox<'a, [T]> {
/// [`CoherentBox`] variant of [`Coherent::zeroed_slice_with_attrs`].
#[inline]
pub fn zeroed_slice_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
@@ -432,7 +431,7 @@ impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
/// Same as [CoherentBox::zeroed_slice_with_attrs], but with `dma::Attrs(0)`.
#[inline]
pub fn zeroed_slice(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
count: usize,
gfp_flags: kernel::alloc::Flags,
) -> Result<Self> {
@@ -480,14 +479,14 @@ impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
///
/// # fn test(dev: &Device<Bound>) -> Result {
/// let data = [0u8, 1u8, 2u8, 3u8];
- /// let c: CoherentBox<[u8]> =
+ /// let c: CoherentBox<'_, [u8]> =
/// CoherentBox::from_slice_with_attrs(dev, &data, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
///
/// assert_eq!(c.deref(), &data);
/// # Ok::<(), Error>(()) }
/// ```
pub fn from_slice_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
data: &[T],
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
@@ -512,7 +511,7 @@ impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
/// `dma_attrs` is 0 by default.
#[inline]
pub fn from_slice(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
data: &[T],
gfp_flags: kernel::alloc::Flags,
) -> Result<Self>
@@ -523,11 +522,11 @@ impl<T: AsBytes + FromBytes> CoherentBox<[T]> {
}
}
-impl<T: AsBytes + FromBytes> CoherentBox<T> {
+impl<'a, T: AsBytes + FromBytes> CoherentBox<'a, T> {
/// Same as [`CoherentBox::zeroed_slice_with_attrs`], but for a single element.
#[inline]
pub fn zeroed_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
) -> Result<Self> {
@@ -536,12 +535,12 @@ impl<T: AsBytes + FromBytes> CoherentBox<T> {
/// Same as [`CoherentBox::zeroed_slice`], but for a single element.
#[inline]
- pub fn zeroed(dev: &device::Device<Bound>, gfp_flags: kernel::alloc::Flags) -> Result<Self> {
+ pub fn zeroed(dev: &'a device::Device<Bound>, gfp_flags: kernel::alloc::Flags) -> Result<Self> {
Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0))
}
}
-impl<T: KnownSize + ?Sized> Deref for CoherentBox<T> {
+impl<T: KnownSize + ?Sized> Deref for CoherentBox<'_, T> {
type Target = T;
#[inline]
@@ -554,7 +553,7 @@ impl<T: KnownSize + ?Sized> Deref for CoherentBox<T> {
}
}
-impl<T: AsBytes + FromBytes + KnownSize + ?Sized> DerefMut for CoherentBox<T> {
+impl<T: AsBytes + FromBytes + KnownSize + ?Sized> DerefMut for CoherentBox<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
// SAFETY:
@@ -565,9 +564,9 @@ impl<T: AsBytes + FromBytes + KnownSize + ?Sized> DerefMut for CoherentBox<T> {
}
}
-impl<T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<T>> for Coherent<T> {
+impl<'a, T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<'a, T>> for Coherent<'a, T> {
#[inline]
- fn from(value: CoherentBox<T>) -> Self {
+ fn from(value: CoherentBox<'a, T>) -> Self {
value.0
}
}
@@ -588,26 +587,20 @@ impl<T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<T>> for Coher
/// to an allocated region of coherent memory and `dma_addr` is the DMA address base of the
/// region.
/// - The size in bytes of the allocation is equal to size information via pointer.
-// TODO
//
-// DMA allocations potentially carry device resources (e.g.IOMMU mappings), hence for soundness
-// reasons DMA allocation would need to be embedded in a `Devres` container, in order to ensure
-// that device resources can never survive device unbind.
-//
-// However, it is neither desirable nor necessary to protect the allocated memory of the DMA
-// allocation from surviving device unbind; it would require RCU read side critical sections to
-// access the memory, which may require subsequent unnecessary copies.
-//
-// Hence, find a way to revoke the device resources of a `Coherent`, but not the
-// entire `Coherent` including the allocated memory itself.
-pub struct Coherent<T: KnownSize + ?Sized> {
- dev: ARef<device::Device>,
+// The lifetime parameter ties DMA allocations to the device's bound scope, ensuring they are freed
+// before the device is unbound under normal circumstances. However, if a `Coherent` is leaked (e.g.
+// via `mem::forget`), device resources such as IOMMU mappings will not be released. Making all
+// constructors `unsafe` to prevent this is considered too restrictive for the common case; this
+// soundness hole is accepted for now.
+pub struct Coherent<'a, T: KnownSize + ?Sized> {
+ dev: &'a device::Device<Bound>,
dma_addr: DmaAddress,
cpu_addr: NonNull<T>,
dma_attrs: Attrs,
}
-impl<T: KnownSize + ?Sized> Coherent<T> {
+impl<T: KnownSize + ?Sized> Coherent<'_, T> {
/// Returns the size in bytes of this allocation.
#[inline]
pub fn size(&self) -> usize {
@@ -663,10 +656,10 @@ impl<T: KnownSize + ?Sized> Coherent<T> {
}
}
-impl<T: AsBytes + FromBytes> Coherent<T> {
+impl<'a, T: AsBytes + FromBytes> Coherent<'a, T> {
/// Allocates a region of `T` of coherent memory.
fn alloc_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
) -> Result<Self> {
@@ -692,9 +685,9 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
// INVARIANT:
// - We just successfully allocated a coherent region which is adequately sized for `T`,
// hence the cpu address is valid.
- // - We also hold a refcounted reference to the device.
+ // - `dev` is a valid reference to a bound device that outlives this allocation.
Ok(Self {
- dev: dev.into(),
+ dev,
dma_addr,
cpu_addr,
dma_attrs,
@@ -716,13 +709,13 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// };
///
/// # fn test(dev: &Device<Bound>) -> Result {
- /// let c: Coherent<[u64; 4]> =
+ /// let c: Coherent<'_, [u64; 4]> =
/// Coherent::zeroed_with_attrs(dev, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
/// # Ok::<(), Error>(()) }
/// ```
#[inline]
pub fn zeroed_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
) -> Result<Self> {
@@ -732,14 +725,14 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// Performs the same functionality as [`Coherent::zeroed_with_attrs`], except the
/// `dma_attrs` is 0 by default.
#[inline]
- pub fn zeroed(dev: &device::Device<Bound>, gfp_flags: kernel::alloc::Flags) -> Result<Self> {
+ pub fn zeroed(dev: &'a device::Device<Bound>, gfp_flags: kernel::alloc::Flags) -> Result<Self> {
Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0))
}
/// Same as [`Coherent::zeroed_with_attrs`], but instead of a zero-initialization the memory is
/// initialized with `init`.
pub fn init_with_attrs<E>(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
init: impl Init<T, E>,
@@ -764,7 +757,7 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// with `init`.
#[inline]
pub fn init<E>(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
gfp_flags: kernel::alloc::Flags,
init: impl Init<T, E>,
) -> Result<Self>
@@ -776,11 +769,11 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// Allocates a region of `[T; len]` of coherent memory.
fn alloc_slice_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
len: usize,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
- ) -> Result<Coherent<[T]>> {
+ ) -> Result<Coherent<'a, [T]>> {
const {
assert!(
core::mem::size_of::<T>() > 0,
@@ -809,9 +802,9 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
// INVARIANT:
// - We just successfully allocated a coherent region which is adequately sized for
// `[T; len]`, hence the cpu address is valid.
- // - We also hold a refcounted reference to the device.
+ // - `dev` is a valid reference to a bound device that outlives this allocation.
Ok(Coherent {
- dev: dev.into(),
+ dev,
dma_addr,
cpu_addr,
dma_attrs,
@@ -836,17 +829,17 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// };
///
/// # fn test(dev: &Device<Bound>) -> Result {
- /// let c: Coherent<[u64]> =
+ /// let c: Coherent<'_, [u64]> =
/// Coherent::zeroed_slice_with_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
/// # Ok::<(), Error>(()) }
/// ```
#[inline]
pub fn zeroed_slice_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
len: usize,
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
- ) -> Result<Coherent<[T]>> {
+ ) -> Result<Coherent<'a, [T]>> {
Coherent::alloc_slice_with_attrs(dev, len, gfp_flags | __GFP_ZERO, dma_attrs)
}
@@ -854,10 +847,10 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// `dma_attrs` is 0 by default.
#[inline]
pub fn zeroed_slice(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
len: usize,
gfp_flags: kernel::alloc::Flags,
- ) -> Result<Coherent<[T]>> {
+ ) -> Result<Coherent<'a, [T]>> {
Self::zeroed_slice_with_attrs(dev, len, gfp_flags, Attrs(0))
}
@@ -876,18 +869,18 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// # fn test(dev: &Device<Bound>) -> Result {
/// let data = [0u8, 1u8, 2u8, 3u8];
/// // `c` has the same content as `data`.
- /// let c: Coherent<[u8]> =
+ /// let c: Coherent<'_, [u8]> =
/// Coherent::from_slice_with_attrs(dev, &data, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
///
/// # Ok::<(), Error>(()) }
/// ```
#[inline]
pub fn from_slice_with_attrs(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
data: &[T],
gfp_flags: kernel::alloc::Flags,
dma_attrs: Attrs,
- ) -> Result<Coherent<[T]>>
+ ) -> Result<Coherent<'a, [T]>>
where
T: Copy,
{
@@ -898,10 +891,10 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
/// `dma_attrs` is 0 by default.
#[inline]
pub fn from_slice(
- dev: &device::Device<Bound>,
+ dev: &'a device::Device<Bound>,
data: &[T],
gfp_flags: kernel::alloc::Flags,
- ) -> Result<Coherent<[T]>>
+ ) -> Result<Coherent<'a, [T]>>
where
T: Copy,
{
@@ -909,7 +902,7 @@ impl<T: AsBytes + FromBytes> Coherent<T> {
}
}
-impl<T> Coherent<[T]> {
+impl<T> Coherent<'_, [T]> {
/// Returns the number of elements `T` in this allocation.
///
/// Note that this is not the size of the allocation in bytes, which is provided by
@@ -922,10 +915,10 @@ impl<T> Coherent<[T]> {
}
/// Note that the device configured to do DMA must be halted before this object is dropped.
-impl<T: KnownSize + ?Sized> Drop for Coherent<T> {
+impl<T: KnownSize + ?Sized> Drop for Coherent<'_, T> {
fn drop(&mut self) {
let size = T::size(self.cpu_addr.as_ptr());
- // SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
+ // SAFETY: Device pointer is guaranteed as valid by the lifetime of this `Coherent`.
// The cpu address, and the dma address are valid due to the type invariants on
// `Coherent`.
unsafe {
@@ -942,15 +935,15 @@ impl<T: KnownSize + ?Sized> Drop for Coherent<T> {
// SAFETY: It is safe to send a `Coherent` to another thread if `T`
// can be sent to another thread.
-unsafe impl<T: KnownSize + Send + ?Sized> Send for Coherent<T> {}
+unsafe impl<T: KnownSize + Send + ?Sized> Send for Coherent<'_, T> {}
// SAFETY: Sharing `&Coherent` across threads is safe if `T` is `Sync`, because all
// methods that access the buffer contents (`field_read`, `field_write`, `as_slice`,
// `as_slice_mut`) are `unsafe`, and callers are responsible for ensuring no data races occur.
// The safe methods only return metadata or raw pointers whose use requires `unsafe`.
-unsafe impl<T: KnownSize + ?Sized + AsBytes + FromBytes + Sync> Sync for Coherent<T> {}
+unsafe impl<T: KnownSize + ?Sized + AsBytes + FromBytes + Sync> Sync for Coherent<'_, T> {}
-impl<T: KnownSize + AsBytes + ?Sized> debugfs::BinaryWriter for Coherent<T> {
+impl<T: KnownSize + AsBytes + ?Sized> debugfs::BinaryWriter for Coherent<'_, T> {
fn write_to_slice(
&self,
writer: &mut UserSliceWriter,
@@ -996,15 +989,15 @@ impl<T: KnownSize + AsBytes + ?Sized> debugfs::BinaryWriter for Coherent<T> {
/// - `size` is the allocation size in bytes as passed to `dma_alloc_attrs`.