summaryrefslogtreecommitdiff
path: root/drivers/gpu
diff options
context:
space:
mode:
authorDanilo Krummrich <dakr@kernel.org>2026-07-13 23:50:39 +0200
committerDanilo Krummrich <dakr@kernel.org>2026-07-13 23:50:39 +0200
commitb07fc8d60bd30caaba4d293929459780166da194 (patch)
tree4f47c377f5b9c8078f02ef883322719f4109b167 /drivers/gpu
parenta56a92be1fa9c6e747fc754c90734ed90cd36670 (diff)
parent11a4784f902ebf3e674dbaf07dbec9a37aabb5e4 (diff)
downloadlinux-b07fc8d60bd30caaba4d293929459780166da194.tar.gz
linux-b07fc8d60bd30caaba4d293929459780166da194.zip
Merge patch series "rust: I/O type generalization and projection"
Gary Guo <gary@garyguo.net> says: This series presents a major rework of I/O types, as a summary: - Make I/O regions typed. The existing untyped region still exists with a dynamically sized `Region` type. - Create I/O view types to represent subregion of a full I/O region mapped. A projection macro is added to allow safely create such subviews. - Split I/O traits, make I/O views play a central role, avoid duplicate monomorphization and less `unsafe` code. - Add a `SysMem` backend, and make `Coherent` implement `Io`. - Add copying methods (memcpy_{from,to}io and friends). This series generalize `Mmio` type from just an untyped region to typed representations (so `MmioRaw<T>` is `__iomem *T`). This allows us to remove the `IoKnownSize` trait; the information is sourced from just the pointer from the `KnownSize` trait instead. Building on top of that, `Mmio` and `ConfigSpace` have been converted to typed views of I/O regions rather than just a big chunk of untyped I/O memory. These changes made it possible to implement `Io` trait for `Coherent<T>`. Shared system memory, `SysMem` is also added to the series, given it similarity in implementation compared to `Coherent`. In fact, the series use `SysMem` to implement `Coherent`'s I/O methods. Built on these generalization, this series add `io_project!()`. `io_project!()` performs a safe way to project a bigger view to a small subviews, and some Nova code has been converted in this series to demonstrate cleanups possible with this addition. New `io_read!()`, `io_write!()` has been added that supersedes `dma_read!()`, `dma_write!()` macro. Although, they work for primitives only (to be exact, types that the backend is `IoCapable` of). One feature that was lost from the old `dma_read!()` and `dma_write!()` series was the ability to read/write a large structs. However, the semantics was unclear to begin with, as there was no guarantee about their atomicity even for structs that were small enough to fit in u32. Suggested-by: Danilo Krummrich <dakr@kernel.org> Link: https://rust-for-linux.zulipchat.com/#narrow/channel/288089-General/topic/Generic.20I.2FO.20backends/near/571198078 Link: https://patch.msgid.link/20260706-io_projection-v6-0-72cd5d055d54@garyguo.net Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'drivers/gpu')
-rw-r--r--drivers/gpu/nova-core/gsp.rs53
-rw-r--r--drivers/gpu/nova-core/gsp/cmdq.rs66
-rw-r--r--drivers/gpu/nova-core/gsp/fw.rs82
3 files changed, 90 insertions, 111 deletions
diff --git a/drivers/gpu/nova-core/gsp.rs b/drivers/gpu/nova-core/gsp.rs
index 69175ca3315c..cfa7553cd820 100644
--- a/drivers/gpu/nova-core/gsp.rs
+++ b/drivers/gpu/nova-core/gsp.rs
@@ -9,14 +9,16 @@ use kernel::{
dma::{
Coherent,
CoherentBox,
+ CoherentView,
DmaAddress, //
},
+ io::{
+ io_project,
+ io_write,
+ Io, //
+ },
pci,
- prelude::*,
- transmute::{
- AsBytes,
- FromBytes, //
- }, //
+ prelude::*, //
};
pub(crate) mod cmdq;
@@ -48,21 +50,21 @@ const LOG_BUFFER_SIZE: usize = RM_LOG_BUFFER_NUM_PAGES * GSP_PAGE_SIZE;
/// Array of page table entries, as understood by the GSP bootloader.
#[repr(C)]
+#[derive(FromBytes, IntoBytes)]
struct PteArray<const NUM_ENTRIES: usize>([u64; NUM_ENTRIES]);
-/// SAFETY: arrays of `u64` implement `FromBytes` and we are but a wrapper around one.
-unsafe impl<const NUM_ENTRIES: usize> FromBytes for PteArray<NUM_ENTRIES> {}
-
-/// SAFETY: arrays of `u64` implement `AsBytes` and we are but a wrapper around one.
-unsafe impl<const NUM_ENTRIES: usize> AsBytes for PteArray<NUM_ENTRIES> {}
-
impl<const NUM_PAGES: usize> PteArray<NUM_PAGES> {
- /// Returns the page table entry for `index`, for a mapping starting at `start`.
- // TODO: Replace with `IoView` projection once available.
- fn entry(start: DmaAddress, index: usize) -> Result<u64> {
- start
- .checked_add(num::usize_as_u64(index) << GSP_PAGE_SHIFT)
- .ok_or(EOVERFLOW)
+ /// Initialize a new page table array mapping `NUM_PAGES` GSP pages starting at address `start`.
+ fn init(view: CoherentView<'_, Self>, start: DmaAddress) -> Result<()> {
+ for i in 0..NUM_PAGES {
+ io_write!(view, .0[build: i],
+ start
+ .checked_add(num::usize_as_u64(i) << GSP_PAGE_SHIFT)
+ .ok_or(EOVERFLOW)?
+ );
+ }
+
+ Ok(())
}
}
@@ -89,17 +91,12 @@ impl LogBuffer {
let start_addr = obj.0.dma_handle();
- // SAFETY: `obj` has just been created and we are its sole user.
- let pte_region = unsafe {
- &mut obj.0.as_mut()[size_of::<u64>()..][..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
- };
-
- // Write values one by one to avoid an on-stack instance of `PteArray`.
- for (i, chunk) in pte_region.chunks_exact_mut(size_of::<u64>()).enumerate() {
- let pte_value = PteArray::<0>::entry(start_addr, i)?;
-
- chunk.copy_from_slice(&pte_value.to_ne_bytes());
- }
+ let pte_view = io_project!(
+ obj.0,
+ [build: size_of::<u64>()..][build: ..RM_LOG_BUFFER_NUM_PAGES * size_of::<u64>()]
+ )
+ .try_cast::<PteArray<RM_LOG_BUFFER_NUM_PAGES>>()?;
+ PteArray::init(pte_view, start_addr)?;
Ok(obj)
}
diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
index 070de0731e95..c34b48961496 100644
--- a/drivers/gpu/nova-core/gsp/cmdq.rs
+++ b/drivers/gpu/nova-core/gsp/cmdq.rs
@@ -2,16 +2,23 @@
mod continuation;
-use core::mem;
+use core::{
+ mem,
+ sync::atomic::{
+ fence,
+ Ordering, //
+ },
+};
use kernel::{
device,
dma::{
Coherent,
+ CoherentBox,
DmaAddress, //
},
- dma_write,
io::{
+ io_project,
poll::read_poll_timeout,
Io, //
},
@@ -171,20 +178,18 @@ static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);
#[repr(C)]
// There is no struct defined for this in the open-gpu-kernel-source headers.
// Instead it is defined by code in `GspMsgQueuesInit()`.
-// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
-pub(super) struct Msgq {
+struct Msgq {
/// Header for sending messages, including the write pointer.
- pub(super) tx: MsgqTxHeader,
+ tx: MsgqTxHeader,
/// Header for receiving messages, including the read pointer.
- pub(super) rx: MsgqRxHeader,
+ rx: MsgqRxHeader,
/// The message queue proper.
msgq: MsgqData,
}
/// Structure shared between the driver and the GSP and containing the command and message queues.
#[repr(C)]
-// TODO: Revert to private once `IoView` projections replace the `gsp_mem` module.
-pub(super) struct GspMem {
+struct GspMem {
/// Self-mapping page table entries.
ptes: PteArray<{ Self::PTE_ARRAY_SIZE }>,
/// CPU queue: the driver writes commands here, and the GSP reads them. It also contains the
@@ -192,13 +197,13 @@ pub(super) struct GspMem {
/// index into the GSP queue.
///
/// This member is read-only for the GSP.
- pub(super) cpuq: Msgq,
+ cpuq: Msgq,
/// GSP queue: the GSP writes messages here, and the driver reads them. It also contains the
/// write and read pointers that the GSP updates. This means that the read pointer here is an
/// index into the CPU queue.
///
/// This member is read-only for the driver.
- pub(super) gspq: Msgq,
+ gspq: Msgq,
}
impl GspMem {
@@ -232,20 +237,12 @@ impl DmaGspMem {
const MSGQ_SIZE: u32 = num::usize_into_u32::<{ size_of::<Msgq>() }>();
const RX_HDR_OFF: u32 = num::usize_into_u32::<{ mem::offset_of!(Msgq, rx) }>();
- let gsp_mem = Coherent::<GspMem>::zeroed(dev, GFP_KERNEL)?;
-
- let start = gsp_mem.dma_handle();
- // Write values one by one to avoid an on-stack instance of `PteArray`.
- for i in 0..GspMem::PTE_ARRAY_SIZE {
- dma_write!(gsp_mem, .ptes.0[build: i], PteArray::<0>::entry(start, i)?);
- }
+ let mut gsp_mem = CoherentBox::<GspMem>::zeroed(dev, GFP_KERNEL)?;
+ gsp_mem.cpuq.tx = MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES);
+ gsp_mem.cpuq.rx = MsgqRxHeader::new();
- dma_write!(
- gsp_mem,
- .cpuq.tx,
- MsgqTxHeader::new(MSGQ_SIZE, RX_HDR_OFF, MSGQ_NUM_PAGES)
- );
- dma_write!(gsp_mem, .cpuq.rx, MsgqRxHeader::new());
+ let gsp_mem: Coherent<_> = gsp_mem.into();
+ PteArray::init(io_project!(gsp_mem, .ptes), gsp_mem.dma_handle())?;
Ok(Self(gsp_mem))
}
@@ -406,7 +403,7 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_write_ptr(&self) -> u32 {
- super::fw::gsp_mem::gsp_write_ptr(&self.0)
+ MsgqTxHeader::write_ptr(io_project!(self.0, .gspq.tx)) % MSGQ_NUM_PAGES
}
// Returns the index of the memory page the GSP will read the next command from.
@@ -415,7 +412,7 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn gsp_read_ptr(&self) -> u32 {
- super::fw::gsp_mem::gsp_read_ptr(&self.0)
+ MsgqRxHeader::read_ptr(io_project!(self.0, .gspq.rx)) % MSGQ_NUM_PAGES
}
// Returns the index of the memory page the CPU can read the next message from.
@@ -424,12 +421,18 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_read_ptr(&self) -> u32 {
- super::fw::gsp_mem::cpu_read_ptr(&self.0)
+ MsgqRxHeader::read_ptr(io_project!(self.0, .cpuq.rx)) % MSGQ_NUM_PAGES
}
// Informs the GSP that it can send `elem_count` new pages into the message queue.
fn advance_cpu_read_ptr(&mut self, elem_count: u32) {
- super::fw::gsp_mem::advance_cpu_read_ptr(&self.0, elem_count)
+ let rx = io_project!(self.0, .cpuq.rx);
+ let rptr = MsgqRxHeader::read_ptr(rx).wrapping_add(elem_count) % MSGQ_NUM_PAGES;
+
+ // Ensure read pointer is properly ordered.
+ fence(Ordering::SeqCst);
+
+ MsgqRxHeader::set_read_ptr(rx, rptr)
}
// Returns the index of the memory page the CPU can write the next command to.
@@ -438,12 +441,17 @@ impl DmaGspMem {
//
// - The returned value is within `0..MSGQ_NUM_PAGES`.
fn cpu_write_ptr(&self) -> u32 {
- super::fw::gsp_mem::cpu_write_ptr(&self.0)
+ MsgqTxHeader::write_ptr(io_project!(self.0, .cpuq.tx)) % MSGQ_NUM_PAGES
}
// Informs the GSP that it can process `elem_count` new pages from the command queue.
fn advance_cpu_write_ptr(&mut self, elem_count: u32) {
- super::fw::gsp_mem::advance_cpu_write_ptr(&self.0, elem_count)
+ let tx = io_project!(self.0, .cpuq.tx);
+ let wptr = MsgqTxHeader::write_ptr(tx).wrapping_add(elem_count) % MSGQ_NUM_PAGES;
+ MsgqTxHeader::set_write_ptr(tx, wptr);
+
+ // Ensure all command data is visible before triggering the GSP read.
+ fence(Ordering::SeqCst);
}
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 4db0cfa4dc4d..b0e7de328eaf 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -10,7 +10,14 @@ use r570_144 as bindings;
use core::ops::Range;
use kernel::{
- dma::Coherent,
+ dma::{
+ Coherent,
+ CoherentView, //
+ },
+ io::{
+ io_read,
+ io_write, //
+ },
prelude::*,
ptr::{
Alignable,
@@ -44,59 +51,6 @@ use crate::{
},
};
-// TODO: Replace with `IoView` projections once available.
-pub(super) mod gsp_mem {
- use core::sync::atomic::{
- fence,
- Ordering, //
- };
-
- use kernel::{
- dma::Coherent,
- dma_read,
- dma_write, //
- };
-
- use crate::gsp::cmdq::{
- GspMem,
- MSGQ_NUM_PAGES, //
- };
-
- pub(in crate::gsp) fn gsp_write_ptr(qs: &Coherent<GspMem>) -> u32 {
- dma_read!(qs, .gspq.tx.0.writePtr) % MSGQ_NUM_PAGES
- }
-
- pub(in crate::gsp) fn gsp_read_ptr(qs: &Coherent<GspMem>) -> u32 {
- dma_read!(qs, .gspq.rx.0.readPtr) % MSGQ_NUM_PAGES
- }
-
- pub(in crate::gsp) fn cpu_read_ptr(qs: &Coherent<GspMem>) -> u32 {
- dma_read!(qs, .cpuq.rx.0.readPtr) % MSGQ_NUM_PAGES
- }
-
- pub(in crate::gsp) fn advance_cpu_read_ptr(qs: &Coherent<GspMem>, count: u32) {
- let rptr = cpu_read_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
-
- // Ensure read pointer is properly ordered.
- fence(Ordering::SeqCst);
-
- dma_write!(qs, .cpuq.rx.0.readPtr, rptr);
- }
-
- pub(in crate::gsp) fn cpu_write_ptr(qs: &Coherent<GspMem>) -> u32 {
- dma_read!(qs, .cpuq.tx.0.writePtr) % MSGQ_NUM_PAGES
- }
-
- pub(in crate::gsp) fn advance_cpu_write_ptr(qs: &Coherent<GspMem>, count: u32) {
- let wptr = cpu_write_ptr(qs).wrapping_add(count) % MSGQ_NUM_PAGES;
-
- dma_write!(qs, .cpuq.tx.0.writePtr, wptr);
-
- // Ensure all command data is visible before triggering the GSP read.
- fence(Ordering::SeqCst);
- }
-}
-
/// Maximum size of a single GSP message queue element in bytes.
pub(crate) const GSP_MSG_QUEUE_ELEMENT_SIZE_MAX: usize =
num::u32_as_usize(bindings::GSP_MSG_QUEUE_ELEMENT_SIZE_MAX);
@@ -720,6 +674,16 @@ impl MsgqTxHeader {
entryOff: num::usize_into_u32::<GSP_PAGE_SIZE>(),
})
}
+
+ /// Returns the value of the write pointer for this queue.
+ pub(crate) fn write_ptr(this: CoherentView<'_, Self>) -> u32 {
+ io_read!(this, .0.writePtr)
+ }
+
+ /// Sets the value of the write pointer for this queue.
+ pub(crate) fn set_write_ptr(this: CoherentView<'_, Self>, val: u32) {
+ io_write!(this, .0.writePtr, val)
+ }
}
// SAFETY: Padding is explicit and does not contain uninitialized data.
@@ -735,6 +699,16 @@ impl MsgqRxHeader {
pub(crate) fn new() -> Self {
Self(Default::default())
}
+
+ /// Returns the value of the read pointer for this queue.
+ pub(crate) fn read_ptr(this: CoherentView<'_, Self>) -> u32 {
+ io_read!(this, .0.readPtr)
+ }
+
+ /// Sets the value of the read pointer for this queue.
+ pub(crate) fn set_read_ptr(this: CoherentView<'_, Self>, val: u32) {
+ io_write!(this, .0.readPtr, val)
+ }
}
// SAFETY: Padding is explicit and does not contain uninitialized data.