mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 23:59:33 -04:00
rust: io: implement a view type for Coherent
Implement a `CoherentView` type which is a view of `Coherent`. To be able to give out DMA handles, the view type contains both CPU and DMA pointers, and the projection method projects both at once. Delegate most of the `Io` implementation to `SysMemBackend`. Provide a method to erase the DMA handle and give out a `SysMem` view, if the user does not need the `dma_handle`. Reviewed-by: Alexandre Courbot <acourbot@nvidia.com> Signed-off-by: Gary Guo <gary@garyguo.net> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com> Link: https://patch.msgid.link/20260706-io_projection-v6-15-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich <dakr@kernel.org>
This commit is contained in:
committed by
Danilo Krummrich
parent
2fabff7807
commit
1d409d1e7a
@@ -14,14 +14,21 @@
|
||||
},
|
||||
error::to_result,
|
||||
fs::file,
|
||||
io::{
|
||||
IoBackend,
|
||||
IoBase,
|
||||
IoCapable,
|
||||
SysMem,
|
||||
SysMemBackend, //
|
||||
},
|
||||
prelude::*,
|
||||
ptr::KnownSize,
|
||||
sync::aref::ARef,
|
||||
transmute::{
|
||||
AsBytes,
|
||||
FromBytes, //
|
||||
}, //
|
||||
uaccess::UserSliceWriter,
|
||||
},
|
||||
uaccess::UserSliceWriter, //
|
||||
};
|
||||
use core::{
|
||||
ops::{
|
||||
@@ -1133,6 +1140,133 @@ unsafe impl Send for CoherentHandle {}
|
||||
// plain `Copy` values.
|
||||
unsafe impl Sync for CoherentHandle {}
|
||||
|
||||
/// View type for `Coherent`.
|
||||
///
|
||||
/// This is same as [`SysMem`] but with additional information that allows handing out a DMA handle.
|
||||
pub struct CoherentView<'a, T: ?Sized> {
|
||||
cpu_addr: SysMem<'a, T>,
|
||||
dma_handle: DmaAddress,
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Copy for CoherentView<'_, T> {}
|
||||
impl<T: ?Sized> Clone for CoherentView<'_, T> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized> CoherentView<'a, T> {
|
||||
/// Erase the DMA handle information and obtain a [`SysMem`] view of the same memory region.
|
||||
#[inline]
|
||||
pub fn as_sys_mem(self) -> SysMem<'a, T> {
|
||||
self.cpu_addr
|
||||
}
|
||||
|
||||
/// Returns a DMA handle which may be given to the device as the DMA address base of the region.
|
||||
#[inline]
|
||||
pub fn dma_handle(self) -> DmaAddress {
|
||||
self.dma_handle
|
||||
}
|
||||
|
||||
/// Returns a reference to the data in the region.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * Callers must ensure that the device does not read/write to/from memory while the returned
|
||||
/// reference is live.
|
||||
/// * Callers must ensure that this call does not race with a write (including call to `as_mut`)
|
||||
/// to the same region while the returned reference is live.
|
||||
#[inline]
|
||||
pub unsafe fn as_ref(self) -> &'a T {
|
||||
// SAFETY: pointer is aligned and valid per type invariant. Aliasing rule is satisfied per
|
||||
// safety requirement.
|
||||
unsafe { &*self.cpu_addr.as_ptr() }
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the data in the region.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// * Callers must ensure that the device does not read/write to/from memory while the returned
|
||||
/// reference is live.
|
||||
/// * Callers must ensure that this call does not race with a read (including call to `as_ref`)
|
||||
/// or write (including call to `as_mut`) to the same region while the returned reference is
|
||||
/// live.
|
||||
#[inline]
|
||||
pub unsafe fn as_mut(self) -> &'a mut T {
|
||||
// SAFETY: pointer is aligned and valid per type invariant. Aliasing rule is satisfied per
|
||||
// safety requirement.
|
||||
unsafe { &mut *self.cpu_addr.as_ptr() }
|
||||
}
|
||||
}
|
||||
|
||||
/// `IoBackend` implementation for `Coherent`.
|
||||
pub struct CoherentIoBackend;
|
||||
|
||||
impl IoBackend for CoherentIoBackend {
|
||||
type View<'a, T: ?Sized + KnownSize> = CoherentView<'a, T>;
|
||||
|
||||
#[inline]
|
||||
fn as_ptr<'a, T: ?Sized + KnownSize>(view: Self::View<'a, T>) -> *mut T {
|
||||
SysMemBackend::as_ptr(view.cpu_addr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
unsafe fn project_view<'a, T: ?Sized + KnownSize, U: ?Sized + KnownSize>(
|
||||
view: Self::View<'a, T>,
|
||||
ptr: *mut U,
|
||||
) -> Self::View<'a, U> {
|
||||
let offset = ptr.addr() - view.cpu_addr.as_ptr().addr();
|
||||
// CAST: The offset DMA address can never overflow.
|
||||
let dma_handle = view.dma_handle + offset as DmaAddress;
|
||||
CoherentView {
|
||||
dma_handle,
|
||||
// SAFETY: Per safety requirement.
|
||||
cpu_addr: unsafe { SysMemBackend::project_view(view.cpu_addr, ptr) },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IoCapable<T> for CoherentIoBackend
|
||||
where
|
||||
SysMemBackend: IoCapable<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn io_read<'a>(view: Self::View<'a, T>) -> T {
|
||||
SysMemBackend::io_read(view.cpu_addr)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn io_write<'a>(view: Self::View<'a, T>, value: T) {
|
||||
SysMemBackend::io_write(view.cpu_addr, value)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for CoherentView<'a, T> {
|
||||
type Backend = CoherentIoBackend;
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn as_view(self) -> CoherentView<'a, Self::Target> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T: ?Sized + KnownSize> IoBase<'a> for &'a Coherent<T> {
|
||||
type Backend = CoherentIoBackend;
|
||||
type Target = T;
|
||||
|
||||
#[inline]
|
||||
fn as_view(self) -> CoherentView<'a, Self::Target> {
|
||||
CoherentView {
|
||||
// SAFETY: `cpu_addr` is valid and aligned kernel accessible memory.
|
||||
cpu_addr: unsafe { SysMem::new(self.cpu_addr.as_ptr()) },
|
||||
dma_handle: self.dma_handle,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Reads a field of an item from an allocated region of structs.
|
||||
///
|
||||
/// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating
|
||||
|
||||
Reference in New Issue
Block a user