The Linux Kernel Mailing List
 help / color / mirror / Atom feed
From: John Hubbard <jhubbard@nvidia.com>
To: Danilo Krummrich <dakr@kernel.org>,
	Joel Fernandes <joel@joelfernandes.org>,
	Alexandre Courbot <acourbot@nvidia.com>
Cc: "Timur Tabi" <ttabi@nvidia.com>,
	"Alistair Popple" <apopple@nvidia.com>,
	"Eliot Courtney" <ecourtney@nvidia.com>,
	"Shashank Sharma" <shashanks@nvidia.com>,
	"Zhi Wang" <zhiw@nvidia.com>, "David Airlie" <airlied@gmail.com>,
	"Simona Vetter" <simona@ffwll.ch>,
	"Bjorn Helgaas" <bhelgaas@google.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	nova-gpu@lists.linux.dev, LKML <linux-kernel@vger.kernel.org>,
	"John Hubbard" <jhubbard@nvidia.com>,
	"Will Pierce" <wpierce@nvidia.com>
Subject: [PATCH 08/17] gpu: nova-core: allocate interrupt vectors for the serviced subtrees
Date: Fri,  7 Aug 2026 20:11:10 -0700	[thread overview]
Message-ID: <20260808031120.363869-9-jhubbard@nvidia.com> (raw)
In-Reply-To: <20260808031120.363869-1-jhubbard@nvidia.com>

Every subtree nova-core enables at TOP needs an allocated PCI vector
with a handler on it. How many vectors that takes depends on the type
the PCI core grants. MSI has one message that every subtree raises,
so one vector serves the whole tree. MSI-X gives each subtree its own
table entry, and Linux masks every entry a driver does not allocate. A
serviced subtree with no entry of its own loses the interrupts it
raises, while its GIN leaf and TOP bits read pending and enabled.

nova-core allocated one vector at probe, and the tree enabled every
implemented subtree.

Size the allocation to the serviced set: MSI-X entries up to the highest
serviced subtree, falling back to a single MSI. Drop the INTx fallback,
since nova-core does not share a level-triggered line. Enable only
the serviced subtrees at TOP, and take the leaf count and the rearm
method from the interrupt HAL when the tree is built.

Assisted-by: Cursor:claude-opus-5
Reviewed-by: Will Pierce <wpierce@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
 drivers/gpu/nova-core/gpu.rs                |  6 --
 drivers/gpu/nova-core/irq.rs                | 78 ++++++++++++++++++---
 drivers/gpu/nova-core/irq/hal.rs            |  2 -
 drivers/gpu/nova-core/irq/interrupt_tree.rs | 68 +++++++++++-------
 4 files changed, 111 insertions(+), 43 deletions(-)

diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 5efeba056f1b..42a4cd7971fa 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -29,7 +29,6 @@
         Gsp,
         GspBootContext, //
     },
-    irq,
     regs,
     vgpu::VgpuManager, //
 };
@@ -387,11 +386,6 @@ pub(crate) fn new(
                 })?,
             }),
 
-            // Allocate a PCI interrupt vector.
-            _: {
-                let _irq_vector = irq::alloc_vector(pdev)?;
-            },
-
             gsp_static_info: {
                 // Obtain and display basic GPU information.
                 let info = gsp_resources.gsp.get_static_info(bar)?;
diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
index ef77066e0514..2f0e2644b9bd 100644
--- a/drivers/gpu/nova-core/irq.rs
+++ b/drivers/gpu/nova-core/irq.rs
@@ -21,16 +21,76 @@
     prelude::*,
 };
 
-pub(crate) fn alloc_vector(pdev: &pci::Device<Bound>) -> Result<pci::IrqVector<'_>> {
-    let msi_types = IrqTypes::default().with(IrqType::Msi).with(IrqType::MsiX);
-
-    let irq_vectors = match pdev.alloc_irq_vectors(1, 1, msi_types) {
-        Ok(vecs) => vecs,
-        Err(_) => {
-            dev_warn!(pdev.as_ref(), "MSI not available, falling back to INTx\n");
-            pdev.alloc_irq_vectors(1, 1, IrqTypes::default().with(IrqType::Intx))?
+/// The PCI interrupt vector that delivers each serviced subtree.
+///
+/// MSI-X raises a separate table entry per subtree, so subtree `N` arrives on entry `N`. MSI has a
+/// single message that every subtree raises, so all of them arrive on the one allocated entry.
+#[derive(Clone, Copy)]
+pub(crate) struct SubtreeVectors<'a> {
+    vectors: pci::IrqAllocation<'a>,
+    /// `TOP` bit of every subtree nova-core services.
+    serviced: u32,
+}
+
+impl<'a> SubtreeVectors<'a> {
+    /// Returns the interrupt type the PCI core selected for these vectors.
+    pub(crate) fn irq_type(&self) -> IrqType {
+        self.vectors.irq_type()
+    }
+
+    /// Returns the vector that delivers `subtree`, a single `TOP` bit of the form
+    /// `interrupt_tree::vector_subtree_mask` returns.
+    ///
+    /// # Errors
+    ///
+    /// `EINVAL` if `subtree` names anything other than a single subtree nova-core services.
+    pub(crate) fn vector_for(&self, subtree: u32) -> Result<pci::IrqVector<'a>> {
+        if subtree.count_ones() != 1 || subtree & self.serviced == 0 {
+            return Err(EINVAL);
         }
+
+        self.vectors.vector(entry_index(self.irq_type(), subtree))
+    }
+}
+
+/// Returns the index of the allocated entry that `subtree` raises.
+///
+/// MSI-X gives subtree `N` its own table entry `N`. MSI raises its one message from every subtree,
+/// and nova-core allocates a single entry for it. nova-core never allocates INTx.
+fn entry_index(irq_type: IrqType, subtree: u32) -> u32 {
+    match irq_type {
+        IrqType::MsiX => subtree.trailing_zeros(),
+        IrqType::Msi | IrqType::Intx => 0,
+    }
+}
+
+/// Allocates the interrupt vectors that the subtrees in `serviced` require.
+///
+/// Every subtree nova-core enables at `TOP` must have an allocated vector with a registered
+/// handler, or the interrupts it raises are lost. Linux masks every MSI-X entry a driver did not
+/// allocate, so the MSI-X request covers every entry up to the highest serviced subtree. A part
+/// whose MSI-X table is smaller than that falls back to a single MSI, which serves the whole tree.
+/// nova-core does not fall back to a shared INTx line.
+///
+/// # Errors
+///
+/// `EINVAL` if `serviced` is empty. The error from the MSI request if neither type can be
+/// allocated.
+pub(crate) fn alloc_vectors(
+    pdev: &pci::Device<Bound>,
+    serviced: u32,
+) -> Result<SubtreeVectors<'_>> {
+    // One entry per subtree up to and including the highest serviced one.
+    let msix_count = u32::BITS - serviced.leading_zeros();
+    if msix_count == 0 {
+        return Err(EINVAL);
+    }
+
+    let msix = IrqTypes::default().with(IrqType::MsiX);
+    let vectors = match pdev.alloc_irq_vectors(msix_count, msix_count, msix) {
+        Ok(vectors) => vectors,
+        Err(_) => pdev.alloc_irq_vectors(1, 1, IrqTypes::default().with(IrqType::Msi))?,
     };
 
-    irq_vectors.vector(0)
+    Ok(SubtreeVectors { vectors, serviced })
 }
diff --git a/drivers/gpu/nova-core/irq/hal.rs b/drivers/gpu/nova-core/irq/hal.rs
index 8de2f6e536c2..cf2d1aa080fa 100644
--- a/drivers/gpu/nova-core/irq/hal.rs
+++ b/drivers/gpu/nova-core/irq/hal.rs
@@ -50,7 +50,6 @@ impl PciIrqRearmMethod {
     /// `serviced` holds the `TOP` bit of every subtree the driver services, and `subtree` holds
     /// the bit of the one subtree the calling handler serves. Each method uses whichever of the
     /// two its interrupt type delivers on, so both are required.
-    #[expect(dead_code)]
     pub(super) fn rearm(self, bar: Bar0<'_>, serviced: u32, subtree: u32) {
         let subtrees = match self {
             // The written value is ignored, so any write rearms delivery.
@@ -98,7 +97,6 @@ fn implemented_subtrees(&self) -> u32 {
     ///
     /// `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.
-    #[expect(dead_code)]
     fn pci_irq_rearm_method(&self, irq_type: IrqType) -> Option<PciIrqRearmMethod>;
 }
 
diff --git a/drivers/gpu/nova-core/irq/interrupt_tree.rs b/drivers/gpu/nova-core/irq/interrupt_tree.rs
index 9f6cfed89bec..51add9f33c89 100644
--- a/drivers/gpu/nova-core/irq/interrupt_tree.rs
+++ b/drivers/gpu/nova-core/irq/interrupt_tree.rs
@@ -16,14 +16,16 @@
         Io, //
     },
     num::Bounded,
+    pci::IrqType,
     prelude::*,
 };
 
 use crate::{
     driver::Bar0,
-    gpu::{
-        Architecture,
-        Chipset, //
+    gpu::Chipset,
+    irq::hal::{
+        cpu_interrupt_hal,
+        PciIrqRearmMethod, //
     },
     regs::{
         NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF as CPU_INTR_LEAF,
@@ -82,31 +84,43 @@ impl Sealed for super::Pending {}
 pub(super) struct Tree {
     /// Number of implemented leaves in this tree, either 8 or 16.
     num_leaves: usize,
-    /// Mask of subtree bits the architecture implements.
-    subtree_mask: u32,
+    /// The subtrees this tree enables and services.
+    serviced_subtrees: u32,
+    /// Method that rearms PCI interrupt delivery, or `None` if the interrupt type needs no rearm
+    /// write.
+    rearm_method: Option<PciIrqRearmMethod>,
 }
 
 impl Tree {
-    /// Creates a `Tree` sized for `chipset`.
-    pub(super) fn new(chipset: Chipset) -> Self {
-        let num_leaves = match chipset.arch() {
-            Architecture::Turing | Architecture::Ampere | Architecture::Ada => 8,
-            Architecture::Hopper | Architecture::BlackwellGB10x | Architecture::BlackwellGB20x => {
-                16
-            }
-        };
-
+    /// Creates a `Tree` for `chipset` covering `serviced_subtrees`, with the rearm method that
+    /// `irq_type` requires.
+    ///
+    /// Each serviced subtree must have an allocated PCI vector and a registered handler, which
+    /// [`super::alloc_vectors`] sizes the allocation for. Bits outside the subtrees the
+    /// architecture implements are dropped.
+    pub(super) fn new(chipset: Chipset, irq_type: IrqType, serviced_subtrees: u32) -> Self {
+        let hal = cpu_interrupt_hal(chipset);
         Self {
-            num_leaves,
-            // Each subtree covers two leaves, so one bit per pair of leaves.
-            subtree_mask: (1u32 << (num_leaves / 2)) - 1,
+            num_leaves: hal.num_leaves(),
+            serviced_subtrees: serviced_subtrees & hal.implemented_subtrees(),
+            rearm_method: hal.pci_irq_rearm_method(irq_type),
+        }
+    }
+
+    /// Rearms PCI interrupt delivery to the CPU after servicing `subtree`, the `TOP` bit of the
+    /// one subtree the calling handler serves.
+    ///
+    /// A handler must call this before it returns, or it receives no further interrupts.
+    pub(super) fn rearm_pci_irq(&self, bar: Bar0<'_>, subtree: u32) {
+        if let Some(method) = self.rearm_method {
+            method.rearm(bar, self.serviced_subtrees, subtree);
         }
     }
 
     /// Returns a [`Top`] handle for this tree.
     pub(super) fn top(&self) -> Top {
         Top {
-            subtree_mask: self.subtree_mask,
+            serviced_subtrees: self.serviced_subtrees,
         }
     }
 
@@ -131,9 +145,9 @@ pub(super) fn trigger(&self, bar: Bar0<'_>, vector: u32) -> Result {
 
     /// Clears every pending bit in every implemented leaf.
     ///
-    /// The walk runs with every implemented subtree disabled at `TOP`, and every implemented
-    /// subtree is enabled on return, whatever its state on entry. The leaves cleared and the
-    /// `TOP_EN` writes both reach subtrees the driver does not service.
+    /// Disables this tree's serviced subtrees at `TOP` across the walk, then enables them,
+    /// whatever their state on entry. The leaves cleared reach subtrees the driver does not
+    /// service, and the `TOP_EN` writes do not.
     ///
     /// Call `drain()` only during probe. It must not run concurrently with an interrupt handler.
     pub(super) fn drain(&self, bar: Bar0<'_>) {
@@ -152,19 +166,21 @@ pub(super) fn drain(&self, bar: Bar0<'_>) {
 }
 
 /// Top-level view of the interrupt tree, enabling and disabling whole subtrees.
+///
+/// Both writes cover the serviced subtrees alone, leaving the rest of the tree as it was.
 pub(super) struct Top {
-    subtree_mask: u32,
+    serviced_subtrees: u32,
 }
 
 impl Top {
-    /// Enables interrupt delivery for every implemented subtree (`TOP_EN_SET`).
+    /// Enables this tree's serviced subtrees (`TOP_EN_SET`).
     pub(super) fn enable(self, bar: Bar0<'_>) {
-        bar.write(CPU_INTR_TOP_EN_SET, self.subtree_mask.into());
+        bar.write(CPU_INTR_TOP_EN_SET, self.serviced_subtrees.into());
     }
 
-    /// Disables interrupt delivery for every implemented subtree (`TOP_EN_CLEAR`).
+    /// Disables this tree's serviced subtrees (`TOP_EN_CLEAR`).
     pub(super) fn disable(self, bar: Bar0<'_>) {
-        bar.write(CPU_INTR_TOP_EN_CLEAR, self.subtree_mask.into());
+        bar.write(CPU_INTR_TOP_EN_CLEAR, self.serviced_subtrees.into());
     }
 }
 
-- 
2.55.0


  parent reply	other threads:[~2026-08-08  3:11 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-08  3:11 [PATCH 00/17] nova-core: GPU interrupt support and GSP event delivery John Hubbard
2026-08-08  3:11 ` [PATCH 01/17] rust: sync: completion: add wait_for_completion_timeout() John Hubbard
     [not found]   ` <DKK2DM3VK6TF.3KBBWP7S4A8T1@nvidia.com>
2026-08-09 21:43     ` John Hubbard
2026-08-08  3:11 ` [PATCH 02/17] rust: pci: expose the whole interrupt vector allocation John Hubbard
2026-08-09 13:27   ` Danilo Krummrich
2026-08-08  3:11 ` [PATCH 03/17] rust: pci: expose the allocated interrupt type John Hubbard
2026-08-09 13:24   ` Danilo Krummrich
2026-08-09 21:42     ` John Hubbard
2026-08-08  3:11 ` [PATCH 04/17] gpu: nova-core: allocate PCI MSI vector during probe John Hubbard
2026-08-08  3:11 ` [PATCH 05/17] gpu: nova-core: add the GIN CPU interrupt tree and MSI EOI registers John Hubbard
2026-08-08  3:11 ` [PATCH 06/17] gpu: nova-core: add the GIN interrupt tree API John Hubbard
2026-08-08  3:11 ` [PATCH 07/17] gpu: nova-core: add the per-architecture GIN CPU interrupt HAL John Hubbard
2026-08-08  3:11 ` John Hubbard [this message]
2026-08-08  3:11 ` [PATCH 09/17] gpu: nova-core: add an interrupt delivery self-test John Hubbard
2026-08-08  3:11 ` [PATCH 10/17] gpu: nova-core: dispatch GSP events instead of discarding them John Hubbard
2026-08-08  3:11 ` [PATCH 11/17] gpu: nova-core: match GSP RPC replies by sequence, not just function John Hubbard
2026-08-08  3:11 ` [PATCH 12/17] gpu: nova-core: recover the GSP receive path from corrupt framing John Hubbard
2026-08-08  3:11 ` [PATCH 13/17] gpu: nova-core: bound a GSP wait by a single deadline John Hubbard
2026-08-08  3:11 ` [PATCH 14/17] gpu: nova-core: drive GSP events with the SWGEN0 interrupt John Hubbard
2026-08-08  3:11 ` [PATCH 15/17] gpu: nova-core: retrigger the GSP falcon and clear every latched cause John Hubbard
2026-08-08  3:11 ` [PATCH 16/17] gpu: nova-core: add KUnit tests for the interrupt tree and HALs John Hubbard
2026-08-08  3:11 ` [PATCH 17/17] gpu: nova-core: document the GIN interrupt controller and GSP events John Hubbard

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260808031120.363869-9-jhubbard@nvidia.com \
    --to=jhubbard@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --cc=acourbot@nvidia.com \
    --cc=airlied@gmail.com \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=apopple@nvidia.com \
    --cc=bhelgaas@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=ecourtney@nvidia.com \
    --cc=gary@garyguo.net \
    --cc=joel@joelfernandes.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=shashanks@nvidia.com \
    --cc=simona@ffwll.ch \
    --cc=tmgross@umich.edu \
    --cc=ttabi@nvidia.com \
    --cc=wpierce@nvidia.com \
    --cc=zhiw@nvidia.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox