Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

rust: dma: add CoherentHandle for DMA allocations without kernel mapping

Add CoherentHandle, an opaque DMA allocation type for buffers that are
only ever accessed by hardware. Unlike Coherent<T>, it does not provide
CPU access to the allocated memory.

CoherentHandle implicitly sets DMA_ATTR_NO_KERNEL_MAPPING and stores the
value returned by dma_alloc_attrs() as an opaque handle
(NonNull<c_void>) rather than a typed pointer, since with this flag the
C API returns an opaque cookie (e.g. struct page *), not a CPU pointer
to the allocated memory.

Only the DMA bus address is exposed to drivers; the opaque handle is
used solely to free the allocation on drop.

This commit is for reference only; there is currently no in-tree user.

Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://patch.msgid.link/20260321172749.592387-2-dakr@kernel.org
[acourbot: fix conflict in dma.rs.]
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>

authored by

Danilo Krummrich and committed by
Alexandre Courbot
6dd782af 18fb5f1f

+119
+119
rust/kernel/dma.rs
··· 907 907 } 908 908 } 909 909 910 + /// An opaque DMA allocation without a kernel virtual mapping. 911 + /// 912 + /// Unlike [`Coherent`], a `CoherentHandle` does not provide CPU access to the allocated memory. 913 + /// The allocation is always performed with `DMA_ATTR_NO_KERNEL_MAPPING`, meaning no kernel 914 + /// virtual mapping is created for the buffer. The value returned by the C API as the CPU 915 + /// address is an opaque handle used only to free the allocation. 916 + /// 917 + /// This is useful for buffers that are only ever accessed by hardware. 918 + /// 919 + /// # Invariants 920 + /// 921 + /// - `cpu_handle` holds the opaque handle returned by `dma_alloc_attrs` with 922 + /// `DMA_ATTR_NO_KERNEL_MAPPING` set, and is only valid for passing back to `dma_free_attrs`. 923 + /// - `dma_handle` is the corresponding bus address for device DMA. 924 + /// - `size` is the allocation size in bytes as passed to `dma_alloc_attrs`. 925 + /// - `dma_attrs` contains the attributes used for the allocation, always including 926 + /// `DMA_ATTR_NO_KERNEL_MAPPING`. 927 + pub struct CoherentHandle { 928 + dev: ARef<device::Device>, 929 + dma_handle: DmaAddress, 930 + cpu_handle: NonNull<c_void>, 931 + size: usize, 932 + dma_attrs: Attrs, 933 + } 934 + 935 + impl CoherentHandle { 936 + /// Allocates `size` bytes of coherent DMA memory without creating a kernel virtual mapping. 937 + /// 938 + /// Additional DMA attributes may be passed via `dma_attrs`; `DMA_ATTR_NO_KERNEL_MAPPING` is 939 + /// always set implicitly. 940 + /// 941 + /// Returns `EINVAL` if `size` is zero, `ENOMEM` if the allocation fails. 942 + pub fn alloc_with_attrs( 943 + dev: &device::Device<Bound>, 944 + size: usize, 945 + gfp_flags: kernel::alloc::Flags, 946 + dma_attrs: Attrs, 947 + ) -> Result<Self> { 948 + if size == 0 { 949 + return Err(EINVAL); 950 + } 951 + 952 + let dma_attrs = dma_attrs | Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING); 953 + let mut dma_handle = 0; 954 + // SAFETY: `dev.as_raw()` is valid by the type invariant on `device::Device`. 955 + let cpu_handle = unsafe { 956 + bindings::dma_alloc_attrs( 957 + dev.as_raw(), 958 + size, 959 + &mut dma_handle, 960 + gfp_flags.as_raw(), 961 + dma_attrs.as_raw(), 962 + ) 963 + }; 964 + 965 + let cpu_handle = NonNull::new(cpu_handle).ok_or(ENOMEM)?; 966 + 967 + // INVARIANT: `cpu_handle` is the opaque handle from a successful `dma_alloc_attrs` call 968 + // with `DMA_ATTR_NO_KERNEL_MAPPING`, `dma_handle` is the corresponding DMA address, 969 + // and we hold a refcounted reference to the device. 970 + Ok(Self { 971 + dev: dev.into(), 972 + dma_handle, 973 + cpu_handle, 974 + size, 975 + dma_attrs, 976 + }) 977 + } 978 + 979 + /// Allocates `size` bytes of coherent DMA memory without creating a kernel virtual mapping. 980 + #[inline] 981 + pub fn alloc( 982 + dev: &device::Device<Bound>, 983 + size: usize, 984 + gfp_flags: kernel::alloc::Flags, 985 + ) -> Result<Self> { 986 + Self::alloc_with_attrs(dev, size, gfp_flags, Attrs(0)) 987 + } 988 + 989 + /// Returns the DMA handle for this allocation. 990 + /// 991 + /// This address can be programmed into device hardware for DMA access. 992 + #[inline] 993 + pub fn dma_handle(&self) -> DmaAddress { 994 + self.dma_handle 995 + } 996 + 997 + /// Returns the size in bytes of this allocation. 998 + #[inline] 999 + pub fn size(&self) -> usize { 1000 + self.size 1001 + } 1002 + } 1003 + 1004 + impl Drop for CoherentHandle { 1005 + fn drop(&mut self) { 1006 + // SAFETY: All values are valid by the type invariants on `CoherentHandle`. 1007 + // `cpu_handle` is the opaque handle from `dma_alloc_attrs` and is passed back unchanged. 1008 + unsafe { 1009 + bindings::dma_free_attrs( 1010 + self.dev.as_raw(), 1011 + self.size, 1012 + self.cpu_handle.as_ptr(), 1013 + self.dma_handle, 1014 + self.dma_attrs.as_raw(), 1015 + ) 1016 + } 1017 + } 1018 + } 1019 + 1020 + // SAFETY: `CoherentHandle` only holds a device reference, a DMA handle, an opaque CPU handle, 1021 + // and a size. None of these are tied to a specific thread. 1022 + unsafe impl Send for CoherentHandle {} 1023 + 1024 + // SAFETY: `CoherentHandle` provides no CPU access to the underlying allocation. The only 1025 + // operations on `&CoherentHandle` are reading the DMA handle and size, both of which are 1026 + // plain `Copy` values. 1027 + unsafe impl Sync for CoherentHandle {} 1028 + 910 1029 /// Reads a field of an item from an allocated region of structs. 911 1030 /// 912 1031 /// The syntax is of the form `kernel::dma_read!(dma, proj)` where `dma` is an expression evaluating