"Danilo Krummrich" <[email protected]> writes: > Currently, dma::Coherent cannot safely provide (mutable) access to its > underlying memory because the memory might be concurrently accessed by a > DMA device. This makes it difficult to safely initialize the memory > before handing it over to the hardware. > > Introduce dma::CoherentBox, a type that encapsulates a dma::Coherent > before its DMA address is exposed to the device. dma::CoherentBox can > guarantee exclusive access to the inner dma::Coherent and implement > Deref and DerefMut. > > Once the memory is properly initialized, dma::CoherentBox can be > converted into a regular dma::Coherent. > > Reviewed-by: Alice Ryhl <[email protected]> > Signed-off-by: Danilo Krummrich <[email protected]> > --- > rust/kernel/dma.rs | 154 ++++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 153 insertions(+), 1 deletion(-) > > diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs > index db645b01bdd0..cefb54f0424a 100644 > --- a/rust/kernel/dma.rs > +++ b/rust/kernel/dma.rs > @@ -20,7 +20,13 @@ > FromBytes, // > }, // > }; > -use core::ptr::NonNull; > +use core::{ > + ops::{ > + Deref, > + DerefMut, // > + }, > + ptr::NonNull, // > +}; > > /// DMA address type. > /// > @@ -352,6 +358,152 @@ fn from(direction: DataDirection) -> Self { > } > } > > +/// CPU-owned DMA allocation that can be converted into a device-shared > [`Coherent`] object. > +/// > +/// Unlike [`Coherent`], a [`CoherentBox`] is guaranteed to be fully owned > by the CPU -- its DMA > +/// address is not exposed and it cannot be accessed by a device. This means > it can safely be used > +/// like a normal boxed allocation (e.g. direct reads, writes, and mutable > slices are all safe). > +/// > +/// A typical use is to allocate a [`CoherentBox`], populate it with normal > CPU access, and then > +/// convert it into a [`Coherent`] object to share it with the device. > +/// > +/// # Examples > +/// > +/// `CoherentBox<T>`: > +/// > +/// ``` > +/// # use kernel::device::{ > +/// # Bound, > +/// # Device, > +/// # }; > +/// use kernel::dma::{attrs::*, > +/// Coherent, > +/// CoherentBox, > +/// }; > +/// > +/// # fn test(dev: &Device<Bound>) -> Result { > +/// let mut dmem: CoherentBox<u64> = CoherentBox::zeroed(dev, GFP_KERNEL)?; > +/// *dmem = 42; > +/// let dmem: Coherent<u64> = dmem.into(); > +/// # Ok::<(), Error>(()) } > +/// ``` > +/// > +/// `CoherentBox<[T]>`: > +/// > +/// > +/// ``` > +/// # use kernel::device::{ > +/// # Bound, > +/// # Device, > +/// # }; > +/// use kernel::dma::{attrs::*, > +/// Coherent, > +/// CoherentBox, > +/// }; > +/// > +/// # fn test(dev: &Device<Bound>) -> Result { > +/// let mut dmem: CoherentBox<[u64]> = CoherentBox::zeroed_slice(dev, 4, > GFP_KERNEL)?; > +/// dmem.fill(42); > +/// let dmem: Coherent<[u64]> = dmem.into(); > +/// # Ok::<(), Error>(()) } > +/// ``` > +pub struct CoherentBox<T: AsBytes + FromBytes + KnownSize + > ?Sized>(Coherent<T>); > + > +impl<T: AsBytes + FromBytes> CoherentBox<[T]> { > + /// [`CoherentBox`] variant of [`Coherent::zeroed_slice_with_attrs`]. > + #[inline] > + pub fn zeroed_slice_with_attrs( > + dev: &device::Device<Bound>, > + count: usize, > + gfp_flags: kernel::alloc::Flags, > + dma_attrs: Attrs, > + ) -> Result<Self> { > + Coherent::zeroed_slice_with_attrs(dev, count, gfp_flags, > dma_attrs).map(Self) > + } > + > + /// Same as [CoherentBox::zeroed_slice_with_attrs], but with > `dma::Attrs(0)`. > + #[inline] > + pub fn zeroed_slice( > + dev: &device::Device<Bound>, > + count: usize, > + gfp_flags: kernel::alloc::Flags, > + ) -> Result<Self> { > + Self::zeroed_slice_with_attrs(dev, count, gfp_flags, Attrs(0)) > + } > + > + /// Initializes the element at `i` using the given initializer. > + /// > + /// Returns `EINVAL` if `i` is out of bounds.
Could you add an example for this function? Reviewed-by: Andreas Hindborg <[email protected]> Best regards, Andreas Hindborg
