All of lore.kernel.org
 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>,
	"Joel Fernandes" <joelagnelf@nvidia.com>,
	"Will Pierce" <wpierce@nvidia.com>
Subject: Re: [PATCH v3 06/14] gpu: nova-core: add the GIN interrupt tree and allocate its vectors
Date: Sat, 05 Sep 2026 22:55:28 +0900	[thread overview]
Message-ID: <DL7FN5S6RSRI.JDTB3MREKFVA@nvidia.com> (raw)
In-Reply-To: <20260903031514.1515905-7-jhubbard@nvidia.com>

On Thu Sep 3, 2026 at 12:15 PM JST, John Hubbard wrote:
<...>
> diff --git a/drivers/gpu/nova-core/irq.rs b/drivers/gpu/nova-core/irq.rs
> index d21dee1b89a0..f6ba883d72c5 100644
> --- a/drivers/gpu/nova-core/irq.rs
> +++ b/drivers/gpu/nova-core/irq.rs
> @@ -12,6 +12,23 @@
>  mod interrupt_tree;
>  mod regs;
>  
> +use kernel::{
> +    device::Bound,
> +    irq,
> +    pci::{
> +        self,
> +        IrqType, //
> +    },
> +    prelude::*, //
> +};
> +
> +use crate::num;
> +
> +use interrupt_tree::{
> +    Subtree,
> +    SubtreeSet, //
> +};
> +
>  /// The message-signaled interrupt type a vector allocation obtained.
>  ///
>  /// nova-core allocates MSI-X or MSI and nothing else, so the level-triggered INTx that
> @@ -24,3 +41,81 @@ pub(crate) enum MsiType {
>      /// One table entry per subtree.
>      MsiX,
>  }
> +
> +/// 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.
> +pub(crate) struct SubtreeVectors<'a> {
> +    vectors: pci::IrqVectorRegistration<'a>,
> +    /// Every subtree nova-core services.
> +    serviced: SubtreeSet,
> +    /// The type [`alloc_vectors`] obtained, which fixes both the entry each subtree raises and the
> +    /// rearm write its handler owes.
> +    msi_type: MsiType,
> +}
> +
> +impl SubtreeVectors<'_> {
> +    /// Returns the interrupt type these vectors were allocated as.
> +    pub(crate) fn msi_type(&self) -> MsiType {
> +        self.msi_type
> +    }

This method is not needed. It is only used by sub-modules, which can
access the private `msi_type` directly.

> +
> +    /// Returns an [`irq::IrqRequest`] for the vector that delivers `subtree`.
> +    ///
> +    /// 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.
> +    ///
> +    /// # Errors
> +    ///
> +    /// `EINVAL` if `subtree` is not one nova-core services.
> +    pub(crate) fn request_for(&self, subtree: Subtree) -> Result<irq::IrqRequest<'_>> {

This method can be private.

> +        if !self.serviced.contains(subtree) {
> +            return Err(EINVAL);
> +        }
> +
> +        let entry = match self.msi_type {
> +            MsiType::MsiX => num::u32_as_usize(subtree.index()),
> +            MsiType::Msi => 0,
> +        };
> +
> +        self.vectors.index(entry).map(Into::into)
> +    }
> +}
> +
> +/// 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.
> +///
> +/// # 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: SubtreeSet,
> +) -> Result<SubtreeVectors<'_>> {
> +    if serviced.is_empty() {
> +        return Err(EINVAL);
> +    }
> +
> +    // One entry per subtree up to and including the highest serviced one.
> +    let entries = serviced.span();
> +
> +    let (vectors, msi_type) = pdev
> +        .alloc_irq_vectors(entries, entries, IrqType::MsiX.into())
> +        .map(|vectors| (vectors, MsiType::MsiX))
> +        .or_else(|_| {
> +            pdev.alloc_irq_vectors(1, 1, IrqType::Msi.into())
> +                .map(|vectors| (vectors, MsiType::Msi))
> +        })?;
> +
> +    Ok(SubtreeVectors {
> +        vectors,
> +        serviced,
> +        msi_type,
> +    })
> +}
> diff --git a/drivers/gpu/nova-core/irq/hal.rs b/drivers/gpu/nova-core/irq/hal.rs
> index 1ea677e37e56..07604458dbbb 100644
> --- a/drivers/gpu/nova-core/irq/hal.rs
> +++ b/drivers/gpu/nova-core/irq/hal.rs
> @@ -25,7 +25,7 @@
>          Subtree,
>          SubtreeSet, //
>      },
> -    regs,
> +    regs::*,
>      MsiType, //
>  };
>  
> @@ -63,7 +63,7 @@ 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());
> +                bar.write(NV_XVE_CYA_2, 0u32.into());
>                  return;
>              }
>              Self::TopEnableCycleServiced => serviced,
> @@ -71,10 +71,10 @@ pub(super) fn rearm(self, bar: Bar0<'_>, serviced: SubtreeSet, subtree: Subtree)
>          };
>  
>          bar.write_reg(
> -            regs::NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR::zeroed().with_subtrees(subtrees),
> +            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),
> +            NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_SET::zeroed().with_subtrees(subtrees),

There's a bit of unneeded churn here. Let's settle on the import style
in patch 5.

>          );
>      }
>  }
> diff --git a/drivers/gpu/nova-core/irq/interrupt_tree.rs b/drivers/gpu/nova-core/irq/interrupt_tree.rs
> index 5aa447cf0ec4..0b4dc2fc8ea8 100644
> --- a/drivers/gpu/nova-core/irq/interrupt_tree.rs
> +++ b/drivers/gpu/nova-core/irq/interrupt_tree.rs
> @@ -1,18 +1,42 @@
>  // SPDX-License-Identifier: GPL-2.0
>  // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
>  
> -//! Vector addressing in the GIN CPU interrupt tree.
> +//! The GIN CPU interrupt tree for one PCIe function.
>  //!
>  //! A vector's number fixes where it latches: leaf `vector / 32` at bit `vector % 32`, and that
>  //! leaf belongs to subtree `vector / 64`. The types here keep those three views apart, so a leaf
>  //! index, a set of vectors within one leaf, and a `TOP` bit cannot stand in for one another.
> +//!
> +//! Servicing a leaf has a required order: read its pending bits, then clear them. Clearing a leaf
> +//! before reading it discards every vector latched in it, and nothing reports the loss. Only
> +//! [`Tree::read_pending`] produces a [`LeafPending`], and only a [`LeafPending`] can clear, so the
> +//! wrong order does not compile.
> +//!
> +//! Serializing access to the tree is the caller's responsibility.
>  
>  use kernel::{
> +    io::{
> +        register::Array,
> +        Io, //
> +    },
>      num::Bounded,
>      prelude::*, //
>  };
>  
> -use crate::num;
> +use crate::{
> +    driver::Bar0,
> +    gpu::Chipset,
> +    num, //
> +};
> +
> +use super::{
> +    hal::{
> +        cpu_interrupt_hal,
> +        PciIrqRearmMethod, //
> +    },
> +    regs::*,
> +    MsiType, //
> +};
>  
>  /// Number of bits a leaf index occupies, covering the `0..16` leaf register arrays.
>  const LEAF_INDEX_BITS: u32 = 4;
> @@ -113,7 +137,7 @@ pub(super) const fn contains(self, other: Self) -> bool {
>  ///
>  /// Exactly one bit is set.
>  #[derive(Clone, Copy, Debug, Eq, PartialEq)]
> -pub(super) struct Subtree(u32);
> +pub(crate) struct Subtree(u32);
>  
>  impl Subtree {
>      /// Returns this subtree's index within the tree.
> @@ -131,7 +155,7 @@ pub(super) const fn into_raw(self) -> u32 {
>  
>  /// Set of subtrees, one bit per subtree, in the layout the `TOP` enable registers take.
>  #[derive(Clone, Copy, Debug, Eq, PartialEq)]
> -pub(super) struct SubtreeSet(u32);
> +pub(crate) struct SubtreeSet(u32);
>  
>  impl SubtreeSet {
>      /// Returns whether `subtree` belongs to this set.
> @@ -240,3 +264,262 @@ fn from(vector: GinVector) -> Self {
>          vector.0.extend()
>      }
>  }
> +
> +/// Clears the enables of the vectors set in `vectors` for `leaf` (`LEAF_EN_CLEAR`).
> +///
> +/// Shared by [`Tree::disable_leaf`] and by [`LeafEnableGuard`]'s [`Drop`], which has no tree to
> +/// reach through.
> +fn clear_leaf_enables(bar: Bar0<'_>, leaf: LeafIndex, vectors: LeafMask) {
> +    bar.write(
> +        NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_EN_CLEAR::at(*leaf),
> +        NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_EN_CLEAR::zeroed().with_vectors(vectors),
> +    );

Mmm, that's not the syntax I gave in my review of v2 [1].

You don't need to repeat the register name:

    bar.write(
        Array::at(*leaf),
        NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_EN_CLEAR::zeroed().with_vectors(vectors),
    );

Please make sure all sites where this applies are fixed.

[1] https://lore.kernel.org/nova-gpu/DL3SD82Q6C81.3G32WDNS642Y3@nvidia.com/

> +}
> +
> +/// Clears the `TOP` enables of every subtree in `serviced` (`TOP_EN_CLEAR`).
> +fn clear_top_enables(bar: Bar0<'_>, serviced: SubtreeSet) {
> +    bar.write_reg(NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR::zeroed().with_subtrees(serviced));
> +}
> +
> +/// Clears the pending vectors set in `vectors` for `leaf` (write-1-to-clear).
> +fn clear_leaf_pending(bar: Bar0<'_>, leaf: LeafIndex, vectors: LeafMask) {
> +    if !vectors.is_empty() {
> +        bar.write(
> +            NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF::at(*leaf),
> +            NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF::zeroed().with_vectors(vectors),
> +        );
> +    }
> +}

This method is only ever used in `LeafPending::clear_vectors`, so let's
inline it there.

> +
> +/// Returns every leaf a tree of `leaves` leaves implements.
> +fn implemented_leaves(leaves: LeafCount) -> impl Iterator<Item = LeafIndex> {
> +    (0..leaves.into_raw()).filter_map(LeafIndex::try_new)
> +}

This looks like it should be a method of `LeafCount`. In this case, I
guess the name can be simply `iter`.

<...>
> +    /// Clears every pending bit in every implemented leaf.
> +    ///
> +    /// Disables this tree's serviced subtrees at `TOP` for the walk and leaves them disabled, so a
> +    /// caller that wants delivery enables them itself once it is ready to receive. The leaves
> +    /// cleared reach subtrees the driver does not service, and the `TOP_EN` write does not.
> +    ///
> +    /// Call `drain()` only during probe. It must not run concurrently with an interrupt handler.
> +    pub(super) fn drain(&self) {
> +        self.disable_top();
> +
> +        // `TOP` summarizes enabled leaf bits, so a vector that latched while it was disabled does
> +        // not appear there.
> +        for leaf in implemented_leaves(self.leaves) {
> +            let pending = self.read_pending(leaf);
> +            if !pending.vectors().is_empty() {

`clear` already does the same check (through the now-inlined
`clear_leaf_pending`), so it is redundant here.

  reply	other threads:[~2026-09-05 13:55 UTC|newest]

Thread overview: 35+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-03  3:14 [PATCH v3 00/14] nova-core: GPU interrupt support and GSP event delivery John Hubbard
2026-09-03  3:15 ` [PATCH v3 01/14] rust: pci: declare IrqType and IrqTypes with impl_flags John Hubbard
2026-09-03  3:15 ` [PATCH v3 02/14] rust: sync: completion: add wait_for_completion_timeout() John Hubbard
2026-09-03  3:15 ` [PATCH v3 03/14] gpu: nova-core: add the GIN vector and subtree newtypes John Hubbard
2026-09-05  1:39   ` Alexandre Courbot
2026-09-03  3:15 ` [PATCH v3 04/14] gpu: nova-core: add the GIN CPU interrupt tree and MSI EOI registers John Hubbard
2026-09-03  3:15 ` [PATCH v3 05/14] gpu: nova-core: add the per-architecture GIN CPU interrupt HAL John Hubbard
2026-09-05  6:11   ` Alexandre Courbot
2026-09-03  3:15 ` [PATCH v3 06/14] gpu: nova-core: add the GIN interrupt tree and allocate its vectors John Hubbard
2026-09-05 13:55   ` Alexandre Courbot [this message]
2026-09-06 23:10     ` John Hubbard
2026-09-07  0:24       ` Alexandre Courbot
2026-09-03  3:15 ` [PATCH v3 07/14] gpu: nova-core: add an interrupt delivery self-test John Hubbard
2026-09-03  3:29   ` sashiko-bot
2026-09-03  3:57     ` John Hubbard
2026-09-07  5:26   ` Alexandre Courbot
2026-09-03  3:15 ` [PATCH v3 08/14] gpu: nova-core: log GSP events instead of discarding them John Hubbard
2026-09-03  3:15 ` [PATCH v3 09/14] gpu: nova-core: recover the GSP receive path from corrupt framing John Hubbard
2026-09-04 10:53   ` Alexandre Courbot
2026-09-04 11:17     ` Gary Guo
2026-09-04 13:45       ` Alexandre Courbot
2026-09-03  3:15 ` [PATCH v3 10/14] gpu: nova-core: bound a GSP wait by a single deadline John Hubbard
2026-09-04 11:13   ` Alexandre Courbot
2026-09-04 11:26     ` Gary Guo
2026-09-04 13:32       ` Alexandre Courbot
2026-09-04 13:41         ` Gary Guo
2026-09-03  3:15 ` [PATCH v3 11/14] gpu: nova-core: add the falcon interrupt status and routing registers John Hubbard
2026-09-03  3:15 ` [PATCH v3 12/14] gpu: nova-core: drive GSP events with the SWGEN0 interrupt John Hubbard
2026-09-03  3:28   ` sashiko-bot
2026-09-03  3:55     ` John Hubbard
2026-09-04  1:53       ` John Hubbard
2026-09-07  6:59   ` Alexandre Courbot
2026-09-07 18:17     ` John Hubbard
2026-09-03  3:15 ` [PATCH v3 13/14] gpu: nova-core: add KUnit tests for the interrupt tree and HALs John Hubbard
2026-09-03  3:15 ` [PATCH v3 14/14] 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=DL7FN5S6RSRI.JDTB3MREKFVA@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=joelagnelf@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=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.