* [PATCH v5 0/2] Add dma coherent allocator abstraction
@ 2024-12-03 17:07 Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 1/2] rust: error: Add EOVERFLOW Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 2/2] rust: add dma coherent allocator abstraction Abdiel Janulgue
0 siblings, 2 replies; 5+ messages in thread
From: Abdiel Janulgue @ 2024-12-03 17:07 UTC (permalink / raw)
To: rust-for-linux
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Wedson Almeida Filho,
Valentin Obst, open list, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, airlied, open list:DMA MAPPING HELPERS,
Abdiel Janulgue
This series adds support for a basic Rust abstraction for the dma coherent
allocator API.
Changes since v4:
- Documentation and example fixes, use Markdown formatting (Miguel Ojeda).
- Discard read()/write() helpers to remove bound on Copy and fix overhead
(Daniel Almeida).
- Improve error-handling in the constructor block (Andreas Hindborg).
- Link to v3: https://lore.kernel.org/all/20241105104726.3111058-1-abdiel.janulgue@gmail.com/
Changes since v3:
- Reject ZST types by checking the type size in the constructor in
addition to requiring FromBytes/AsBytes traits for the type (Alice Ryhl).
Changes since v2:
- Fixed missing header for generating the bindings.
Changes since v1:
- Fix missing info in commit log where EOVERFLOW is used.
- Restrict the dma coherent allocator to numeric types for now for valid
behaviour (Daniel Almeida).
- Build slice dynamically.
Abdiel Janulgue (2):
rust: error: Add EOVERFLOW
rust: add dma coherent allocator abstraction.
rust/bindings/bindings_helper.h | 1 +
rust/kernel/dma.rs | 136 ++++++++++++++++++++++++++++++++
rust/kernel/error.rs | 1 +
rust/kernel/lib.rs | 1 +
4 files changed, 139 insertions(+)
create mode 100644 rust/kernel/dma.rs
base-commit: 40384c840ea1944d7c5a392e8975ed088ecf0b37
--
2.43.0
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH v5 1/2] rust: error: Add EOVERFLOW
2024-12-03 17:07 [PATCH v5 0/2] Add dma coherent allocator abstraction Abdiel Janulgue
@ 2024-12-03 17:07 ` Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 2/2] rust: add dma coherent allocator abstraction Abdiel Janulgue
1 sibling, 0 replies; 5+ messages in thread
From: Abdiel Janulgue @ 2024-12-03 17:07 UTC (permalink / raw)
To: rust-for-linux
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Wedson Almeida Filho,
Valentin Obst, open list, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, airlied, open list:DMA MAPPING HELPERS,
Abdiel Janulgue
Trivial addition for missing EOVERFLOW error. This is used by a
subsequent patch that might require returning EOVERFLOW as a result
of `checked_mul`.
Signed-off-by: Abdiel Janulgue <abdiel.janulgue@gmail.com>
---
rust/kernel/error.rs | 1 +
1 file changed, 1 insertion(+)
diff --git a/rust/kernel/error.rs b/rust/kernel/error.rs
index 52c502432447..cd57fac7f1f9 100644
--- a/rust/kernel/error.rs
+++ b/rust/kernel/error.rs
@@ -63,6 +63,7 @@ macro_rules! declare_err {
declare_err!(EPIPE, "Broken pipe.");
declare_err!(EDOM, "Math argument out of domain of func.");
declare_err!(ERANGE, "Math result not representable.");
+ declare_err!(EOVERFLOW, "Value too large for defined data type.");
declare_err!(ERESTARTSYS, "Restart the system call.");
declare_err!(ERESTARTNOINTR, "System call was interrupted by a signal and will be restarted.");
declare_err!(ERESTARTNOHAND, "Restart if no handler.");
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH v5 2/2] rust: add dma coherent allocator abstraction.
2024-12-03 17:07 [PATCH v5 0/2] Add dma coherent allocator abstraction Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 1/2] rust: error: Add EOVERFLOW Abdiel Janulgue
@ 2024-12-03 17:07 ` Abdiel Janulgue
2024-12-04 19:08 ` Daniel Almeida
1 sibling, 1 reply; 5+ messages in thread
From: Abdiel Janulgue @ 2024-12-03 17:07 UTC (permalink / raw)
To: rust-for-linux
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Wedson Almeida Filho,
Valentin Obst, open list, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, airlied, open list:DMA MAPPING HELPERS,
Abdiel Janulgue
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.
Signed-off-by: Abdiel Janulgue <abdiel.janulgue@gmail.com>
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/dma.rs | 136 ++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
3 files changed, 138 insertions(+)
create mode 100644 rust/kernel/dma.rs
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 5c4dfe22f41a..49bf713b9bb6 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -11,6 +11,7 @@
#include <linux/blk_types.h>
#include <linux/blkdev.h>
#include <linux/cred.h>
+#include <linux/dma-mapping.h>
#include <linux/errname.h>
#include <linux/ethtool.h>
#include <linux/file.h>
diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
new file mode 100644
index 000000000000..9279be235fcf
--- /dev/null
+++ b/rust/kernel/dma.rs
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Direct memory access (DMA).
+//!
+//! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h)
+
+use crate::{
+ bindings,
+ build_assert,
+ device::Device,
+ error::code::*,
+ error::Result,
+ types::ARef,
+ transmute::{AsBytes, FromBytes},
+};
+
+/// An abstraction of the `dma_alloc_coherent` API.
+///
+/// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map
+/// large consistent DMA regions.
+///
+/// A [`CoherentAllocation`] instance contains a pointer to the allocated region (in the
+/// processor's virtual address space) and the device address which can be given to the device
+/// as the DMA address base of the region. The region is released once [`CoherentAllocation`]
+/// is dropped.
+///
+/// # Invariants
+///
+/// For the lifetime of an instance of [`CoherentAllocation`], the cpu address is a valid pointer
+/// to an allocated region of consistent memory and we hold a reference to the device.
+pub struct CoherentAllocation<T: AsBytes + FromBytes> {
+ dev: ARef<Device>,
+ dma_handle: bindings::dma_addr_t,
+ count: usize,
+ cpu_addr: *mut T,
+}
+
+impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
+ /// Allocates a region of `size_of::<T> * count` of consistent memory.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::device::Device;
+ /// use kernel::dma::CoherentAllocation;
+ ///
+ /// # fn test(dev: &Device) -> Result {
+ /// let c: CoherentAllocation<u64> = CoherentAllocation::alloc_coherent(dev, 4, GFP_KERNEL)?;
+ /// # Ok::<(), Error>(()) }
+ /// ```
+ pub fn alloc_coherent(
+ dev: &Device,
+ count: usize,
+ flags: kernel::alloc::Flags,
+ ) -> Result<CoherentAllocation<T>> {
+ build_assert!(core::mem::size_of::<T>() > 0,
+ "It doesn't make sense for the allocated type to be a ZST");
+
+ let size = count.checked_mul(core::mem::size_of::<T>()).ok_or(EOVERFLOW)?;
+ let mut dma_handle = 0;
+ // SAFETY: device pointer is guaranteed as valid by invariant on `Device`.
+ // We ensure that we catch the failure on this function and throw an ENOMEM
+ let ret = unsafe {
+ bindings::dma_alloc_attrs(
+ dev.as_raw(),
+ size,
+ &mut dma_handle, flags.as_raw(),
+ 0,
+ )
+ };
+ if ret.is_null() {
+ return Err(ENOMEM)
+ }
+ // INVARIANT: We just successfully allocated a coherent region which is accessible for
+ // `count` elements, hence the cpu address is valid. We also hold a refcounted reference
+ // to the device.
+ Ok(Self {
+ dev: dev.into(),
+ dma_handle,
+ count,
+ cpu_addr: ret as *mut T,
+ })
+ }
+
+ /// Returns the base address to the allocated region and the dma handle. The caller takes
+ /// ownership of the returned resources.
+ pub fn into_parts(self) -> (usize, bindings::dma_addr_t) {
+ let ret = (self.cpu_addr as _, self.dma_handle);
+ core::mem::forget(self);
+ ret
+ }
+
+ /// Returns the base address to the allocated region in the CPU's virtual address space.
+ pub fn start_ptr(&self) -> *const T {
+ self.cpu_addr as _
+ }
+
+ /// Returns the base address to the allocated region in the CPU's virtual address space as
+ /// a mutable pointer.
+ pub fn start_ptr_mut(&mut self) -> *mut T {
+ self.cpu_addr
+ }
+
+ /// Returns a DMA handle which may given to the device as the DMA address base of
+ /// the region.
+ pub fn dma_handle(&self) -> bindings::dma_addr_t {
+ self.dma_handle
+ }
+
+ /// Returns the CPU-addressable region as a slice.
+ pub fn cpu_buf<'a>(&self) -> &'a [T]
+ {
+ // SAFETY: The pointer is valid due to type invariant on `CoherentAllocation` and
+ // is valid for reads for `self.count * size_of::<T>` bytes.
+ unsafe { core::slice::from_raw_parts(self.cpu_addr, self.count) }
+ }
+
+ /// Performs the same functionality as `cpu_buf`, except that a mutable slice is returned.
+ pub fn cpu_buf_mut<'a>(&mut self) -> &'a mut [T]
+ {
+ // SAFETY: The pointer is valid due to type invariant on `CoherentAllocation` and
+ // is valid for reads for `self.count * size_of::<T>` bytes.
+ unsafe { core::slice::from_raw_parts_mut(self.cpu_addr, self.count) }
+ }
+}
+
+impl<T: AsBytes + FromBytes> Drop for CoherentAllocation<T> {
+ fn drop(&mut self) {
+ let size = self.count * core::mem::size_of::<T>();
+ // SAFETY: the device, cpu address, and the dma handle is valid due to the
+ // type invariants on `CoherentAllocation`.
+ unsafe { bindings::dma_free_attrs(self.dev.as_raw(), size,
+ self.cpu_addr as _,
+ self.dma_handle, 0) }
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index e1065a7551a3..6e90ebf5a130 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -35,6 +35,7 @@
mod build_assert;
pub mod cred;
pub mod device;
+pub mod dma;
pub mod error;
#[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
pub mod firmware;
--
2.43.0
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v5 2/2] rust: add dma coherent allocator abstraction.
2024-12-03 17:07 ` [PATCH v5 2/2] rust: add dma coherent allocator abstraction Abdiel Janulgue
@ 2024-12-04 19:08 ` Daniel Almeida
2024-12-05 10:15 ` Abdiel Janulgue
0 siblings, 1 reply; 5+ messages in thread
From: Daniel Almeida @ 2024-12-04 19:08 UTC (permalink / raw)
To: Abdiel Janulgue
Cc: rust-for-linux, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Wedson Almeida Filho,
Valentin Obst, open list, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, airlied, open list:DMA MAPPING HELPERS
Hi Abdiel,
> On 3 Dec 2024, at 14:07, Abdiel Janulgue <abdiel.janulgue@gmail.com> wrote:
>
> 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.
>
> Signed-off-by: Abdiel Janulgue <abdiel.janulgue@gmail.com>
> ---
> rust/bindings/bindings_helper.h | 1 +
> rust/kernel/dma.rs | 136 ++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 3 files changed, 138 insertions(+)
> create mode 100644 rust/kernel/dma.rs
>
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 5c4dfe22f41a..49bf713b9bb6 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -11,6 +11,7 @@
> #include <linux/blk_types.h>
> #include <linux/blkdev.h>
> #include <linux/cred.h>
> +#include <linux/dma-mapping.h>
> #include <linux/errname.h>
> #include <linux/ethtool.h>
> #include <linux/file.h>
> diff --git a/rust/kernel/dma.rs b/rust/kernel/dma.rs
> new file mode 100644
> index 000000000000..9279be235fcf
> --- /dev/null
> +++ b/rust/kernel/dma.rs
> @@ -0,0 +1,136 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Direct memory access (DMA).
> +//!
> +//! C header: [`include/linux/dma-mapping.h`](srctree/include/linux/dma-mapping.h)
> +
> +use crate::{
> + bindings,
> + build_assert,
> + device::Device,
> + error::code::*,
> + error::Result,
> + types::ARef,
> + transmute::{AsBytes, FromBytes},
> +};
> +
> +/// An abstraction of the `dma_alloc_coherent` API.
> +///
> +/// This is an abstraction around the `dma_alloc_coherent` API which is used to allocate and map
> +/// large consistent DMA regions.
> +///
> +/// A [`CoherentAllocation`] instance contains a pointer to the allocated region (in the
> +/// processor's virtual address space) and the device address which can be given to the device
> +/// as the DMA address base of the region. The region is released once [`CoherentAllocation`]
> +/// is dropped.
> +///
> +/// # Invariants
> +///
> +/// For the lifetime of an instance of [`CoherentAllocation`], the cpu address is a valid pointer
> +/// to an allocated region of consistent memory and we hold a reference to the device.
> +pub struct CoherentAllocation<T: AsBytes + FromBytes> {
> + dev: ARef<Device>,
> + dma_handle: bindings::dma_addr_t,
> + count: usize,
> + cpu_addr: *mut T,
> +}
> +
> +impl<T: AsBytes + FromBytes> CoherentAllocation<T> {
> + /// Allocates a region of `size_of::<T> * count` of consistent memory.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::device::Device;
> + /// use kernel::dma::CoherentAllocation;
> + ///
> + /// # fn test(dev: &Device) -> Result {
> + /// let c: CoherentAllocation<u64> = CoherentAllocation::alloc_coherent(dev, 4, GFP_KERNEL)?;
> + /// # Ok::<(), Error>(()) }
> + /// ```
> + pub fn alloc_coherent(
> + dev: &Device,
> + count: usize,
> + flags: kernel::alloc::Flags,
> + ) -> Result<CoherentAllocation<T>> {
> + build_assert!(core::mem::size_of::<T>() > 0,
> + "It doesn't make sense for the allocated type to be a ZST");
> +
> + let size = count.checked_mul(core::mem::size_of::<T>()).ok_or(EOVERFLOW)?;
> + let mut dma_handle = 0;
> + // SAFETY: device pointer is guaranteed as valid by invariant on `Device`.
> + // We ensure that we catch the failure on this function and throw an ENOMEM
> + let ret = unsafe {
> + bindings::dma_alloc_attrs(
> + dev.as_raw(),
> + size,
> + &mut dma_handle, flags.as_raw(),
> + 0,
> + )
> + };
> + if ret.is_null() {
> + return Err(ENOMEM)
> + }
> + // INVARIANT: We just successfully allocated a coherent region which is accessible for
> + // `count` elements, hence the cpu address is valid. We also hold a refcounted reference
> + // to the device.
> + Ok(Self {
> + dev: dev.into(),
> + dma_handle,
> + count,
> + cpu_addr: ret as *mut T,
> + })
> + }
> +
> + /// Returns the base address to the allocated region and the dma handle. The caller takes
> + /// ownership of the returned resources.
> + pub fn into_parts(self) -> (usize, bindings::dma_addr_t) {
> + let ret = (self.cpu_addr as _, self.dma_handle);
> + core::mem::forget(self);
> + ret
> + }
> +
> + /// Returns the base address to the allocated region in the CPU's virtual address space.
> + pub fn start_ptr(&self) -> *const T {
> + self.cpu_addr as _
> + }
> +
> + /// Returns the base address to the allocated region in the CPU's virtual address space as
> + /// a mutable pointer.
> + pub fn start_ptr_mut(&mut self) -> *mut T {
> + self.cpu_addr
> + }
> +
> + /// Returns a DMA handle which may given to the device as the DMA address base of
> + /// the region.
> + pub fn dma_handle(&self) -> bindings::dma_addr_t {
> + self.dma_handle
> + }
> +
> + /// Returns the CPU-addressable region as a slice.
> + pub fn cpu_buf<'a>(&self) -> &'a [T]
Why this ‘a here? Lifetime elision rules should tie the lifetime of the returned slice
to the lifetime of &self automatically
> + {
> + // SAFETY: The pointer is valid due to type invariant on `CoherentAllocation` and
> + // is valid for reads for `self.count * size_of::<T>` bytes.
> + unsafe { core::slice::from_raw_parts(self.cpu_addr, self.count) }
> + }
> +
> + /// Performs the same functionality as `cpu_buf`, except that a mutable slice is returned.
> + pub fn cpu_buf_mut<'a>(&mut self) -> &'a mut [T]
Same here
> + {
> + // SAFETY: The pointer is valid due to type invariant on `CoherentAllocation` and
> + // is valid for reads for `self.count * size_of::<T>` bytes.
> + unsafe { core::slice::from_raw_parts_mut(self.cpu_addr, self.count) }
> + }
> +}
> +
> +impl<T: AsBytes + FromBytes> Drop for CoherentAllocation<T> {
> + fn drop(&mut self) {
> + let size = self.count * core::mem::size_of::<T>();
> + // SAFETY: the device, cpu address, and the dma handle is valid due to the
> + // type invariants on `CoherentAllocation`.
> + unsafe { bindings::dma_free_attrs(self.dev.as_raw(), size,
> + self.cpu_addr as _,
> + self.dma_handle, 0) }
> + }
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index e1065a7551a3..6e90ebf5a130 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -35,6 +35,7 @@
> mod build_assert;
> pub mod cred;
> pub mod device;
> +pub mod dma;
> pub mod error;
> #[cfg(CONFIG_RUST_FW_LOADER_ABSTRACTIONS)]
> pub mod firmware;
> --
> 2.43.0
>
>
By the way, I tested this using a slightly modified version of the sample Rust platform driver
from Danilo’s v3 submission. It’s working as intended :)
With the change above, you can add:
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
— Daniel
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v5 2/2] rust: add dma coherent allocator abstraction.
2024-12-04 19:08 ` Daniel Almeida
@ 2024-12-05 10:15 ` Abdiel Janulgue
0 siblings, 0 replies; 5+ messages in thread
From: Abdiel Janulgue @ 2024-12-05 10:15 UTC (permalink / raw)
To: Daniel Almeida
Cc: rust-for-linux, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Benno Lossin, Andreas Hindborg, Alice Ryhl,
Trevor Gross, Danilo Krummrich, Wedson Almeida Filho,
Valentin Obst, open list, Christoph Hellwig, Marek Szyprowski,
Robin Murphy, airlied, open list:DMA MAPPING HELPERS
On 04/12/2024 21:08, Daniel Almeida wrote:
>>
>
>
> By the way, I tested this using a slightly modified version of the sample Rust platform driver
> from Danilo’s v3 submission. It’s working as intended :)
>
> With the change above, you can add:
Will do. Thanks for the review!
Regards,
Abdiel
>
> Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
> Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
>
> — Daniel
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2024-12-05 10:15 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-12-03 17:07 [PATCH v5 0/2] Add dma coherent allocator abstraction Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 1/2] rust: error: Add EOVERFLOW Abdiel Janulgue
2024-12-03 17:07 ` [PATCH v5 2/2] rust: add dma coherent allocator abstraction Abdiel Janulgue
2024-12-04 19:08 ` Daniel Almeida
2024-12-05 10:15 ` Abdiel Janulgue
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox