Skip to content

Commit ad2907b

Browse files
xyn-ackojeda
authored andcommitted
rust: add dma coherent allocator abstraction
Add a simple dma coherent allocator rust abstraction. Based on Andreas Hindborg's dma abstractions from the rnvme driver, which was also based on earlier work by Wedson Almeida Filho. Reviewed-by: Alice Ryhl <[email protected]> Signed-off-by: Abdiel Janulgue <[email protected]> Acked-by: Danilo Krummrich <[email protected]> Link: https://lore.kernel.org/r/[email protected] Nacked-by: Christoph Hellwig <[email protected]> [ Removed period. - Miguel ] Signed-off-by: Miguel Ojeda <[email protected]>
1 parent e385e94 commit ad2907b

File tree

3 files changed

+389
-0
lines changed

3 files changed

+389
-0
lines changed

rust/bindings/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <linux/blkdev.h>
1313
#include <linux/cred.h>
1414
#include <linux/device/faux.h>
15+
#include <linux/dma-mapping.h>
1516
#include <linux/errname.h>
1617
#include <linux/ethtool.h>
1718
#include <linux/file.h>

rust/kernel/dma.rs

Lines changed: 387 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,387 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Direct memory access (DMA).
4+
//!
5+
//! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h)
6+
7+
use crate::{
8+
bindings, build_assert,
9+
device::Device,
10+
error::code::*,
11+
error::Result,
12+
transmute::{AsBytes, FromBytes},
13+
types::ARef,
14+
};
15+
16+
/// Possible attributes associated with a DMA mapping.
17+
///
18+
/// They can be combined with the operators `|`, `&`, and `!`.
19+
///
20+
/// Values can be used from the [`attrs`] module.
21+
///
22+
/// # Examples
23+
///
24+
/// ```
25+
/// use kernel::device::Device;
26+
/// use kernel::dma::{attrs::*, CoherentAllocation};
27+
///
28+
/// # fn test(dev: &Device) -> Result {
29+
/// let attribs = DMA_ATTR_FORCE_CONTIGUOUS | DMA_ATTR_NO_WARN;
30+
/// let c: CoherentAllocation<u64> =
31+
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, attribs)?;
32+
/// # Ok::<(), Error>(()) }
33+
/// ```
34+
#[derive(Clone, Copy, PartialEq)]
35+
#[repr(transparent)]
36+
pub struct Attrs(u32);
37+
38+
impl Attrs {
39+
/// Get the raw representation of this attribute.
40+
pub(crate) fn as_raw(self) -> crate::ffi::c_ulong {
41+
self.0 as _
42+
}
43+
44+
/// Check whether `flags` is contained in `self`.
45+
pub fn contains(self, flags: Attrs) -> bool {
46+
(self & flags) == flags
47+
}
48+
}
49+
50+
impl core::ops::BitOr for Attrs {
51+
type Output = Self;
52+
fn bitor(self, rhs: Self) -> Self::Output {
53+
Self(self.0 | rhs.0)
54+
}
55+
}
56+
57+
impl core::ops::BitAnd for Attrs {
58+
type Output = Self;
59+
fn bitand(self, rhs: Self) -> Self::Output {
60+
Self(self.0 & rhs.0)
61+
}
62+
}
63+
64+
impl core::ops::Not for Attrs {
65+
type Output = Self;
66+
fn not(self) -> Self::Output {
67+
Self(!self.0)
68+
}
69+
}
70+
71+
/// DMA mapping attributes.
72+
pub mod attrs {
73+
use super::Attrs;
74+
75+
/// Specifies that reads and writes to the mapping may be weakly ordered, that is that reads
76+
/// and writes may pass each other.
77+
pub const DMA_ATTR_WEAK_ORDERING: Attrs = Attrs(bindings::DMA_ATTR_WEAK_ORDERING);
78+
79+
/// Specifies that writes to the mapping may be buffered to improve performance.
80+
pub const DMA_ATTR_WRITE_COMBINE: Attrs = Attrs(bindings::DMA_ATTR_WRITE_COMBINE);
81+
82+
/// Lets the platform to avoid creating a kernel virtual mapping for the allocated buffer.
83+
pub const DMA_ATTR_NO_KERNEL_MAPPING: Attrs = Attrs(bindings::DMA_ATTR_NO_KERNEL_MAPPING);
84+
85+
/// Allows platform code to skip synchronization of the CPU cache for the given buffer assuming
86+
/// that it has been already transferred to 'device' domain.
87+
pub const DMA_ATTR_SKIP_CPU_SYNC: Attrs = Attrs(bindings::DMA_ATTR_SKIP_CPU_SYNC);
88+
89+
/// Forces contiguous allocation of the buffer in physical memory.
90+
pub const DMA_ATTR_FORCE_CONTIGUOUS: Attrs = Attrs(bindings::DMA_ATTR_FORCE_CONTIGUOUS);
91+
92+
/// This is a hint to the DMA-mapping subsystem that it's probably not worth the time to try
93+
/// to allocate memory to in a way that gives better TLB efficiency.
94+
pub const DMA_ATTR_ALLOC_SINGLE_PAGES: Attrs = Attrs(bindings::DMA_ATTR_ALLOC_SINGLE_PAGES);
95+
96+
/// This tells the DMA-mapping subsystem to suppress allocation failure reports (similarly to
97+
/// __GFP_NOWARN).
98+
pub const DMA_ATTR_NO_WARN: Attrs = Attrs(bindings::DMA_ATTR_NO_WARN);
99+
100+
/// Used to indicate that the buffer is fully accessible at an elevated privilege level (and
101+
/// ideally inaccessible or at least read-only at lesser-privileged levels).
102+
pub const DMA_ATTR_PRIVILEGED: Attrs = Attrs(bindings::DMA_ATTR_PRIVILEGED);
103+
}
104+
105+
/// An abstraction of the `dma_alloc_coherent` API.
106+
///
107+
/// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map
108+
/// large consistent DMA regions.
109+
///
110+
/// A [`CoherentAllocation`] instance contains a pointer to the allocated region (in the
111+
/// processor's virtual address space) and the device address which can be given to the device
112+
/// as the DMA address base of the region. The region is released once [`CoherentAllocation`]
113+
/// is dropped.
114+
///
115+
/// # Invariants
116+
///
117+
/// For the lifetime of an instance of [`CoherentAllocation`], the `cpu_addr` is a valid pointer
118+
/// to an allocated region of consistent memory and `dma_handle` is the DMA address base of
119+
/// the region.
120+
// TODO
121+
//
122+
// DMA allocations potentially carry device resources (e.g.IOMMU mappings), hence for soundness
123+
// reasons DMA allocation would need to be embedded in a `Devres` container, in order to ensure
124+
// that device resources can never survive device unbind.
125+
//
126+
// However, it is neither desirable nor necessary to protect the allocated memory of the DMA
127+
// allocation from surviving device unbind; it would require RCU read side critical sections to
128+
// access the memory, which may require subsequent unnecessary copies.
129+
//
130+
// Hence, find a way to revoke the device resources of a `CoherentAllocation`, but not the
131+
// entire `CoherentAllocation` including the allocated memory itself.
132+
pub struct CoherentAllocation<T: AsBytes + FromBytes> {
133+
dev: ARef<Device>,
134+
dma_handle: bindings::dma_addr_t,
135+
count: usize,
136+
cpu_addr: *mut T,
137+
dma_attrs: Attrs,
138+
}
139+
140+
impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
141+
/// Allocates a region of `size_of::<T> * count` of consistent memory.
142+
///
143+
/// # Examples
144+
///
145+
/// ```
146+
/// use kernel::device::Device;
147+
/// use kernel::dma::{attrs::*, CoherentAllocation};
148+
///
149+
/// # fn test(dev: &Device) -> Result {
150+
/// let c: CoherentAllocation<u64> =
151+
/// CoherentAllocation::alloc_attrs(dev, 4, GFP_KERNEL, DMA_ATTR_NO_WARN)?;
152+
/// # Ok::<(), Error>(()) }
153+
/// ```
154+
pub fn alloc_attrs(
155+
dev: &Device,
156+
count: usize,
157+
gfp_flags: kernel::alloc::Flags,
158+
dma_attrs: Attrs,
159+
) -> Result<CoherentAllocation<T>> {
160+
build_assert!(
161+
core::mem::size_of::<T>() > 0,
162+
"It doesn't make sense for the allocated type to be a ZST"
163+
);
164+
165+
let size = count
166+
.checked_mul(core::mem::size_of::<T>())
167+
.ok_or(EOVERFLOW)?;
168+
let mut dma_handle = 0;
169+
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
170+
let ret = unsafe {
171+
bindings::dma_alloc_attrs(
172+
dev.as_raw(),
173+
size,
174+
&mut dma_handle,
175+
gfp_flags.as_raw(),
176+
dma_attrs.as_raw(),
177+
)
178+
};
179+
if ret.is_null() {
180+
return Err(ENOMEM);
181+
}
182+
// INVARIANT: We just successfully allocated a coherent region which is accessible for
183+
// `count` elements, hence the cpu address is valid. We also hold a refcounted reference
184+
// to the device.
185+
Ok(Self {
186+
dev: dev.into(),
187+
dma_handle,
188+
count,
189+
cpu_addr: ret as *mut T,
190+
dma_attrs,
191+
})
192+
}
193+
194+
/// Performs the same functionality as [`CoherentAllocation::alloc_attrs`], except the
195+
/// `dma_attrs` is 0 by default.
196+
pub fn alloc_coherent(
197+
dev: &Device,
198+
count: usize,
199+
gfp_flags: kernel::alloc::Flags,
200+
) -> Result<CoherentAllocation<T>> {
201+
CoherentAllocation::alloc_attrs(dev, count, gfp_flags, Attrs(0))
202+
}
203+
204+
/// Returns the base address to the allocated region in the CPU's virtual address space.
205+
pub fn start_ptr(&self) -> *const T {
206+
self.cpu_addr
207+
}
208+
209+
/// Returns the base address to the allocated region in the CPU's virtual address space as
210+
/// a mutable pointer.
211+
pub fn start_ptr_mut(&mut self) -> *mut T {
212+
self.cpu_addr
213+
}
214+
215+
/// Returns a DMA handle which may given to the device as the DMA address base of
216+
/// the region.
217+
pub fn dma_handle(&self) -> bindings::dma_addr_t {
218+
self.dma_handle
219+
}
220+
221+
/// Returns a pointer to an element from the region with bounds checking. `offset` is in
222+
/// units of `T`, not the number of bytes.
223+
///
224+
/// Public but hidden since it should only be used from [`dma_read`] and [`dma_write`] macros.
225+
#[doc(hidden)]
226+
pub fn item_from_index(&self, offset: usize) -> Result<*mut T> {
227+
if offset >= self.count {
228+
return Err(EINVAL);
229+
}
230+
// SAFETY:
231+
// - The pointer is valid due to type invariant on `CoherentAllocation`
232+
// and we've just checked that the range and index is within bounds.
233+
// - `offset` can't overflow since it is smaller than `self.count` and we've checked
234+
// that `self.count` won't overflow early in the constructor.
235+
Ok(unsafe { self.cpu_addr.add(offset) })
236+
}
237+
238+
/// Reads the value of `field` and ensures that its type is [`FromBytes`].
239+
///
240+
/// # Safety
241+
///
242+
/// This must be called from the [`dma_read`] macro which ensures that the `field` pointer is
243+
/// validated beforehand.
244+
///
245+
/// Public but hidden since it should only be used from [`dma_read`] macro.
246+
#[doc(hidden)]
247+
pub unsafe fn field_read<F: FromBytes>(&self, field: *const F) -> F {
248+
// SAFETY:
249+
// - By the safety requirements field is valid.
250+
// - Using read_volatile() here is not sound as per the usual rules, the usage here is
251+
// a special exception with the following notes in place. When dealing with a potential
252+
// race from a hardware or code outside kernel (e.g. user-space program), we need that
253+
// read on a valid memory is not UB. Currently read_volatile() is used for this, and the
254+
// rationale behind is that it should generate the same code as READ_ONCE() which the
255+
// kernel already relies on to avoid UB on data races. Note that the usage of
256+
// read_volatile() is limited to this particular case, it cannot be used to prevent
257+
// the UB caused by racing between two kernel functions nor do they provide atomicity.
258+
unsafe { field.read_volatile() }
259+
}
260+
261+
/// Writes a value to `field` and ensures that its type is [`AsBytes`].
262+
///
263+
/// # Safety
264+
///
265+
/// This must be called from the [`dma_write`] macro which ensures that the `field` pointer is
266+
/// validated beforehand.
267+
///
268+
/// Public but hidden since it should only be used from [`dma_write`] macro.
269+
#[doc(hidden)]
270+
pub unsafe fn field_write<F: AsBytes>(&self, field: *mut F, val: F) {
271+
// SAFETY:
272+
// - By the safety requirements field is valid.
273+
// - Using write_volatile() here is not sound as per the usual rules, the usage here is
274+
// a special exception with the following notes in place. When dealing with a potential
275+
// race from a hardware or code outside kernel (e.g. user-space program), we need that
276+
// write on a valid memory is not UB. Currently write_volatile() is used for this, and the
277+
// rationale behind is that it should generate the same code as WRITE_ONCE() which the
278+
// kernel already relies on to avoid UB on data races. Note that the usage of
279+
// write_volatile() is limited to this particular case, it cannot be used to prevent
280+
// the UB caused by racing between two kernel functions nor do they provide atomicity.
281+
unsafe { field.write_volatile(val) }
282+
}
283+
}
284+
285+
/// Note that the device configured to do DMA must be halted before this object is dropped.
286+
impl<T: AsBytes + FromBytes> Drop for CoherentAllocation<T> {
287+
fn drop(&mut self) {
288+
let size = self.count * core::mem::size_of::<T>();
289+
// SAFETY: Device pointer is guaranteed as valid by the type invariant on `Device`.
290+
// The cpu address, and the dma handle are valid due to the type invariants on
291+
// `CoherentAllocation`.
292+
unsafe {
293+
bindings::dma_free_attrs(
294+
self.dev.as_raw(),
295+
size,
296+
self.cpu_addr as _,
297+
self.dma_handle,
298+
self.dma_attrs.as_raw(),
299+
)
300+
}
301+
}
302+
}
303+
304+
/// Reads a field of an item from an allocated region of structs.
305+
///
306+
/// # Examples
307+
///
308+
/// ```
309+
/// use kernel::device::Device;
310+
/// use kernel::dma::{attrs::*, CoherentAllocation};
311+
///
312+
/// struct MyStruct { field: u32, }
313+
///
314+
/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
315+
/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
316+
/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
317+
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
318+
///
319+
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
320+
/// let whole = kernel::dma_read!(alloc[2]);
321+
/// let field = kernel::dma_read!(alloc[1].field);
322+
/// # Ok::<(), Error>(()) }
323+
/// ```
324+
#[macro_export]
325+
macro_rules! dma_read {
326+
($dma:expr, $idx: expr, $($field:tt)*) => {{
327+
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
328+
// SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
329+
// dereferenced. The compiler also further validates the expression on whether `field`
330+
// is a member of `item` when expanded by the macro.
331+
unsafe {
332+
let ptr_field = ::core::ptr::addr_of!((*item) $($field)*);
333+
$crate::dma::CoherentAllocation::field_read(&$dma, ptr_field)
334+
}
335+
}};
336+
($dma:ident [ $idx:expr ] $($field:tt)* ) => {
337+
$crate::dma_read!($dma, $idx, $($field)*);
338+
};
339+
($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {
340+
$crate::dma_read!($($dma).*, $idx, $($field)*);
341+
};
342+
}
343+
344+
/// Writes to a field of an item from an allocated region of structs.
345+
///
346+
/// # Examples
347+
///
348+
/// ```
349+
/// use kernel::device::Device;
350+
/// use kernel::dma::{attrs::*, CoherentAllocation};
351+
///
352+
/// struct MyStruct { member: u32, }
353+
///
354+
/// // SAFETY: All bit patterns are acceptable values for `MyStruct`.
355+
/// unsafe impl kernel::transmute::FromBytes for MyStruct{};
356+
/// // SAFETY: Instances of `MyStruct` have no uninitialized portions.
357+
/// unsafe impl kernel::transmute::AsBytes for MyStruct{};
358+
///
359+
/// # fn test(alloc: &kernel::dma::CoherentAllocation<MyStruct>) -> Result {
360+
/// kernel::dma_write!(alloc[2].member = 0xf);
361+
/// kernel::dma_write!(alloc[1] = MyStruct { member: 0xf });
362+
/// # Ok::<(), Error>(()) }
363+
/// ```
364+
#[macro_export]
365+
macro_rules! dma_write {
366+
($dma:ident [ $idx:expr ] $($field:tt)*) => {{
367+
$crate::dma_write!($dma, $idx, $($field)*);
368+
}};
369+
($($dma:ident).* [ $idx:expr ] $($field:tt)* ) => {{
370+
$crate::dma_write!($($dma).*, $idx, $($field)*);
371+
}};
372+
($dma:expr, $idx: expr, = $val:expr) => {
373+
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
374+
// SAFETY: `item_from_index` ensures that `item` is always a valid item.
375+
unsafe { $crate::dma::CoherentAllocation::field_write(&$dma, item, $val) }
376+
};
377+
($dma:expr, $idx: expr, $(.$field:ident)* = $val:expr) => {
378+
let item = $crate::dma::CoherentAllocation::item_from_index(&$dma, $idx)?;
379+
// SAFETY: `item_from_index` ensures that `item` is always a valid pointer and can be
380+
// dereferenced. The compiler also further validates the expression on whether `field`
381+
// is a member of `item` when expanded by the macro.
382+
unsafe {
383+
let ptr_field = ::core::ptr::addr_of_mut!((*item) $(.$field)*);
384+
$crate::dma::CoherentAllocation::field_write(&$dma, ptr_field, $val)
385+
}
386+
};
387+
}

0 commit comments

Comments
 (0)