* [PATCH 1/2] rust: scatterlist: tie owned tables to the device's bound lifetime
@ 2026-11-14 23:42 Danilo Krummrich
2026-11-14 23:42 ` [PATCH 2/2] gpu: nova-core: remove unnecessary firmware pin-init Danilo Krummrich
0 siblings, 1 reply; 2+ messages in thread
From: Danilo Krummrich @ 2026-11-14 23:42 UTC (permalink / raw)
To: dakr, acourbot, aliceryhl, abdiel.janulgue, daniel.almeida,
robin.murphy, a.hindborg, ojeda, boqun, gary, bjorn3_gh, lossin,
tmgross, tamird, work
Cc: driver-core, nova-gpu, dri-devel, linux-kernel, rust-for-linux
Add a lifetime parameter to Owned that ties owned scatter-gather tables
to the device's bound scope, ensuring their DMA mappings are released
before the device is unbound.
The DMA mapping currently holds a pointer into Owned, requiring the
table to stay at a fixed address and its cleanup to be ordered around
the device-managed mapping.
Let DmaMappedSgt own RawSGTable and borrow Device<Bound>. Its destructor
unmaps the entries before RawSGTable frees them, and Owned releases the
backing pages last. This removes the internal pointer and makes the
mapping constructor safe without a self-reference.
Carry the device lifetime through Owned and its users. Remove pin
initialization from Owned and SGTable, return tables by value, and update
the callers accordingly.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/gpu/nova-core/firmware/radix3.rs | 29 +++--
rust/kernel/scatterlist.rs | 141 ++++++++---------------
samples/rust/rust_dma.rs | 11 +-
3 files changed, 62 insertions(+), 119 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-core/firmware/radix3.rs
index 94235bd1209d..a3e43fc8ec86 100644
--- a/drivers/gpu/nova-core/firmware/radix3.rs
+++ b/drivers/gpu/nova-core/firmware/radix3.rs
@@ -35,14 +35,11 @@
#[pin_data]
pub(crate) struct Radix3<'a> {
/// The mapped data.
- #[pin]
- data: SGTable<Owned<VVec<u8>>>,
+ data: SGTable<Owned<'a, VVec<u8>>>,
/// Level 2: one entry per page of `data`.
- #[pin]
- level2: SGTable<Owned<VVec<u8>>>,
+ level2: SGTable<Owned<'a, VVec<u8>>>,
/// Level 1: one entry per page of `level2`.
- #[pin]
- level1: SGTable<Owned<VVec<u8>>>,
+ level1: SGTable<Owned<'a, VVec<u8>>>,
/// Level 0: one page, whose single entry is the DMA address of the first `level1` page.
level0: Coherent<'a, [u64]>,
/// Length of `data`, in bytes.
@@ -59,14 +56,14 @@ pub(crate) fn new(
pin_init::pin_init_scope(move || {
Ok(try_pin_init!(Self {
- data <- SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL),
- level2 <- {
- build_lvl(&data)
- .map(|l2| SGTable::new(dev, l2, DataDirection::ToDevice, GFP_KERNEL))?
+ data: SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL)?,
+ level2: {
+ let l2 = build_lvl(data)?;
+ SGTable::new(dev, l2, DataDirection::ToDevice, GFP_KERNEL)?
},
- level1 <- {
- build_lvl(&level2)
- .map(|l1| SGTable::new(dev, l1, DataDirection::ToDevice, GFP_KERNEL))?
+ level1: {
+ let l1 = build_lvl(level2)?;
+ SGTable::new(dev, l1, DataDirection::ToDevice, GFP_KERNEL)?
},
level0: {
// Allocate the level 0 page table as a device-visible DMA object, and map the
@@ -81,7 +78,7 @@ pub(crate) fn new(
let mut level0 = CoherentBox::<'_, [u64]>::zeroed_slice(
dev,
GSP_PAGE_SIZE / size_of::<u64>(),
- GFP_KERNEL
+ GFP_KERNEL,
)?;
level0[0] = level1_entry_addr.to_le();
@@ -105,7 +102,7 @@ pub(crate) fn size(&self) -> usize {
/// Returns the size of the level that maps `sg_table`: one `u64` entry per [`GSP_PAGE_SIZE`]
/// page of each DMA-mapped region, rounded up to whole pages.
-fn lvl_size(sg_table: &SGTable<Owned<VVec<u8>>>) -> usize {
+fn lvl_size(sg_table: &SGTable<Owned<'_, VVec<u8>>>) -> usize {
let entries: usize = sg_table
.iter()
.map(|sg_entry| usize::from_safe_cast(sg_entry.dma_len()).div_ceil(GSP_PAGE_SIZE))
@@ -122,7 +119,7 @@ fn lvl_size(sg_table: &SGTable<Owned<VVec<u8>>>) -> usize {
///
/// - `ENOMEM` if the level cannot be allocated.
/// - `EINVAL` if `sg_table` yields more entries than [`lvl_size`] sized the level for.
-fn build_lvl(sg_table: &SGTable<Owned<VVec<u8>>>) -> Result<VVec<u8>> {
+fn build_lvl(sg_table: &SGTable<Owned<'_, VVec<u8>>>) -> Result<VVec<u8>> {
let mut dst = VVec::<u8>::zeroed(lvl_size(sg_table), GFP_KERNEL).map_err(|_| ENOMEM)?;
let mut entries = dst.chunks_exact_mut(size_of::<u64>());
diff --git a/rust/kernel/scatterlist.rs b/rust/kernel/scatterlist.rs
index 6379a8748117..93f431d7f548 100644
--- a/rust/kernel/scatterlist.rs
+++ b/rust/kernel/scatterlist.rs
@@ -19,7 +19,8 @@
//! The main entry point is the [`SGTable`] type, which represents a complete scatter-gather table.
//! It can be either:
//!
-//! - An owned table ([`SGTable<Owned<P>>`]), created from a Rust memory buffer (e.g., [`VVec`]).
+//! - An owned table ([`SGTable<Owned<'a, P>>`]), created from a Rust memory buffer (e.g.,
+//! [`VVec`]) and tied to the [`Device<Bound>`] lifetime.
//! This type manages the allocation of the `struct sg_table`, the DMA mapping of the buffer, and
//! the automatic cleanup of all resources.
//! - A borrowed reference (&[`SGTable`]), which provides safe, read-only access to a table that was
@@ -33,15 +34,13 @@
alloc::allocator::VmallocPageIter,
bindings,
device::{Bound, Device},
- devres::Devres,
dma,
error,
page,
prelude::*,
- sync::aref::ARef,
types::Opaque, //
};
-use core::{ops::Deref, ptr::NonNull};
+use core::ops::Deref;
/// A single entry in a scatter-gather list.
///
@@ -123,9 +122,7 @@ unsafe impl Sync for Borrowed {}
///
/// All [`SGTable`] variants can be iterated over the individual [`SGEntry`]s.
#[repr(transparent)]
-#[pin_data]
pub struct SGTable<T: private::Sealed = Borrowed> {
- #[pin]
inner: T,
}
@@ -177,63 +174,41 @@ pub fn iter(&self) -> SGTableIter<'_> {
///
/// # Invariants
///
-/// - `sgt` is a valid pointer to a `struct sg_table` for the entire lifetime of the
-/// [`DmaMappedSgt`].
/// - `sgt` is always DMA mapped.
-struct DmaMappedSgt {
- sgt: NonNull<bindings::sg_table>,
- dev: ARef<Device>,
+struct DmaMappedSgt<'a> {
+ dev: &'a Device<Bound>,
+ sgt: RawSGTable,
dir: dma::DataDirection,
}
-// SAFETY: `DmaMappedSgt` can be sent to any task.
-unsafe impl Send for DmaMappedSgt {}
-
-// SAFETY: `DmaMappedSgt` has no interior mutability and can be accessed concurrently.
-unsafe impl Sync for DmaMappedSgt {}
-
-impl DmaMappedSgt {
- /// # Safety
- ///
- /// - `sgt` must be a valid pointer to a `struct sg_table` for the entire lifetime of the
- /// returned [`DmaMappedSgt`].
- /// - The caller must guarantee that `sgt` remains DMA mapped for the entire lifetime of
- /// [`DmaMappedSgt`].
- unsafe fn new(
- sgt: NonNull<bindings::sg_table>,
- dev: &Device<Bound>,
- dir: dma::DataDirection,
- ) -> Result<Self> {
+impl<'a> DmaMappedSgt<'a> {
+ /// Takes ownership of `sgt` and maps it for DMA.
+ fn new(dev: &'a Device<Bound>, sgt: RawSGTable, dir: dma::DataDirection) -> Result<Self> {
// SAFETY:
// - `dev.as_raw()` is a valid pointer to a `struct device`, which is guaranteed to be
// bound to a driver for the duration of this call.
- // - `sgt` is a valid pointer to a `struct sg_table`.
+ // - `sgt.as_raw()` is a valid pointer to a `struct sg_table`.
error::to_result(unsafe {
- bindings::dma_map_sgtable(dev.as_raw(), sgt.as_ptr(), dir.into(), 0)
+ bindings::dma_map_sgtable(dev.as_raw(), sgt.as_raw(), dir.into(), 0)
})?;
- // INVARIANT: By the safety requirements of this function it is guaranteed that `sgt` is
- // valid for the entire lifetime of this object instance.
- Ok(Self {
- sgt,
- dev: dev.into(),
- dir,
- })
+ // INVARIANT: `sgt` was successfully DMA mapped and is owned by this object until drop.
+ Ok(Self { dev, sgt, dir })
}
}
-impl Drop for DmaMappedSgt {
+impl Drop for DmaMappedSgt<'_> {
#[inline]
fn drop(&mut self) {
// SAFETY:
// - `self.dev.as_raw()` is a pointer to a valid `struct device`.
// - `self.dev` is the same device the mapping has been created for in `Self::new()`.
- // - `self.sgt.as_ptr()` is a valid pointer to a `struct sg_table` by the type invariants
- // of `Self`.
+ // - `self.sgt.as_raw()` is a valid pointer to a `struct sg_table` for the lifetime of
+ // `self`.
// - `self.dir` is the same `dma::DataDirection` the mapping has been created with in
// `Self::new()`.
unsafe {
- bindings::dma_unmap_sgtable(self.dev.as_raw(), self.sgt.as_ptr(), self.dir.into(), 0)
+ bindings::dma_unmap_sgtable(self.dev.as_raw(), self.sgt.as_raw(), self.dir.into(), 0)
};
}
}
@@ -308,38 +283,27 @@ fn drop(&mut self) {
///
/// - The backing memory pages.
/// - The `struct sg_table` allocation (`sgt`).
-/// - The DMA mapping, managed through a [`Devres`]-managed `DmaMappedSgt`.
+/// - The DMA mapping, released before the table and backing pages are freed.
///
/// Users interact with this type through the [`SGTable`] handle and do not need to manage
/// [`Owned`] directly.
-#[pin_data]
-pub struct Owned<P> {
+pub struct Owned<'a, P> {
// Note: The drop order is relevant; we first have to unmap the `struct sg_table`, then free the
// `struct sg_table` and finally free the backing pages.
- #[pin]
- dma: Devres<DmaMappedSgt>,
- #[pin]
- sgt: RawSGTable,
+ dma: DmaMappedSgt<'a>,
_pages: P,
}
-// SAFETY: `Owned` can be sent to any task if `P` can be send to any task.
-unsafe impl<P: Send> Send for Owned<P> {}
-
-// SAFETY: `Owned` has no interior mutability and can be accessed concurrently if `P` can be
-// accessed concurrently.
-unsafe impl<P: Sync> Sync for Owned<P> {}
-
-impl<P> Owned<P>
+impl<'a, P> Owned<'a, P>
where
- for<'a> P: page::AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
+ for<'b> P: page::AsPageIter<Iter<'b> = VmallocPageIter<'b>> + 'static,
{
fn new(
- dev: &Device<Bound>,
+ dev: &'a Device<Bound>,
mut pages: P,
dir: dma::DataDirection,
flags: alloc::Flags,
- ) -> Result<impl PinInit<Self, Error> + '_> {
+ ) -> Result<Self> {
let page_iter = pages.page_iter();
let size = page_iter.size();
@@ -367,34 +331,22 @@ fn new(
let max_segment = max_mapping_size.min(max_seg_size);
- Ok(try_pin_init!(&this in Self {
- // SAFETY:
- // - `page_vec` is a `KVec` of valid `struct page *` obtained from `pages`.
- // - The pages contained in `pages` remain valid for the entire lifetime of the
- // `RawSGTable`.
- sgt: unsafe { RawSGTable::new(&mut page_vec, size, max_segment, flags) }?,
- dma <- {
- // SAFETY: `this` is a valid pointer to uninitialized memory.
- let sgt = unsafe { &raw mut (*this.as_ptr()).sgt }.cast();
-
- // SAFETY: `sgt` is guaranteed to be non-null.
- let sgt = unsafe { NonNull::new_unchecked(sgt) };
-
- // SAFETY:
- // - It is guaranteed that the object returned by `DmaMappedSgt::new` won't out-live
- // `sgt`.
- // - `sgt` is never DMA unmapped manually.
- // - `sgt` is structurally pinned and therefore cannot move.
- Devres::new(dev, unsafe { DmaMappedSgt::new(sgt, dev, dir) })
- },
+ // SAFETY:
+ // - `page_vec` is a `KVec` of valid `struct page *` obtained from `pages`.
+ // - The pages contained in `pages` remain valid for the entire lifetime of the
+ // `RawSGTable`.
+ let sgt = unsafe { RawSGTable::new(&mut page_vec, size, max_segment, flags) }?;
+
+ Ok(Self {
+ dma: DmaMappedSgt::new(dev, sgt, dir)?,
_pages: pages,
- }))
+ })
}
}
-impl<P> SGTable<Owned<P>>
+impl<'a, P> SGTable<Owned<'a, P>>
where
- for<'a> P: page::AsPageIter<Iter<'a> = VmallocPageIter<'a>> + 'static,
+ for<'b> P: page::AsPageIter<Iter<'b> = VmallocPageIter<'b>> + 'static,
{
/// Allocates a new scatter-gather table from the given pages and maps it for DMA.
///
@@ -403,8 +355,7 @@ impl<P> SGTable<Owned<P>>
/// pages of `P`, and maps the table for DMA with the specified [`Device`] and
/// [`dma::DataDirection`].
///
- /// The DMA mapping is managed through [`Devres`], ensuring that the DMA mapping is unmapped
- /// once the associated [`Device`] is unbound, or when the [`SGTable<Owned>`] is dropped.
+ /// The table is tied to the [`Device<Bound>`] lifetime and unmaps the DMA mapping when dropped.
///
/// # Parameters
///
@@ -428,38 +379,38 @@ impl<P> SGTable<Owned<P>>
/// let size = 4 * page::PAGE_SIZE;
/// let pages = VVec::<u8>::with_capacity(size, GFP_KERNEL)?;
///
- /// let sgt = KBox::pin_init(SGTable::new(
+ /// let sgt = SGTable::new(
/// dev,
/// pages,
/// dma::DataDirection::ToDevice,
/// GFP_KERNEL,
- /// ), GFP_KERNEL)?;
+ /// )?;
///
/// Ok(())
/// }
/// ```
pub fn new(
- dev: &Device<Bound>,
+ dev: &'a Device<Bound>,
pages: P,
dir: dma::DataDirection,
flags: alloc::Flags,
- ) -> impl PinInit<Self, Error> + '_ {
- try_pin_init!(Self {
- inner <- Owned::new(dev, pages, dir, flags)?
+ ) -> Result<Self> {
+ Ok(Self {
+ inner: Owned::new(dev, pages, dir, flags)?,
})
}
}
-impl<P> Deref for SGTable<Owned<P>> {
+impl<P> Deref for SGTable<Owned<'_, P>> {
type Target = SGTable;
#[inline]
fn deref(&self) -> &Self::Target {
// SAFETY:
- // - `self.inner.sgt.as_raw()` is a valid pointer to a `struct sg_table` for the entire
+ // - `self.inner.dma.sgt.as_raw()` is a valid pointer to a `struct sg_table` for the entire
// lifetime of `self`.
// - The backing `struct sg_table` is not modified for the entire lifetime of `self`.
- unsafe { SGTable::from_raw(self.inner.sgt.as_raw()) }
+ unsafe { SGTable::from_raw(self.inner.dma.sgt.as_raw()) }
}
}
@@ -467,7 +418,7 @@ mod private {
pub trait Sealed {}
impl Sealed for super::Borrowed {}
- impl<P> Sealed for super::Owned<P> {}
+ impl<P> Sealed for super::Owned<'_, P> {}
}
/// An [`Iterator`] over the DMA mapped [`SGEntry`] items of an [`SGTable`].
diff --git a/samples/rust/rust_dma.rs b/samples/rust/rust_dma.rs
index ffb693544673..844e137cddc8 100644
--- a/samples/rust/rust_dma.rs
+++ b/samples/rust/rust_dma.rs
@@ -35,8 +35,7 @@
struct DmaSampleData<'bound> {
pdev: &'bound pci::Device<Bound>,
ca: Coherent<'bound, [MyStruct]>,
- #[pin]
- sgt: SGTable<Owned<VVec<u8>>>,
+ sgt: SGTable<Owned<'bound, VVec<u8>>>,
}
const TEST_VALUES: [(u32, u32); 5] = [
@@ -96,13 +95,9 @@ fn probe<'bound>(
let size = 4 * page::PAGE_SIZE;
let pages = VVec::with_capacity(size, GFP_KERNEL)?;
- let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL);
+ let sgt = SGTable::new(pdev.as_ref(), pages, DataDirection::ToDevice, GFP_KERNEL)?;
- Ok(try_pin_init!(Self::Data {
- pdev,
- ca,
- sgt <- sgt,
- }))
+ Ok(try_pin_init!(Self::Data { pdev, ca, sgt }))
})
}
}
base-commit: 2268cf8e3d7b4192629e4beff4a413efe5501f11
--
2.56.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
* [PATCH 2/2] gpu: nova-core: remove unnecessary firmware pin-init
2026-11-14 23:42 [PATCH 1/2] rust: scatterlist: tie owned tables to the device's bound lifetime Danilo Krummrich
@ 2026-11-14 23:42 ` Danilo Krummrich
0 siblings, 0 replies; 2+ messages in thread
From: Danilo Krummrich @ 2026-11-14 23:42 UTC (permalink / raw)
To: dakr, acourbot, aliceryhl, abdiel.janulgue, daniel.almeida,
robin.murphy, a.hindborg, ojeda, boqun, gary, bjorn3_gh, lossin,
tmgross, tamird, work
Cc: driver-core, nova-gpu, dri-devel, linux-kernel, rust-for-linux
Now that SGTable no longer requires pinning, remove the pin-init
machinery from Radix3 and GspFirmware.
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
---
drivers/gpu/nova-core/firmware/gsp.rs | 43 +++++--------
drivers/gpu/nova-core/firmware/radix3.rs | 82 +++++++++++-------------
drivers/gpu/nova-core/gsp/boot.rs | 2 +-
3 files changed, 56 insertions(+), 71 deletions(-)
diff --git a/drivers/gpu/nova-core/firmware/gsp.rs b/drivers/gpu/nova-core/firmware/gsp.rs
index 341a8b19aa38..ff92a12b2994 100644
--- a/drivers/gpu/nova-core/firmware/gsp.rs
+++ b/drivers/gpu/nova-core/firmware/gsp.rs
@@ -25,10 +25,8 @@
};
/// The GSP firmware image, its signatures, and the GSP bootloader.
-#[pin_data]
pub(crate) struct GspFirmware<'a> {
/// The firmware image and the radix3 table that maps it.
- #[pin]
radix3: Radix3<'a>,
/// Device-mapped GSP signatures matching the GPU's [`Chipset`].
pub(crate) signatures: Coherent<'a, [u8]>,
@@ -39,34 +37,27 @@ pub(crate) struct GspFirmware<'a> {
impl<'a> GspFirmware<'a> {
/// Loads the GSP firmware binaries, map them into `dev`'s address-space, and creates the page
/// tables expected by the GSP bootloader to load it.
- pub(crate) fn new(
- dev: &'a device::Device<device::Bound>,
- chipset: Chipset,
- ) -> impl PinInit<Self, Error> + 'a {
- pin_init::pin_init_scope(move || {
- let firmware = request_tlv(dev, chipset, "gsp")?;
- let tlv = Tlv::new(firmware.data())?;
- dev_dbg!(dev, "loaded gsp firmware v{}\n", tlv.get_string(b"VERS")?);
+ pub(crate) fn new(dev: &'a device::Device<device::Bound>, chipset: Chipset) -> Result<Self> {
+ let firmware = request_tlv(dev, chipset, "gsp")?;
+ let tlv = Tlv::new(firmware.data())?;
+ dev_dbg!(dev, "loaded gsp firmware v{}\n", tlv.get_string(b"VERS")?);
- let size = usize::from_safe_cast(tlv.get_u32(b"SIZE")?);
- let mut fw_vvec = VVec::zeroed(size, GFP_KERNEL).map_err(|_| ENOMEM)?;
+ let size = usize::from_safe_cast(tlv.get_u32(b"SIZE")?);
+ let mut fw_vvec = VVec::zeroed(size, GFP_KERNEL).map_err(|_| ENOMEM)?;
- let chip_name = chipset.name();
- let file = tlv.get_string(b"FILE")?;
- let filename = CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{file}"))?;
- firmware::request_into_buf(&filename, dev, fw_vvec.as_mut_slice())?;
+ let chip_name = chipset.name();
+ let file = tlv.get_string(b"FILE")?;
+ let filename = CString::try_from_fmt(fmt!("nvidia/{chip_name}/gsp/{file}"))?;
+ firmware::request_into_buf(&filename, dev, fw_vvec.as_mut_slice())?;
- let signatures = Coherent::from_slice(dev, tlv.get_bytes(b"SIGN")?, GFP_KERNEL)?;
+ Ok(Self {
+ radix3: Radix3::new(dev, fw_vvec)?,
+ signatures: Coherent::from_slice(dev, tlv.get_bytes(b"SIGN")?, GFP_KERNEL)?,
+ bootloader: {
+ let bl = request_tlv(dev, chipset, "gsp_bootloader")?;
- Ok(try_pin_init!(Self {
- radix3 <- Radix3::new(dev, fw_vvec),
- signatures,
- bootloader: {
- let bl = request_tlv(dev, chipset, "gsp_bootloader")?;
-
- RiscvFirmware::new(dev, &bl)?
- },
- }))
+ RiscvFirmware::new(dev, &bl)?
+ },
})
}
diff --git a/drivers/gpu/nova-core/firmware/radix3.rs b/drivers/gpu/nova-core/firmware/radix3.rs
index a3e43fc8ec86..2c8787d5930d 100644
--- a/drivers/gpu/nova-core/firmware/radix3.rs
+++ b/drivers/gpu/nova-core/firmware/radix3.rs
@@ -32,60 +32,54 @@
};
/// A radix3 page table and the data it maps.
-#[pin_data]
pub(crate) struct Radix3<'a> {
/// The mapped data.
- data: SGTable<Owned<'a, VVec<u8>>>,
- /// Level 2: one entry per page of `data`.
- level2: SGTable<Owned<'a, VVec<u8>>>,
- /// Level 1: one entry per page of `level2`.
- level1: SGTable<Owned<'a, VVec<u8>>>,
- /// Level 0: one page, whose single entry is the DMA address of the first `level1` page.
+ _data: SGTable<Owned<'a, VVec<u8>>>,
+ /// Level 2: one entry per page of `_data`.
+ _level2: SGTable<Owned<'a, VVec<u8>>>,
+ /// Level 1: one entry per page of `_level2`.
+ _level1: SGTable<Owned<'a, VVec<u8>>>,
+ /// Level 0: one page, whose single entry is the DMA address of the first `_level1` page.
level0: Coherent<'a, [u64]>,
- /// Length of `data`, in bytes.
+ /// Length of `_data`, in bytes.
size: usize,
}
impl<'a> Radix3<'a> {
/// Builds a radix3 page table over `data`, mapped for `dev` to read. May sleep.
- pub(crate) fn new(
- dev: &'a device::Device<device::Bound>,
- data: VVec<u8>,
- ) -> impl PinInit<Self, Error> + 'a {
+ pub(crate) fn new(dev: &'a device::Device<device::Bound>, data: VVec<u8>) -> Result<Self> {
let size = data.len();
- pin_init::pin_init_scope(move || {
- Ok(try_pin_init!(Self {
- data: SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL)?,
- level2: {
- let l2 = build_lvl(data)?;
- SGTable::new(dev, l2, DataDirection::ToDevice, GFP_KERNEL)?
- },
- level1: {
- let l1 = build_lvl(level2)?;
- SGTable::new(dev, l1, DataDirection::ToDevice, GFP_KERNEL)?
- },
- level0: {
- // Allocate the level 0 page table as a device-visible DMA object, and map the
- // level 1 page table onto it.
-
- // Fill level 1 page entry.
- let level1_entry = level1.iter().next().ok_or(EINVAL)?;
- let level1_entry_addr = level1_entry.dma_address();
-
- // Create level 0 page table data and fill its first entry with the level 1
- // table.
- let mut level0 = CoherentBox::<'_, [u64]>::zeroed_slice(
- dev,
- GSP_PAGE_SIZE / size_of::<u64>(),
- GFP_KERNEL,
- )?;
- level0[0] = level1_entry_addr.to_le();
-
- level0.into()
- },
- size,
- }))
+ let data = SGTable::new(dev, data, DataDirection::ToDevice, GFP_KERNEL)?;
+
+ let l2 = build_lvl(&data)?;
+ let level2 = SGTable::new(dev, l2, DataDirection::ToDevice, GFP_KERNEL)?;
+
+ let l1 = build_lvl(&level2)?;
+ let level1 = SGTable::new(dev, l1, DataDirection::ToDevice, GFP_KERNEL)?;
+
+ // Allocate the level 0 page table as a device-visible DMA object, and map the
+ // level 1 page table onto it.
+
+ // Fill level 1 page entry.
+ let level1_entry = level1.iter().next().ok_or(EINVAL)?;
+ let level1_entry_addr = level1_entry.dma_address();
+
+ // Create level 0 page table data and fill its first entry with the level 1
+ // table.
+ let mut level0 = CoherentBox::<'_, [u64]>::zeroed_slice(
+ dev,
+ GSP_PAGE_SIZE / size_of::<u64>(),
+ GFP_KERNEL,
+ )?;
+ level0[0] = level1_entry_addr.to_le();
+
+ Ok(Self {
+ _data: data,
+ _level2: level2,
+ _level1: level1,
+ level0: level0.into(),
+ size,
})
}
diff --git a/drivers/gpu/nova-core/gsp/boot.rs b/drivers/gpu/nova-core/gsp/boot.rs
index 4fb1b69ac9d5..ac09b40832aa 100644
--- a/drivers/gpu/nova-core/gsp/boot.rs
+++ b/drivers/gpu/nova-core/gsp/boot.rs
@@ -40,7 +40,7 @@ pub(crate) fn boot(
let dev = pdev.as_ref();
let hal = super::hal::gsp_hal(chipset);
- let gsp_fw = KBox::pin_init(GspFirmware::new(dev, chipset), GFP_KERNEL)?;
+ let gsp_fw = GspFirmware::new(dev, chipset)?;
self.cmdq
.send_command_no_wait(commands::SetSystemInfo::new(pdev, chipset))?;
--
2.56.1
^ permalink raw reply related [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-11-14 23:43 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-14 23:42 [PATCH 1/2] rust: scatterlist: tie owned tables to the device's bound lifetime Danilo Krummrich
2026-11-14 23:42 ` [PATCH 2/2] gpu: nova-core: remove unnecessary firmware pin-init Danilo Krummrich
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.