All of lore.kernel.org
 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 17/17] gpu: nova-core: document the GIN interrupt controller and GSP events
Date: Fri,  7 Aug 2026 20:11:19 -0700	[thread overview]
Message-ID: <20260808031120.363869-18-jhubbard@nvidia.com> (raw)
In-Reply-To: <20260808031120.363869-1-jhubbard@nvidia.com>

The hardware behind nova-core's interrupt support is not obvious from
the code. Delivery is edge-triggered and needs a rearm after every
interrupt, the rearm operation differs by GPU family and PCI interrupt
type, and a vector that latched while disabled is invisible in the TOP
summary register. Three different numbers are also all called a vector,
in GIN, the MSI-X table, and the Linux IRQ API.

Add a design document covering the two-level register tree, how it
reaches the CPU under MSI and MSI-X, and the rules those behaviors
impose on a handler. It also covers the GSP event: the falcon retrigger,
the handoff from boot-time polling to interrupts, and how its messages
are classified. A glossary names each term after the register or the
specification that defines it.

Assisted-by: Cursor:claude-opus-5
Reviewed-by: Will Pierce <wpierce@nvidia.com>
Signed-off-by: John Hubbard <jhubbard@nvidia.com>
---
 Documentation/gpu/nova/core/interrupts.rst | 686 +++++++++++++++++++++
 Documentation/gpu/nova/index.rst           |   1 +
 2 files changed, 687 insertions(+)
 create mode 100644 Documentation/gpu/nova/core/interrupts.rst

diff --git a/Documentation/gpu/nova/core/interrupts.rst b/Documentation/gpu/nova/core/interrupts.rst
new file mode 100644
index 000000000000..d7ddbfd6a0af
--- /dev/null
+++ b/Documentation/gpu/nova/core/interrupts.rst
@@ -0,0 +1,686 @@
+.. SPDX-License-Identifier: GPL-2.0
+.. SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+=================================================
+GPU interrupt handling: GIN and the GSP event
+=================================================
+
+This document describes how nova-core receives interrupts from the GPU on Turing
+and later parts. It covers the GPU Interrupt and Notification unit (GIN), which
+is the GPU's interrupt controller, and the GSP event interrupt.
+
+Throughout, *CPU* means the CPU and the nova-core driver running on it. The GPU
+also has on-chip processors that run their own firmware and receive their own
+interrupts, and the GSP (GPU System Processor) is one of them.
+
+The register names in this document are the names from the GPU hardware
+reference headers. The CPU tree's registers live in the per-function
+``NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_*`` aperture on every supported part, and
+the controller itself has a second name on pre-Hopper parts (see "Register
+naming").
+
+Terminology
+===========
+
+Three different numbers are all called a "vector" in the surrounding material.
+This document gives each one its own name and never uses "vector" on its own.
+
+GIN vector
+    The GPU-internal interrupt source number, 0 through 511 on Hopper. It is a
+    bit address within the tree: leaf ``vector / 32``, bit ``vector % 32``. The
+    CPU doorbell is GIN vector 129 and the GSP event is GIN vector 155.
+
+MSI-X entry
+    An index into the device's MSI-X table, 0 through 7 on Hopper. Linux's
+    ``struct msix_entry`` names its Linux IRQ number ``.vector``, which is a
+    third meaning.
+
+Linux IRQ number
+    What ``request_irq()`` takes, obtained from ``pci_irq_vector()``.
+
+The remaining terms, each named for the register or the specification that owns
+it:
+
+enable / disable a GIN vector
+    ``LEAF_EN_SET`` and ``LEAF_EN_CLEAR``.
+
+enable / disable a subtree
+    ``TOP_EN_SET`` and ``TOP_EN_CLEAR``.
+
+serviced subtree
+    A subtree nova-core enables and has a handler for.
+
+rearm
+    Restoring PCI interrupt delivery after servicing an interrupt. It is a
+    ``TOP_EN`` disable-then-enable cycle everywhere except under pre-Hopper
+    MSI, where it is a write to the end-of-interrupt (EOI) register in the BAR0
+    configuration-space mirror (see "Rearming PCI interrupt delivery").
+
+mask
+    Reserved for the two places hardware and the PCI specification use the
+    word: the MSI-X per-entry Vector Control mask bit, which Linux owns, and
+    the falcon cause masks. It never names a GIN enable.
+
+latched, pending
+    A ``LEAF`` bit records its source whether or not the GIN vector is enabled.
+    A disabled vector's pending bit never appears in ``TOP``.
+
+clear a leaf vector
+    Write a 1 to the vector's bit in ``LEAF``. Open RM spells the same
+    operation ``intrClearLeafVector_HAL``.
+
+pending bits
+    The plain bitmask value read from a ``LEAF`` register.
+
+unit
+    A generic interrupt-raising block. "Engine" is reserved for the blocks that
+    do usermode work: GR, CE, NVDEC, and the like.
+
+The GIN controller
+==================
+
+A GPU has many interrupt sources: the GSP, copy engines, the graphics engine,
+video decode and encode, the MMU fault path, timers, and others. Each one has a
+GIN vector number, which is internal to the controller and is not a PCI vector
+index.
+
+GIN records which vectors are pending in its own two-level register tree and
+raises the PCI interrupt when an enabled vector becomes pending. The CPU's
+handler reads that tree to tell the sources apart, clears the pending vectors,
+and runs the work for each.
+
+How the tree reaches the CPU over PCI
+-------------------------------------
+
+How many PCI interrupts the tree needs depends on the interrupt type Linux
+grants.
+
+MSI has a single message, and every subtree raises that one message. One
+allocated vector serves the whole tree.
+
+MSI-X raises a separate table entry per subtree, so a subtree's interrupts
+arrive on the table entry whose index is the subtree number. Linux masks each
+table entry a driver did not allocate, and a masked entry sends no message: the
+request sets a bit in the pending-bit array and waits for an unmask that never
+comes. A driver that leaves out the entry its subtree raises loses every
+interrupt on that subtree, and loses it silently, with the GIN leaf and TOP
+registers showing the vector pending and enabled while no handler runs.
+
+The serviced-subtree invariant
+------------------------------
+
+Every subtree enabled at TOP must have an allocated PCI vector with a registered
+handler.
+
+MSI satisfies this with one message that every subtree raises. MSI-X needs one
+allocated, unmasked entry per serviced subtree, and a PCI allocation cannot be
+sparse, so it runs from entry 0 through the highest serviced subtree::
+
+    MSI-X, with subtree 2 serviced:
+
+      subtree 0  ->  entry 0   allocated, no handler, stays masked
+      subtree 1  ->  entry 1   allocated, no handler, stays masked
+      subtree 2  ->  entry 2   handler here, and its rearm covers subtree 2
+
+    MSI, with any serviced set:
+
+      every serviced subtree  ->  the one allocated vector, whose handler's
+                                  rearm covers the whole serviced set
+
+The entries allocated below a serviced subtree that the driver does not service
+cost nothing: Linux unmasks an entry only when its interrupt is requested, and a
+disabled subtree raises nothing.
+
+nova-core services exactly one subtree, subtree 2, because both the vectors it
+uses are in leaf 4: the GSP event (155) and the self-test doorbell (129). That
+is also the subtree the resource manager assigns to its ``UVM_SHARED`` interrupt
+category on every chipset nova-core supports.
+
+Interrupt trees
+===============
+
+GIN keeps a separate interrupt tree for each place an interrupt can be sent to:
+
+* One tree per PCIe function. The Physical Function (PF) has a tree, and each
+  Virtual Function (VF) has a tree.
+* One tree per on-chip microcontroller that receives interrupts, starting with
+  the GSP.
+
+Each destination reaches its own tree through its own BAR0 and cannot reach any
+other tree. GSP firmware selects the tree each unit's interrupt is sent to.
+
+nova-core services the CPU tree of one function. The VF trees and the
+microcontroller trees belong to firmware or to virtual functions.
+
+The two-level tree
+==================
+
+Each tree has two levels. The bottom level is the LEAF registers, which hold one
+pending bit per vector. The top level is the single TOP register, which
+summarizes the leaves.
+
+* Each ``LEAF(i)`` is a 32-bit register holding the pending bits for vectors
+  ``i * 32`` through ``i * 32 + 31``. A set bit means that vector is pending.
+* ``TOP`` is a single 32-bit read-only register. Each of its bits summarizes one
+  *subtree*, which is a pair of adjacent leaves. TOP bit ``N`` reflects
+  ``LEAF[2N]`` and ``LEAF[2N + 1]`` as filtered by their leaf enables, so a
+  vector that latched while disabled does not appear in TOP.
+
+A subtree is two leaves, so a part with L leaves has L / 2 subtrees and uses
+that many TOP bits. An 8-leaf part uses TOP bits 0 through 3, and the other 28
+bits always read 0. A 16-leaf part uses TOP bits 0 through 7::
+
+    TOP  (one 32-bit register, and an 8-leaf part uses only bits 0..3)
+
+      bit 0  ->  subtree 0  ->  LEAF[0], LEAF[1]   vectors   0..63
+      bit 1  ->  subtree 1  ->  LEAF[2], LEAF[3]   vectors  64..127
+      bit 2  ->  subtree 2  ->  LEAF[4], LEAF[5]   vectors 128..191
+      bit 3  ->  subtree 3  ->  LEAF[6], LEAF[7]   vectors 192..255
+      bits 4..31: always 0 on an 8-leaf part (a 16-leaf part uses bits 0..7)
+
+    A LEAF is one 32-bit register, one bit per vector. For example, LEAF[4]
+    holds vectors 128..159:
+
+      bit 1  = vector 129  (CPU doorbell)
+      bit 27 = vector 155  (GSP event)
+
+Registers
+---------
+
+All the registers are 32 bits, defined in ``regs.rs`` under the
+``NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_*`` names. The leaf registers are arrays
+indexed by leaf number:
+
+* ``LEAF(i)`` holds the pending bits for the vectors in leaf ``i``. Reading
+  returns the pending bits, and writing a 1 to a bit clears that vector
+  (write-1-to-clear).
+* ``LEAF_EN_SET(i)`` and ``LEAF_EN_CLEAR(i)`` enable and disable individual
+  vectors in leaf ``i``.
+* ``TOP`` is the read-only summary: bit N is set when an enabled vector is
+  pending in ``LEAF[2N]`` or ``LEAF[2N + 1]``. A vector that latched while its
+  leaf enable was clear does not appear.
+* ``TOP_EN_SET`` and ``TOP_EN_CLEAR`` enable and disable subtrees.
+* ``LEAF_TRIGGER`` makes a vector pending in software. The self-test uses it.
+
+Mapping a vector to the tree
+----------------------------
+
+Each vector occupies one bit of one leaf, and each leaf belongs to one
+subtree::
+
+    leaf    = v / 32
+    bit     = v % 32
+    subtree = leaf / 2
+
+Both of the vectors nova-core names by number fall in leaf 4: vector 129 at bit
+1 and vector 155 at bit 27, so both arrive under subtree 2.
+
+Enabling and clearing
+---------------------
+
+Each bit of a set or clear register acts on its own: writing a 1 performs the
+action for that bit, and writing a 0 leaves the bit's state alone. No caller
+ever needs a read-modify-write.
+
+* ``LEAF(i)`` is write-1-to-clear. Reading returns the pending bits. Each bit
+  must be cleared before its vector is serviced.
+* ``LEAF_EN_SET(i)`` and ``LEAF_EN_CLEAR(i)`` enable and disable individual
+  vectors in a leaf.
+* ``TOP_EN_SET`` and ``TOP_EN_CLEAR`` enable and disable whole subtrees.
+
+A vector reaches the CPU only when both its leaf enable bit and its subtree's
+TOP enable bit are set. The leaf enable governs delivery and the TOP summary,
+but not the latch: a disabled vector still latches its LEAF bit, and that bit is
+visible only by reading the leaf directly.
+
+How a unit interrupt reaches the CPU
+====================================
+
+A unit does not write a LEAF register itself. Each unit has an interrupt routing
+register, and GSP firmware programs it once at boot. Firmware writes three
+things into it: the unit's VECTOR (which leaf bit it uses), its GFID (which tree
+to post to: the PF or a specific VF), and its destination flags (which consumers
+get it: the CPU, the GSP, or another on-chip microcontroller).
+
+Later, when a unit has an event, three things happen in turn::
+
+    1. The unit sends an interrupt message to GIN, carrying the VECTOR, GFID,
+       and destination flags from its routing register.
+    2. GIN sets bit (VECTOR % 32) in LEAF[VECTOR / 32], in the tree that the
+       GFID and destination flags select.
+    3. If that vector is enabled and its subtree is enabled, GIN raises the PCI
+       interrupt to the CPU.
+
+Because firmware assigns the vectors, nova-core does not hardcode which vector
+belongs to which unit. The one exception nova-core relies on is the GSP event
+vector, which firmware pins to a fixed number (see "The GSP event vector").
+
+Edge behavior and rearm
+=======================
+
+The pieces behave as follows:
+
+* A LEAF bit is a latch. It is set on the rising edge of its source and stays set
+  until the CPU writes a 1 to it. A source that stays high does not set the bit
+  again.
+* TOP is read-only and reports the subtree's *enabled* pending state. A vector
+  that latched while its leaf enable was clear does not appear in TOP.
+* LEAF_EN and TOP_EN are CPU-controlled enables that allow or block delivery.
+* GIN raises the PCI interrupt for subtree N when the subtree's enabled pending
+  state goes from low to high::
+
+    Per vector, in leaf i at bit b:
+        LEAF[i][b] AND LEAF_EN[i][b]
+
+    Per subtree N, across its leaves 2N and 2N + 1:
+        OR of every enabled pending bit  ->  TOP[N]
+
+    Delivery for subtree N:
+        TOP[N] AND TOP_EN[N]  ->  rising edge  ->  PCI interrupt
+
+    TOP_EN applies below TOP, so disabling a subtree halts delivery and leaves
+    what TOP reports unchanged.
+
+Because a disabled vector is invisible in TOP, code that must find every pending
+bit cannot descend from TOP. It has to read the leaves directly. Open RM does
+the same: its stalling-interrupt path never reads TOP, and instead walks every
+subtree it implements reading LEAF registers.
+
+Because delivery is edge-triggered, writing ``TOP_EN_SET`` while an enabled leaf
+bit is still set produces a new edge. A full tree walk uses this: after it
+clears the leaves, it writes ``TOP_EN_SET`` so an interrupt that arrived during
+servicing is still delivered.
+
+A unit that holds an internal level signal high does not produce a new leaf edge
+after the CPU clears the bit, so rearming alone does not re-deliver it. Such
+units have an ``INTR_RETRIGGER`` register that forces a new edge.
+
+Retriggering a falcon
+---------------------
+
+A falcon signals the tree on a transition of its enabled interrupt causes.
+Clearing the tree leaf while a cause is still latched leaves no transition, so
+the vector stays clear however many further causes arrive. Both clear orders
+have that window, so a handler on a falcon vector writes ``INTR_RETRIGGER`` on
+every path that services the vector.
+
+That re-emit must not be able to raise a cause that nothing clears. A cause the
+handler does not service is removed from the falcon's enabled set with
+``IRQMCLR`` and cleared with ``IRQSCLR`` before the re-emit.
+
+``INTR_RETRIGGER`` is absent on Turing falcons and present from GA100 onward, so
+the write is conditional on the architecture. A Turing handler cannot supply a
+transition that went missing, so it must leave no cause latched: it reads the
+status once and takes every cause that status reports, rather than stopping at
+the first one it recognizes. A cause left behind holds the falcon's enabled set
+non-empty, and no later cause from that falcon signals the tree at all.
+
+One window stays open on Turing. A cause that arrives between the status read
+and the clears is not in the status, so it stays latched after the tree leaf has
+been cleared. Open RM has the same window: ``kgspService_TU102`` ends with
+``kflcnIntrRetrigger``, which is implemented from GA100 onward and does nothing
+on Turing.
+
+Rearming PCI interrupt delivery
+-------------------------------
+
+Clearing the GIN state is not enough. A message-signaled interrupt is
+delivered once per edge, and the PCI side delivers no further interrupt until the
+CPU rearms it. Which operation does that depends on the GPU family and on the
+interrupt type Linux granted:
+
+==================  =====  ===========================================
+Architecture        Type   Rearm operation
+==================  =====  ===========================================
+Turing through Ada  MSI    write the configuration-mirror EOI register
+Hopper and later    MSI    clear then set the serviced TOP enables
+Any                 MSI-X  clear then set the handler's own TOP enable
+==================  =====  ===========================================
+
+The MSI forms cover every serviced subtree, because one message serves all of
+them. The MSI-X form covers one subtree, because each serviced subtree has its
+own table entry and its own handler.
+
+INTx is level-triggered and needs no rearm write. nova-core does not allocate it,
+so it never reaches a handler.
+
+A handler must rearm once per delivered interrupt, on every path that services
+one. A handler that skips the rearm receives no further interrupts at all.
+
+The rearm is separate from the TOP restore at the end of a full tree walk, even
+though two of the three forms write the same registers. The walk clears TOP_EN
+on entry so that it can read and clear without new interrupts arriving, and sets
+it again on exit. For the two enable-cycle forms that restore also rearms, but
+pre-Hopper MSI rearms through the configuration mirror, which the walk never
+writes, so the startup sequence rearms explicitly after the walk.
+
+Servicing an interrupt
+======================
+
+nova-core services the tree in one of two ways, depending on which code handles
+the interrupt.
+
+The GSP event handler services one vector, so it leaves its subtree enabled and
+reads and clears only its own leaf bit, touching a single leaf per interrupt.
+
+The startup drain walks the whole tree instead, because it must clear whatever is
+pending across every subtree rather than one known vector. It disables the
+subtrees, clears every pending leaf, then enables them again.
+
+The drain reads every implemented leaf rather than descending from TOP. Boot
+latches vectors while they are still disabled, and those bits do not appear in
+TOP, so a TOP-driven walk would skip exactly the state the drain has to clear.
+
+The two paths as register operations::
+
+    Full tree walk (the one-time startup drain):
+        write TOP_EN_CLEAR = serviced        disable, to stop new interrupts
+        for each implemented subtree N, for i in {2N, 2N+1}:
+            pending = read LEAF[i]           pending vectors in this leaf
+            write LEAF[i] = pending          clear (write-1-to-clear)
+        write TOP_EN_SET = serviced          restore TOP_EN
+
+    Notification, subtree stays enabled (the GSP event handler, and the
+    self-test, which deliberately mirrors it):
+        pending = read LEAF[gsp_leaf]        is our vector's bit set?
+        write LEAF[gsp_leaf] = GSP_BIT       clear only our bit
+        rearm PCI interrupt delivery         see "Rearming PCI interrupt
+                                             delivery"
+
+Two rules for the full walk:
+
+* Clear every pending leaf bit, including bits nova-core does not handle. An
+  uncleared bit holds its subtree in the pending state, and restoring TOP_EN
+  over it produces a delivery edge straight away. The walk writes back every bit
+  it read.
+* Restore TOP_EN only after clearing every pending leaf. Otherwise a still-set
+  bit raises the interrupt again while the walk is still running.
+
+The notification path clears one bit, so a vector pending alongside it in the
+same leaf keeps its bit and stays pending for whoever services it. Both paths
+must rearm PCI delivery for the interrupt they serviced.
+
+Interrupts and notifications
+============================
+
+Two kinds of source use the tree:
+
+* An interrupt means a unit needs servicing.
+* A notification means a unit is reporting that something happened, such as a log
+  record or completed work.
+
+The GSP event is a notification. Its handler leaves the subtree enabled and
+clears only the GSP leaf bit.
+
+The hardware manuals also split the vector space into "stall" and "nonstall"
+ranges. Those name address ranges rather than describing behavior. nova-core
+does not service the stall range.
+
+Per-architecture differences
+============================
+
+The tree is the same on every supported GPU except for its size, and there are
+only two sizes, split at Hopper:
+
+===================  ======  ========  ====================
+GPUs                 Leaves  Subtrees  Implemented subtrees
+===================  ======  ========  ====================
+Turing, Ampere, Ada  8       4         ``0x0f``
+Hopper and later     16      8         ``0xff``
+===================  ======  ========  ====================
+
+Only the lower eight leaves exist before Hopper, so TOP bits 4 through 31 read
+zero there. Hopper and later have 16 leaves, though sources do not populate all
+of them.
+
+The implemented subtrees bound which TOP bits mean anything. That set is wider
+than the set nova-core enables, which is the subtrees it services, per the
+serviced-subtree invariant. The startup drain still reads every implemented
+leaf, because a vector that latched while disabled is invisible in TOP and can
+be in any leaf.
+
+The HAL provides the leaf count, and the subtree count (leaves / 2) and the
+implemented-subtree set derive from it. The rearm method is the HAL's other
+per-architecture value.
+
+Multi-die parts
+===============
+
+On multi-die parts the controller is replicated per die, with an aggregation
+level above the per-die TOP registers. nova-core services the CPU tree of one
+function on a single-die part, so it does not drive the aggregation level.
+
+The GSP event
+=============
+
+When the GSP has output for the CPU (log records, error records, and other
+events), it writes the messages into the GSP-to-CPU queue in shared memory and
+raises SWGEN0, one of the software-generated interrupt outputs of the GSP
+microcontroller (a "falcon" in NVIDIA hardware). SWGEN0 is routed through a GIN
+vector, so it reaches the CPU as a PCI interrupt::
+
+    GSP writes messages into the GSP-to-CPU queue
+    GSP raises SWGEN0
+    GIN sets the GSP leaf bit, and the subtree becomes pending
+    PCI interrupt -> Linux IRQ -> nova-core top half, in IRQ context, which
+                                 must not sleep:
+        read the GSP leaf bit and clear it (subtree stays enabled)
+        read the GSP falcon IRQ status, clearing SWGEN0 if it was set
+        for every other cause that status reports: report it, then remove it
+            from the falcon's enabled set and clear it
+        retrigger the falcon
+        rearm PCI interrupt delivery
+        wake the IRQ thread if SWGEN0 was set
+    IRQ thread, which may sleep: take the command-queue lock and drain the
+        GSP-to-CPU queue, routing each message
+
+A halt and a posted message can be pending together, so the top half handles
+every cause the status reports rather than choosing between them (see
+"Retriggering a falcon").
+
+The interrupt is only the trigger to drain the queue. A thread polling for a
+command reply routes the messages it reads through the same classifier (see
+"Draining and classifying the GSP-to-CPU queue").
+
+If the drain fails, the queue cannot advance past the message it could not parse,
+so every later notification would repeat the same failure. The IRQ thread
+disables the GSP vector before reporting the failure, which leaves the queue
+unserviced until the device is reset.
+
+Enabling the GSP event
+----------------------
+
+SWGEN0 is a latch, and the GSP drives no new edge into the tree while it stays
+set. GSP boot consumes its notifications by polling the queue, which leaves the
+latch set and leaves stale state in the tree, so the handoff from polling to
+interrupts has a required order::
+
+    disable every implemented vector    drop enables left by boot or by a
+                                        driver that ran before this one
+    drain the tree (full walk)          clear stale GIN state from boot
+    clear the SWGEN0 latch              so the next assertion makes an edge
+    rearm PCI interrupt delivery        the walk does not do it under
+                                        pre-Hopper MSI
+    register the threaded IRQ handler   nothing can reach it yet
+    enable the GSP vector at its leaf   deliveries become possible here
+    drain the GSP-to-CPU queue          messages posted before the clear
+
+Clearing the latch makes the first interrupt possible. Messages the GSP posted
+before that clear produce no interrupt, so the queue drain follows.
+
+The tree is quiesced before the handler is registered. Registering unmasks the
+PCI interrupt, and a leaf enable that boot left set would reach a handler that
+services one vector and has no way to service any other. Open RM clears all
+leaf enables at the same point for the same reason.
+
+The latch is cleared after the tree walk, not before. The walk erases every leaf
+bit, so a message posted between an earlier clear and the walk would leave the
+latch set with nothing in the tree to show for it, and on Turing no later
+message would signal the tree at all. Clearing last can instead leave the GSP
+vector pending with the latch already clear, so enabling the vector delivers one
+interrupt whose ``IRQSTAT`` reads zero. The queue drain that follows reads the
+message.
+
+The GSP event vector
+--------------------
+
+The GSP event uses a fixed vector, ``GSP_INTR_0_VECTOR`` (155), on Turing
+through Blackwell. Vector 155 is leaf 4, bit 27, subtree 2. nova-core enables
+that leaf bit and services it, with no runtime vector discovery.
+
+A full unit-to-vector table can be fetched from the GSP by RPC. nova-core does
+not fetch it, because a pinned vector needs no lookup.
+
+Draining and classifying the GSP-to-CPU queue
+=============================================
+
+The queue carries both command replies and unsolicited events. Each message is
+routed by its function code and its RPC sequence number, into one of three
+classes:
+
+* Function code and sequence both match the awaited reply. The message is
+  decoded and returned to the caller that sent the command.
+* The function code matches but the sequence does not. This is a reply to a
+  command that already timed out, so it is logged at warning level and dropped
+  rather than satisfying a later command that reused the same function code.
+* Anything else is an unsolicited event. OS-error and robust-channel records are
+  logged at error level. An unrecognized function code is logged at warning
+  level. Other known events (GSP logs, libos prints, assertion records,
+  lifecycle notices) need no action and are not logged again, because the RPC
+  receive trace already records their arrival.
+
+The read pointer advances past the message in all three cases, and also when a
+matched message fails to decode, so a message is never left at the queue head
+for the next receive to parse again.
+
+Corrupt framing is the exception. A message carries its length inside the
+region the checksum covers, so once the framing or the checksum fails there is
+no trustworthy length with which to skip the message. Such a failure poisons the
+queue and every later receive fails, which the IRQ thread reports before
+disabling the GSP vector.
+
+The classifier is a fixed set of function codes rather than a handler registry.
+The events that need action are handled directly in it.
+
+Both the polling path and the IRQ thread route messages through this classifier
+under the command-queue lock. Replies and events share one queue and one set of
+read pointers, so one lock covers the whole drain. A thread waiting for a reply
+dispatches any event it reads first and keeps waiting, under a single deadline
+for the whole wait rather than a fresh timeout after each message.
+
+One lock means a drain waits for an in-flight command's receive to finish or
+time out. For log and error records that delay does not matter.
+
+Design notes
+============
+
+Register naming
+---------------
+
+nova-core uses the ``NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_*`` names for the CPU
+tree on both pre-Hopper and Hopper-plus parts. Any function reaches its own tree
+through that aperture. The Hopper-plus central aperture (``NV_GIN_CPU_INTR_*``)
+configures other functions and is not used by the CPU path.
+
+The controller has two names in the hardware headers and in Open RM.
+``NV_CTRL`` names the tree on pre-Hopper parts, and ``NV_GIN`` names the
+Hopper+ unit that contains the tree along with arbiter logic. This document
+calls the controller GIN throughout, because the tree nova-core drives is the
+same on every supported part.
+
+Type-state tree API
+-------------------
+
+Servicing a leaf has a required order: read its pending bits, then clear them.
+The code encodes the two stages as distinct types (``Idle`` and ``Pending``) so
+that clearing a leaf before reading it does not compile. ``Top`` carries no type
+state, because enabling and disabling a subtree can happen in any order.
+
+The types order the calls on a single handle. They are not a lock and they do
+not coordinate the tree as a whole. Nothing stops two walks from running against
+the tree at once. nova-core does not run concurrent walks: the GSP event handler
+touches only its own leaf and never walks the tree, and the only whole-tree
+walk, the startup drain, runs once during probe.
+
+Threaded handler
+----------------
+
+The drain sleeps: it takes the command-queue mutex and walks shared memory, so it
+cannot run in hard-IRQ context. nova-core uses a threaded IRQ handler. The top
+half clears the GIN leaf, takes every cause the falcon reports, rearms delivery,
+and wakes the IRQ thread if SWGEN0 was among them. The thread takes the lock and
+drains the queue. The self-test does no sleeping work and uses a non-threaded
+handler with a completion.
+
+Shared BAR0 mapping
+-------------------
+
+The GPU, the self-test, and the GSP event handler read the same BAR0 registers.
+nova-core keeps one BAR0 mapping and lets each of them borrow it. An interrupt
+handler is torn down when the device unbinds, so it only runs while the mapping
+is alive.
+
+Self-test
+=========
+
+The self-test runs during driver probe. It registers a real interrupt handler
+and confirms that an interrupt injected at the GPU is delivered all the way to
+that handler, so it needs a working GPU and PCI interrupt path. It is gated by
+``CONFIG_NOVA_CORE_IRQ_SELFTEST`` and runs before GSP boot, so it never touches
+GSP interrupt state.
+
+The parts with no hardware dependency are covered by KUnit tests instead: the
+vector encoding, the subtree and leaf arithmetic, and the per-architecture rearm
+policy.
+
+The test drives ``LEAF_TRIGGER``, a hardware register that every supported part
+implements. Writing a vector number to it latches that vector exactly as its
+unit would, after which the vector takes the ordinary path to the CPU under the
+ordinary enables.
+
+The test drives vector 129, at leaf 4 bit 1. It registers a handler for that
+vector and triggers it twice, waiting for the first delivery before triggering
+the second. Its handler deliberately mirrors the notification path: it clears
+only its own leaf bit and rearms PCI interrupt delivery, rather than walking the
+tree.
+
+The two interrupts cannot coalesce into one, because the second is triggered
+only after the first handler has finished. A handler that fails to rearm times
+out on the second delivery instead of passing. A single delivery serviced by a
+full tree walk cannot detect that, because the walk's own TOP_EN restore
+produces an edge by itself.
+
+The test passes only if both deliveries arrive, each one finds the doorbell bit
+and nothing else pending in the leaf, and the leaf is clear once the source is
+stopped. Anything else fails probe. Requiring the exact mask on the second
+delivery shows that the first handler's clear reached the hardware. The test
+runs before GSP boot on a leaf the drain has just cleared, so no other vector in
+that leaf can be active and the exact mask costs nothing.
+
+The test borrows the allocation that probe made for the serviced subtrees rather
+than allocating its own, and looks up the vector for the doorbell's own subtree.
+A doorbell vector moved to a subtree nova-core does not service fails that
+lookup, and with it the self-test and probe, rather than being misrouted
+silently.
+
+The test exercises the interrupt path from the GPU to the handler without GSP
+firmware, which is useful when bringing up PCI, MSI, MSI-X, and passthrough
+setups. Under MSI-X a pass also shows that the per-subtree table entry routing
+works, since the delivery arrives on the entry belonging to the serviced
+subtree.
+
+Virtualization
+==============
+
+The per-function trees, the GFID routing, and the central ``NV_GIN`` aperture
+support virtualization: each VF gets its own tree, and the PF or firmware routes
+a unit's interrupt to the right function. MIG (multi-instance GPU) partitioning
+adds more structure. nova-core services the CPU tree of one function, and
+implements no VF tree management, GFID routing, or MIG support.
+
+References
+==========
+
+* nova-core source: the register definitions in ``regs.rs``, the interrupt HAL
+  and tree API in the ``irq`` module, and the GSP command queue in the ``gsp``
+  module.
diff --git a/Documentation/gpu/nova/index.rst b/Documentation/gpu/nova/index.rst
index 2afa58e8f08d..2130d1caf4c3 100644
--- a/Documentation/gpu/nova/index.rst
+++ b/Documentation/gpu/nova/index.rst
@@ -34,3 +34,4 @@ vGPU manager VFIO driver and the nova-drm driver.
    core/fwsec
    core/falcon
    core/tlv
+   core/interrupts
-- 
2.55.0


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

Thread overview: 26+ 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
2026-08-09  2:40   ` Alexandre Courbot
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-10 22:53       ` Danilo Krummrich
2026-08-10 22:55         ` John Hubbard
2026-08-11  3:09         ` 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 ` [PATCH 08/17] gpu: nova-core: allocate interrupt vectors for the serviced subtrees John Hubbard
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 ` John Hubbard [this message]

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-18-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 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.