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: introduce dma::CoherentBox for memory initialization

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 <aliceryhl@google.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://patch.msgid.link/20260320194626.36263-5-dakr@kernel.org
[ Remove unnecessary trait bounds. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

+153 -1
+153 -1
rust/kernel/dma.rs
··· 20 20 FromBytes, // 21 21 }, // 22 22 }; 23 - use core::ptr::NonNull; 23 + use core::{ 24 + ops::{ 25 + Deref, 26 + DerefMut, // 27 + }, 28 + ptr::NonNull, // 29 + }; 24 30 25 31 /// DMA address type. 26 32 /// ··· 355 349 // with the enum variants of `DataDirection`, which is a valid assumption given our 356 350 // compile-time checks. 357 351 direction as u32 as Self 352 + } 353 + } 354 + 355 + /// CPU-owned DMA allocation that can be converted into a device-shared [`Coherent`] object. 356 + /// 357 + /// Unlike [`Coherent`], a [`CoherentBox`] is guaranteed to be fully owned by the CPU -- its DMA 358 + /// address is not exposed and it cannot be accessed by a device. This means it can safely be used 359 + /// like a normal boxed allocation (e.g. direct reads, writes, and mutable slices are all safe). 360 + /// 361 + /// A typical use is to allocate a [`CoherentBox`], populate it with normal CPU access, and then 362 + /// convert it into a [`Coherent`] object to share it with the device. 363 + /// 364 + /// # Examples 365 + /// 366 + /// `CoherentBox<T>`: 367 + /// 368 + /// ``` 369 + /// # use kernel::device::{ 370 + /// # Bound, 371 + /// # Device, 372 + /// # }; 373 + /// use kernel::dma::{attrs::*, 374 + /// Coherent, 375 + /// CoherentBox, 376 + /// }; 377 + /// 378 + /// # fn test(dev: &Device<Bound>) -> Result { 379 + /// let mut dmem: CoherentBox<u64> = CoherentBox::zeroed(dev, GFP_KERNEL)?; 380 + /// *dmem = 42; 381 + /// let dmem: Coherent<u64> = dmem.into(); 382 + /// # Ok::<(), Error>(()) } 383 + /// ``` 384 + /// 385 + /// `CoherentBox<[T]>`: 386 + /// 387 + /// 388 + /// ``` 389 + /// # use kernel::device::{ 390 + /// # Bound, 391 + /// # Device, 392 + /// # }; 393 + /// use kernel::dma::{attrs::*, 394 + /// Coherent, 395 + /// CoherentBox, 396 + /// }; 397 + /// 398 + /// # fn test(dev: &Device<Bound>) -> Result { 399 + /// let mut dmem: CoherentBox<[u64]> = CoherentBox::zeroed_slice(dev, 4, GFP_KERNEL)?; 400 + /// dmem.fill(42); 401 + /// let dmem: Coherent<[u64]> = dmem.into(); 402 + /// # Ok::<(), Error>(()) } 403 + /// ``` 404 + pub struct CoherentBox<T: KnownSize + ?Sized>(Coherent<T>); 405 + 406 + impl<T: AsBytes + FromBytes> CoherentBox<[T]> { 407 + /// [`CoherentBox`] variant of [`Coherent::zeroed_slice_with_attrs`]. 408 + #[inline] 409 + pub fn zeroed_slice_with_attrs( 410 + dev: &device::Device<Bound>, 411 + count: usize, 412 + gfp_flags: kernel::alloc::Flags, 413 + dma_attrs: Attrs, 414 + ) -> Result<Self> { 415 + Coherent::zeroed_slice_with_attrs(dev, count, gfp_flags, dma_attrs).map(Self) 416 + } 417 + 418 + /// Same as [CoherentBox::zeroed_slice_with_attrs], but with `dma::Attrs(0)`. 419 + #[inline] 420 + pub fn zeroed_slice( 421 + dev: &device::Device<Bound>, 422 + count: usize, 423 + gfp_flags: kernel::alloc::Flags, 424 + ) -> Result<Self> { 425 + Self::zeroed_slice_with_attrs(dev, count, gfp_flags, Attrs(0)) 426 + } 427 + 428 + /// Initializes the element at `i` using the given initializer. 429 + /// 430 + /// Returns `EINVAL` if `i` is out of bounds. 431 + pub fn init_at<E>(&mut self, i: usize, init: impl Init<T, E>) -> Result 432 + where 433 + Error: From<E>, 434 + { 435 + if i >= self.0.len() { 436 + return Err(EINVAL); 437 + } 438 + 439 + let ptr = &raw mut self[i]; 440 + 441 + // SAFETY: 442 + // - `ptr` is valid, properly aligned, and within this allocation. 443 + // - `T: AsBytes + FromBytes` guarantees all bit patterns are valid, so partial writes on 444 + // error cannot leave the element in an invalid state. 445 + // - The DMA address has not been exposed yet, so there is no concurrent device access. 446 + unsafe { init.__init(ptr)? }; 447 + 448 + Ok(()) 449 + } 450 + } 451 + 452 + impl<T: AsBytes + FromBytes> CoherentBox<T> { 453 + /// Same as [`CoherentBox::zeroed_slice_with_attrs`], but for a single element. 454 + #[inline] 455 + pub fn zeroed_with_attrs( 456 + dev: &device::Device<Bound>, 457 + gfp_flags: kernel::alloc::Flags, 458 + dma_attrs: Attrs, 459 + ) -> Result<Self> { 460 + Coherent::zeroed_with_attrs(dev, gfp_flags, dma_attrs).map(Self) 461 + } 462 + 463 + /// Same as [`CoherentBox::zeroed_slice`], but for a single element. 464 + #[inline] 465 + pub fn zeroed(dev: &device::Device<Bound>, gfp_flags: kernel::alloc::Flags) -> Result<Self> { 466 + Self::zeroed_with_attrs(dev, gfp_flags, Attrs(0)) 467 + } 468 + } 469 + 470 + impl<T: KnownSize + ?Sized> Deref for CoherentBox<T> { 471 + type Target = T; 472 + 473 + #[inline] 474 + fn deref(&self) -> &Self::Target { 475 + // SAFETY: 476 + // - We have not exposed the DMA address yet, so there can't be any concurrent access by a 477 + // device. 478 + // - We have exclusive access to `self.0`. 479 + unsafe { self.0.as_ref() } 480 + } 481 + } 482 + 483 + impl<T: AsBytes + FromBytes + KnownSize + ?Sized> DerefMut for CoherentBox<T> { 484 + #[inline] 485 + fn deref_mut(&mut self) -> &mut Self::Target { 486 + // SAFETY: 487 + // - We have not exposed the DMA address yet, so there can't be any concurrent access by a 488 + // device. 489 + // - We have exclusive access to `self.0`. 490 + unsafe { self.0.as_mut() } 491 + } 492 + } 493 + 494 + impl<T: AsBytes + FromBytes + KnownSize + ?Sized> From<CoherentBox<T>> for Coherent<T> { 495 + #[inline] 496 + fn from(value: CoherentBox<T>) -> Self { 497 + value.0 358 498 } 359 499 } 360 500