mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Alexandre Courbot" <acourbot@nvidia.com>
To: "John Hubbard" <jhubbard@nvidia.com>
Cc: "Danilo Krummrich" <dakr@kernel.org>,
	"Timur Tabi" <ttabi@nvidia.com>,
	"Alistair Popple" <apopple@nvidia.com>,
	"Eliot Courtney" <ecourtney@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>
Subject: Re: [PATCH v4 15/17] gpu: nova-core: service GSP events from the SWGEN0 interrupt
Date: Mon, 21 Sep 2026 15:46:49 +0900	[thread overview]
Message-ID: <DLKSJOFH2EZ1.21JRCSY7IY1ZP@nvidia.com> (raw)
In-Reply-To: <20260912044400.677097-16-jhubbard@nvidia.com>

On Sat Sep 12, 2026 at 5:43 AM BST, John Hubbard wrote:
<...>
> diff --git a/drivers/gpu/nova-core/falcon/gsp.rs b/drivers/gpu/nova-core/falcon/gsp.rs
> index 4c96ae325fda..dfa08bc6867c 100644
> --- a/drivers/gpu/nova-core/falcon/gsp.rs
> +++ b/drivers/gpu/nova-core/falcon/gsp.rs
> @@ -5,6 +5,7 @@
>          io_project,
>          poll::read_poll_timeout,
>          register,
> +        register::Array,
>          Io,
>          Mmio, //
>      },
> @@ -18,9 +19,11 @@
>          NovaRegisters, //
>      },
>      falcon::{
> +        hal,
>          Falcon,
>          FalconEngine, //
>      },
> +    gpu::Chipset,
>      regs,
>  };
>  
> @@ -46,14 +49,72 @@ fn pfalcon2(io: Bar0<'_>) -> Mmio<'_, super::PFalcon2Registers> {
>      }
>  }
>  
> -impl<'a> Falcon<'a, Gsp> {
> -    /// Clears the SWGEN0 bit in the Falcon's IRQ status clear register to
> -    /// allow GSP to signal CPU for processing new messages in message queue.
> -    pub(crate) fn clear_swgen0_intr(&self) {
> -        self.pfalcon
> -            .write_reg(regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true));
> +impl Gsp {
> +    /// Clears the SWGEN0 latch in the GSP falcon.
> +    ///
> +    /// While the latch is set, no later message signals the tree, so a caller that consumed a
> +    /// notification by polling must clear it.
> +    pub(crate) fn clear_swgen0_intr(bar: Bar0<'_>) {
> +        Self::pfalcon(bar).write_reg(regs::NV_PFALCON_FALCON_IRQSCLR::zeroed().with_swgen0(true));
> +    }

This hunk is the symptom of something we are losing in this series: you
don't need a reference to the Falcon instance anymore to manage
interrupts, just call a crate-public method with a BAR reference. I
think there is a way to design this properly but I don't trust the LLM
to do it right, so I'll fix it in a follow-up after the series is
merged.

> +
> +    /// Reads the GSP falcon causes that are routed to the host, without clearing any latch.
> +    ///
> +    /// Every one of them other than SWGEN0 reports a GSP fault.
> +    pub(crate) fn read_host_intr(
> +        bar: Bar0<'_>,
> +        chipset: Chipset,
> +    ) -> regs::NV_PFALCON_FALCON_IRQSTAT {
> +        let latched = Self::pfalcon(bar).read(regs::NV_PFALCON_FALCON_IRQSTAT);
> +
> +        hal::falcon_intr_hal(chipset)
> +            .riscv_routing()
> +            .host_routed_causes(Self::pfalcon2(bar), latched)
> +    }
> +
> +    /// Reads the host-routed causes and clears the SWGEN0 latch if it was set.
> +    ///
> +    /// Returns the causes as read, before the clear. No other latch changes.
> +    pub(crate) fn take_host_intr(
> +        bar: Bar0<'_>,
> +        chipset: Chipset,
> +    ) -> regs::NV_PFALCON_FALCON_IRQSTAT {
> +        let status = Self::read_host_intr(bar, chipset);
> +
> +        if status.swgen0() {
> +            Self::clear_swgen0_intr(bar);
> +        }
> +
> +        status
> +    }
> +
> +    /// Clears the latch of every interrupt cause set in `status`.
> +    ///
> +    /// A cause driven from outside the falcon is still set on return, and
> +    /// [`Self::read_host_intr`] reports the causes that remain.
> +    pub(crate) fn clear_intr(bar: Bar0<'_>, status: regs::NV_PFALCON_FALCON_IRQSTAT) {
> +        Self::pfalcon(bar).write_reg(regs::NV_PFALCON_FALCON_IRQSCLR::from(status.into_raw()));
> +    }
> +
> +    /// Retriggers the GSP falcon, which then re-emits its host-routed causes into the tree.
> +    ///
> +    /// Call this only once every host cause is clear. A cause still set is re-emitted at once, and
> +    /// its vector arrives again as soon as delivery is rearmed.
> +    ///
> +    /// Does nothing on Turing, whose falcons have no retrigger register.
> +    pub(crate) fn retrigger_intr(bar: Bar0<'_>, chipset: Chipset) {
> +        if !hal::falcon_intr_hal(chipset).has_intr_retrigger() {
> +            return;
> +        }
> +
> +        Self::pfalcon(bar).write(
> +            Array::at(0),
> +            regs::NV_PFALCON_FALCON_INTR_RETRIGGER::zeroed().with_trigger(true),
> +        );
>      }
> +}
>  
> +impl<'a> Falcon<'a, Gsp> {
>      /// Checks if GSP reload/resume has completed during the boot process.
>      pub(crate) fn check_reload_completed(&self, timeout: Delta) -> Result<bool> {
>          read_poll_timeout(
> diff --git a/drivers/gpu/nova-core/falcon/hal.rs b/drivers/gpu/nova-core/falcon/hal.rs
> index 052610c4a4da..3f1f509eccbd 100644
> --- a/drivers/gpu/nova-core/falcon/hal.rs
> +++ b/drivers/gpu/nova-core/falcon/hal.rs
> @@ -82,7 +82,6 @@ fn signature_reg_fuse_version(
>  
>  /// Offsets of a falcon's RISC-V interrupt routing registers.
>  #[derive(Clone, Copy, Debug, Eq, PartialEq)]
> -#[expect(dead_code)]
>  pub(crate) enum RiscvRouting {
>      /// The Turing offsets. GA100 uses them too.
>      Tu102,
> @@ -97,7 +96,6 @@ impl RiscvRouting {
>      ///
>      /// The causes routed to the core belong to the firmware running on it, and the host does not
>      /// service them.
> -    #[expect(dead_code)]
>      pub(crate) fn host_routed_causes(
>          self,
>          pfalcon2: Mmio<'_, PFalcon2Registers>,
> @@ -122,7 +120,6 @@ pub(crate) fn host_routed_causes(
>  ///
>  /// Separate from [`FalconHal`] because the GSP event handler calls these from hard interrupt
>  /// context, where it cannot make the heap allocation that a `FalconHal` takes.
> -#[expect(dead_code)]
>  pub(crate) trait FalconIntrHal {
>      /// Returns whether these falcons implement `NV_PFALCON_FALCON_INTR_RETRIGGER`.
>      fn has_intr_retrigger(&self) -> bool;
> @@ -135,7 +132,6 @@ pub(crate) trait FalconIntrHal {
>  ///
>  /// GA100 has its own arm: it has the retrigger register, which Turing lacks, and the Turing
>  /// routing offsets, which GA102 moved.
> -#[expect(dead_code)]
>  pub(crate) fn falcon_intr_hal(chipset: Chipset) -> &'static dyn FalconIntrHal {
>      match chipset.arch() {
>          Architecture::Turing => tu102::TU102_INTR_HAL,
> diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
> index 3d796d6c7013..d1e0da7b8682 100644
> --- a/drivers/gpu/nova-core/gpu.rs
> +++ b/drivers/gpu/nova-core/gpu.rs
> @@ -38,6 +38,11 @@
>          Gsp,
>          GspBootContext, //
>      },
> +    irq::{
> +        self,
> +        gsp::GspIrq,
> +        SubtreeVectors, //
> +    },
>      mm::{
>          bar_user::BarUser,
>          pagetable::MmuVersion,
> @@ -301,6 +306,13 @@ struct GspResources<'gpu> {
>  #[pin_data]
>  pub(crate) struct Gpu<'gpu> {
>      spec: Spec,
> +    /// GSP event interrupt registration.
> +    ///
> +    /// Must be kept declared *before* `gsp_resources`, so that the handler is unregistered, and
> +    /// any in-flight run of it has finished, before the command queue it drains is freed and
> +    /// before the GSP is unloaded.
> +    #[pin]
> +    _gsp_irq: GspIrq<'gpu>,

Eventually this should belong to the `Gsp` instance. It looks weird that
the IRQ aspect is the only one handled separately.

(this is not an ask for fix, more of a self-note for later)

>      /// Static GPU information as provided by the GSP.
>      gsp_static_info: GetGspStaticInfoReply,
>      /// GPU memory manager owning memory management resources.
> @@ -319,6 +331,14 @@ pub(crate) struct Gpu<'gpu> {
>      /// Must be kept declared *after* `gsp_resources`, as the latter's `PinnedDrop` implementation
>      /// requires the sysmem flush page to be in place.
>      sysmem_flush: SysmemFlush<'gpu>,
> +    /// Borrow of `vectors` that `_gsp_irq` holds. A field that borrows a sibling field is
> +    /// self-referential, which `pin_init` cannot express, so the borrow is taken by hand.
> +    vectors_ref: &'gpu SubtreeVectors<'gpu>,
> +    /// PCI interrupt vector allocation.
> +    ///
> +    /// Must be kept declared *after* `_gsp_irq`, which holds a borrow of it.
> +    #[pin]
> +    vectors: SubtreeVectors<'gpu>,
>  }
>  
>  #[pinned_drop]
> @@ -358,6 +378,12 @@ pub(crate) fn new<'a>(
>          let dev = pdev.as_ref();
>  
>          try_pin_init!(Self {
> +            vectors: irq::alloc_vectors(pdev, irq::gsp::GSP_SUBTREE.into())?,
> +
> +            // SAFETY: `vectors` is initialized above, is pinned at a stable address, and is
> +            // dropped after every field that uses `vectors_ref` (struct field drop order).
> +            vectors_ref: unsafe { &*core::ptr::from_ref(vectors.as_ref().get_ref()) },
> +
>              spec: Spec::new(dev, bar).inspect(|spec| {
>                  dev_info!(dev,"NVIDIA ({})\n", spec);
>              })?,
> @@ -380,12 +406,7 @@ pub(crate) fn new<'a>(
>  
>                  bar,
>  
> -                gsp_falcon: Falcon::new(
> -                    dev,
> -                    spec.chipset,
> -                    bar
> -                )
> -                .inspect(|falcon| falcon.clear_swgen0_intr())?,
> +                gsp_falcon: Falcon::new(dev, spec.chipset, bar)?,
>  
>                  sec2_falcon: Falcon::new(dev, spec.chipset, bar)?,
>  
> @@ -409,6 +430,30 @@ pub(crate) fn new<'a>(
>                  })?,
>              }),
>  
> +            _: {
> +                irq::gsp::quiesce(bar, gsp_resources.spec.chipset, vectors_ref)?;
> +            },
> +
> +            // SAFETY: the command queue is a field of `gsp_resources`, which is initialized
> +            // above and pinned, so the reference outlives the registration. The registration is
> +            // a field of `Gpu` and is never leaked, so its `Drop` runs, and field drop order
> +            // runs it before the queue is freed.
> +            _gsp_irq <- unsafe {
> +                GspIrq::new(
> +                    pdev,
> +                    vectors_ref,
> +                    bar,
> +                    &*core::ptr::from_ref(&gsp_resources.gsp.cmdq),
> +                    gsp_resources.spec.chipset,
> +                )
> +            },
> +
> +            // No interrupt announces the messages that the GSP posted during boot, before the
> +            // SWGEN0 latch was cleared.
> +            _: {
> +                gsp_resources.gsp.cmdq.drain()?;
> +            },
> +
>              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/gsp.rs b/drivers/gpu/nova-core/gsp.rs
> index 25ea43f1cbe9..fcfb4210d435 100644
> --- a/drivers/gpu/nova-core/gsp.rs
> +++ b/drivers/gpu/nova-core/gsp.rs
> @@ -152,7 +152,7 @@ pub(crate) struct Gsp<'gsp> {
>      /// Log buffers, optionally exposed via debugfs.
>      #[pin]
>      logs: debugfs::Scope<LogBuffers<'gsp>>,
> -    /// Command queue.
> +    /// Command queue, borrowed by the GSP event interrupt handler.

The borrow comment shouldn't matter here. 

>      #[pin]
>      pub(crate) cmdq: Cmdq<'gsp>,
>      /// RM arguments.
> diff --git a/drivers/gpu/nova-core/gsp/cmdq.rs b/drivers/gpu/nova-core/gsp/cmdq.rs
> index acee444e898d..f1231569aa33 100644
> --- a/drivers/gpu/nova-core/gsp/cmdq.rs
> +++ b/drivers/gpu/nova-core/gsp/cmdq.rs
> @@ -643,7 +643,6 @@ pub(crate) fn await_msg<M: MessageFromGsp>(&self) -> Result<M>
>      /// # Errors
>      ///
>      /// `EIO` if the queue is poisoned, or if a message fails framing or checksum validation.
> -    #[expect(dead_code)]

The patch adding the drain method is short and looks lonely as-is, let's
fold it into this one and remove this temporary dead code.

>      pub(crate) fn drain(&self) -> Result {
>          self.inner.lock().drain()
>      }
> diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
> index 7fb7d9f2e237..cafcc613770a 100644
> --- a/drivers/gpu/nova-core/irq.rs
> +++ b/drivers/gpu/nova-core/irq.rs
> @@ -11,6 +11,7 @@
>  
>  #[cfg(CONFIG_NOVA_CORE_SELFTESTS)]
>  pub(crate) mod doorbell_test;
> +pub(crate) mod gsp;
>  mod hal;
>  mod interrupt_tree;
>  mod regs;
> @@ -25,11 +26,16 @@
>      prelude::*, //
>  };
>  
> -use crate::num;
> +use crate::{
> +    driver::Bar0,
> +    gpu::Chipset,
> +    num, //
> +};
>  
>  use interrupt_tree::{
>      Subtree,
> -    SubtreeSet, //
> +    SubtreeSet,
> +    Tree, //
>  };
>  
>  /// The message-signaled interrupt type that Linux granted.
> @@ -56,6 +62,39 @@ pub(crate) struct SubtreeVectors<'a> {
>  }
>  
>  impl SubtreeVectors<'_> {
> +    /// Returns the tree of `chipset`, covering the serviced subtrees.
> +    ///
> +    /// # Errors
> +    ///
> +    /// `EINVAL` if `chipset` does not implement every serviced subtree.
> +    fn tree<'b>(&self, bar: Bar0<'b>, chipset: Chipset) -> Result<Tree<'b>> {
> +        Tree::new(bar, chipset, self)
> +    }

We are 4 revisions in, and I still don't understand the relationship
between `SubTreeVectors` and `Tree` clearly. And this method hints very
strongly to me that they should be merged into a single type.

And also this: `SubtreeVectors` already store HAL-dependent information
with the `MsiType` and the number of IRQ registrations, yet when we
invoke `tree` we need to pass it a chipset again. What happens if
`chipset` is not the same as the one with which the `SubtreeVectors` was
created?

I'm not asking for a redesign now because I think it would mess with
ownership of the interrupt handler and cascade into requiring more
changes, but adding a note to myself to address that in a follow-up
patch.

What should be done in v5 though: the doorbell test basically rewrites
this method at the beginning of `run_selftest`. Let's introduce it in
patch 6 so it can be used in the doorbell test as well, and then
`Tree::new` can be made `pub(super)`.

> +
> +    /// Disables every vector in the tree, clears every pending bit, and rearms PCI interrupt
> +    /// delivery.
> +    ///
> +    /// On return, the serviced subtrees are enabled at `TOP` under a `TOP` rearm method and
> +    /// disabled under the configuration-space one. A caller that needs delivery enables them
> +    /// itself.
> +    ///
> +    /// Call this only during probe, with no interrupt handler registered.
> +    ///
> +    /// # Errors
> +    ///
> +    /// `EINVAL` if `chipset` does not implement every serviced subtree.
> +    pub(crate) fn reset_tree(&self, bar: Bar0<'_>, chipset: Chipset) -> Result {
> +        let tree = self.tree(bar, chipset)?;
> +
> +        tree.disable_all_leaves();
> +        tree.drain();
> +        for subtree in self.serviced.iter() {
> +            tree.rearm_pci_irq(subtree);
> +        }
> +
> +        Ok(())
> +    }

So this calls `tree` and then just calls a bunch of methods on it
(except `serviced`, but `serviced` is copied into `Tree` and thus
available from it). IOW, this should just be a `reset` method of `Tree`.

  reply	other threads:[~2026-09-21  6:47 UTC|newest]

Thread overview: 28+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-12  4:43 [PATCH v4 00/17] nova-core: GPU interrupt support and GSP event delivery John Hubbard
2026-09-12  4:43 ` [PATCH v4 01/17] rust: pci: declare IrqType and IrqTypes with impl_flags John Hubbard
2026-09-12  4:43 ` [PATCH v4 02/17] rust: sync: completion: add wait_for_completion_timeout() John Hubbard
2026-09-12  4:43 ` [PATCH v4 03/17] gpu: nova-core: add the GIN vector, leaf and subtree types John Hubbard
2026-09-21  6:34   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 04/17] gpu: nova-core: add the GIN CPU interrupt tree and MSI EOI registers John Hubbard
2026-09-12  4:43 ` [PATCH v4 05/17] gpu: nova-core: add the per-architecture GIN CPU interrupt HAL John Hubbard
2026-09-12  4:43 ` [PATCH v4 06/17] gpu: nova-core: add the GIN interrupt tree and allocate its vectors John Hubbard
2026-09-21  6:35   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 07/17] gpu: nova-core: wait for GFW boot in probe, not in the Gpu constructor John Hubbard
2026-09-21  6:35   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 08/17] gpu: nova-core: add an interrupt delivery self-test John Hubbard
2026-09-21  6:35   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 09/17] gpu: nova-core: log GSP events instead of discarding them John Hubbard
2026-09-12  4:43 ` [PATCH v4 10/17] gpu: nova-core: stop re-parsing a bad GSP message John Hubbard
2026-09-21  6:32   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 11/17] gpu: nova-core: return ENOMSG for an unmatched " John Hubbard
2026-09-12  4:43 ` [PATCH v4 12/17] gpu: nova-core: bound a GSP wait by a single deadline John Hubbard
2026-09-12  4:43 ` [PATCH v4 13/17] gpu: nova-core: add a GSP message queue drain John Hubbard
2026-09-12  4:43 ` [PATCH v4 14/17] gpu: nova-core: add the falcon interrupt registers and their HAL John Hubbard
2026-09-21  6:37   ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 15/17] gpu: nova-core: service GSP events from the SWGEN0 interrupt John Hubbard
2026-09-21  6:46   ` Alexandre Courbot [this message]
2026-09-21  7:20     ` Alexandre Courbot
2026-09-12  4:43 ` [PATCH v4 16/17] gpu: nova-core: add KUnit tests for the interrupt tree and HALs John Hubbard
2026-09-21  6:47   ` Alexandre Courbot
2026-09-12  4:44 ` [PATCH v4 17/17] gpu: nova-core: document the GIN interrupt controller and GSP events John Hubbard
2026-09-21  6:59 ` [PATCH v4 00/17] nova-core: GPU interrupt support and GSP event delivery Alexandre Courbot

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=DLKSJOFH2EZ1.21JRCSY7IY1ZP@nvidia.com \
    --to=acourbot@nvidia.com \
    --cc=a.hindborg@kernel.org \
    --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=jhubbard@nvidia.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=nova-gpu@lists.linux.dev \
    --cc=ojeda@kernel.org \
    --cc=simona@ffwll.ch \
    --cc=tmgross@umich.edu \
    --cc=ttabi@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

all inboxes | Powered by JetHome®