Re: [PATCH v2 05/15] gpu: nova-core: add the per-architecture GIN CPU interrupt HAL
From: Alexandre Courbot
Date: Mon Aug 31 2026 - 21:30:26 EST
On Sat Aug 29, 2026 at 10:22 AM JST, John Hubbard wrote:
> GIN, the GPU Interrupt and Notification unit, is the GPU's interrupt
> controller. Each PCIe function has its own tree, whose leaf count
> depends on the GPU family.
>
> Message-signaled delivery stops after each edge until the CPU rearms it,
> and the rearm write differs by family and interrupt type:
>
> * Pre-Hopper MSI writes an EOI through the BAR0 PCI configuration
> space mirror.
>
> * MSI for Hopper and later cycles the TOP enable bits of every
> serviced subtree.
>
> * MSI-X on any family cycles the bits of the handler's own subtree.
The indentation of this bullet list is a bit unconventional for a kernel
git log (`*` typically starts at column 1).
<...>
> +impl PciIrqRearmMethod {
> + /// Performs this method's register write.
> + ///
> + /// `serviced` holds every subtree the driver services, and `subtree` is the one subtree the
> + /// calling handler serves. Each method uses whichever of the two its interrupt type delivers
> + /// on, so both are required.
> + pub(super) fn rearm(self, bar: Bar0<'_>, serviced: SubtreeSet, subtree: Subtree) {
> + let subtrees = match self {
> + // The written value is ignored, so any write rearms delivery.
> + Self::ConfigMirrorEoi => {
> + bar.write(regs::NV_XVE_CYA_2, 0u32.into());
> + return;
> + }
> + Self::TopEnableCycleServiced => serviced,
> + Self::TopEnableCycleSubtree => SubtreeSet::from(subtree),
> + };
> +
> + bar.write(
> + regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR,
> + subtrees.into_raw().into(),
> + );
> + bar.write(
> + regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_SET,
> + subtrees.into_raw().into(),
> + );
With the typed register fields in patch 3, these can become:
bar.write_reg(
regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR::zeroed().with_subtrees(subtrees),
);
bar.write_reg(
regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_SET::zeroed().with_subtrees(subtrees),
);
> + }
> +}
> +
> +/// Per-architecture properties of the GIN CPU interrupt tree.
> +///
> +/// The tree size and the method that rearms PCI interrupt delivery differ by family. The tree
> +/// walk, the vector encoding, and the read-and-clear sequence do not, and are in generic code.
The last sentence sounds a bit self-evident and unneeded.
> +///
> +/// See `Documentation/gpu/nova/core/interrupts.rst`.
> +pub(super) trait CpuInterruptHal {
> + /// Returns the number of leaves the CPU tree implements.
> + ///
> + /// [`LeafCount::subtree_set`] gives the subtrees behind them, and
> + /// [`LeafCount::vector_count`] the vectors they carry.
> + fn leaf_count(&self) -> LeafCount;
> +
> + /// Returns the method that rearms PCI interrupt delivery for `irq_type`.
> + ///
> + /// `None` means that `irq_type` needs no rearm write. That is the case for `INTx`, which is
> + /// level-triggered, and which nova-core does not allocate.
> + fn pci_irq_rearm_method(&self, irq_type: IrqType) -> Option<PciIrqRearmMethod>;
> +}
> +
> +/// Returns the [`CpuInterruptHal`] for `chipset`.
> +pub(super) fn cpu_interrupt_hal(chipset: Chipset) -> &'static dyn CpuInterruptHal {
> + match chipset.arch() {
> + Architecture::Turing | Architecture::Ampere | Architecture::Ada => tu102::TU102_HAL,
> + Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
> + gh100::GH100_HAL
> + }
> + }
> +}
> diff --git a/drivers/gpu/nova-core/irq/hal/gh100.rs b/drivers/gpu/nova-core/irq/hal/gh100.rs
> new file mode 100644
> index 000000000000..32b9b4a01adb
> --- /dev/null
> +++ b/drivers/gpu/nova-core/irq/hal/gh100.rs
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: GPL-2.0
> +// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
> +
> +use kernel::pci::IrqType;
> +
> +use super::{
> + CpuInterruptHal,
> + LeafCount,
> + PciIrqRearmMethod, //
> +};
> +
> +/// GIN parameters for Hopper and Blackwell, which implement a 16-leaf CPU tree. Only 12 leaves
> +/// carry sources.
> +struct Gh100;
> +
> +impl CpuInterruptHal for Gh100 {
> + fn leaf_count(&self) -> LeafCount {
> + LeafCount::Sixteen
> + }
> +
> + fn pci_irq_rearm_method(&self, irq_type: IrqType) -> Option<PciIrqRearmMethod> {
> + match irq_type {
> + IrqType::Intx => None,
> + IrqType::Msi => Some(PciIrqRearmMethod::TopEnableCycleServiced),
> + IrqType::MsiX => Some(PciIrqRearmMethod::TopEnableCycleSubtree),
> + }
> + }
AFAIU we do not support INTx at all, right? In this case it just
shouldn't be handled here, and we could simplify this method to just
return `PciIrqRearmMethod`.
I suspect we are considering INTx because we work with the kernel's
`IrqType`, which includes it, and must make our match arms exhaustive.
Let's just use our own IRQ type for Nova:
pub(crate) enum MsiType {
Msi,
MsiX,
}
Then every instance of `IrqType` in the `irq` module can be replaced by
this one, and we don't need to care about INTx anymore. This method can
also just return a `PciIrqRearmMethod`.
You will probably need to include a `MsiType` into `SubtreeVectors` to
make its `irq_type` method work, but that's still better than having
code for managing an INTx variant that is dead code since it would have
failed at probe time anyway.