drivers/gpu/nova-core/gsp/cmdq.rs

Source file repositories/reference/linux-study-clean/drivers/gpu/nova-core/gsp/cmdq.rs

File Facts

System
Linux kernel
Corpus path
drivers/gpu/nova-core/gsp/cmdq.rs
Extension
.rs
Size
32315 bytes
Lines
850
Domain
Driver Families
Bucket
drivers/gpu
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct MsgqData {
    data: [[u8; GSP_PAGE_SIZE]; num::u32_as_usize(MSGQ_NUM_PAGES)],
}

// Annoyingly we are forced to use a literal to specify the alignment of
// `MsgqData`, so check that it corresponds to the actual GSP page size here.
static_assert!(align_of::<MsgqData>() == GSP_PAGE_SIZE);

/// Unidirectional message queue.
///
/// Contains the data for a message queue, that either the driver or GSP writes to.
///
/// Note that while the write pointer of `tx` corresponds to the `msgq` of the same instance, the
/// read pointer of `rx` actually refers to the `Msgq` owned by the other side.
/// This design ensures that only the driver or GSP ever writes to a given instance of this struct.
#[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 {
    /// Header for sending messages, including the write pointer.
    pub(super) tx: MsgqTxHeader,
    /// Header for receiving messages, including the read pointer.
    pub(super) 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 {
    /// 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
    /// write and read pointers that the CPU updates. This means that the read pointer here is an
    /// index into the GSP queue.
    ///
    /// This member is read-only for the GSP.
    pub(super) 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,
}

impl GspMem {
    const PTE_ARRAY_SIZE: usize = GSP_PAGE_SIZE / size_of::<u64>();
}

// SAFETY: These structs don't meet the no-padding requirements of AsBytes but
// that is not a problem because they are not used outside the kernel.
unsafe impl AsBytes for GspMem {}

// SAFETY: These structs don't meet the no-padding requirements of FromBytes but
// that is not a problem because they are not used outside the kernel.
unsafe impl FromBytes for GspMem {}

/// Wrapper around [`GspMem`] to share it with the GPU using a [`Coherent`].
///
/// This provides the low-level functionality to communicate with the GSP, including allocation of
/// queue space to write messages to and management of read/write pointers.
///
/// This is shared with the GSP, with clear ownership rules regarding the command queues:
///
/// * The driver owns (i.e. can write to) the part of the CPU message queue between the CPU write
///   pointer and the GSP read pointer. This region is returned by [`Self::driver_write_area`].
/// * The driver owns (i.e. can read from) the part of the GSP message queue between the CPU read
///   pointer and the GSP write pointer. This region is returned by [`Self::driver_read_area`].
struct DmaGspMem(Coherent<GspMem>);

impl DmaGspMem {
    /// Allocate a new instance and map it for `dev`.
    fn new(dev: &device::Device<device::Bound>) -> Result<Self> {
        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)?);
        }

        dma_write!(
            gsp_mem,
            .cpuq.tx,

Annotation

Implementation Notes