// SPDX-License-Identifier: GPL-2.0 // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. //! The GIN CPU interrupt tree for one PCIe function. //! //! A [`GinVector`] names an interrupt source, a [`LeafIndex`] the leaf register that latches it, //! a [`LeafMask`] a set of vectors within one leaf, and a [`Subtree`] one `TOP` bit. The types //! keep the four from being confused with one another. //! //! Servicing a leaf requires reading its pending bits before clearing them. Only //! [`Tree::read_pending`] produces a [`LeafPending`], and only a [`LeafPending`] clears a leaf, //! so that the wrong order does not compile. This module does not serialize access to the tree. //! //! See `Documentation/gpu/nova/core/interrupts.rst`. use kernel::{ device::Bound, io::{ register::Array, Io, // }, irq, num::Bounded, pci::{ self, IrqType, // }, prelude::*, // }; use crate::{ driver::Bar0, gpu::Chipset, num, // }; use super::{ hal::{ cpu_interrupt_hal, CpuInterruptHal, // }, regs::*, MsiType, // }; /// Number of vectors one leaf register carries, one per bit. const VECTORS_PER_LEAF: u32 = u32::BITS; /// Number of leaves one subtree covers. const LEAVES_PER_SUBTREE: u32 = 2; /// Number of subtrees the widest supported tree implements. const MAX_NUM_SUBTREES: u32 = 8; /// Number of leaves the widest supported tree implements. const MAX_NUM_LEAVES: u32 = MAX_NUM_SUBTREES * LEAVES_PER_SUBTREE; /// Number of bits needed to address every vector in the widest supported tree. const VECTOR_BITS: u32 = (MAX_NUM_LEAVES * VECTORS_PER_LEAF).ilog2(); /// Width of the vector field in the leaf trigger register. const TRIGGER_VECTOR_BITS: u32 = { let range = NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_TRIGGER::VECTOR_RANGE; num::u8_as_u32(*range.end() - *range.start() + 1) }; /// Index of a leaf register within the widest supported tree. An 8-leaf tree implements only the /// lower half of the range. pub(super) type LeafIndex = Bounded; /// Number of leaves a tree implements. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[repr(u32)] pub(super) enum LeafCount { /// Turing through Ada. Eight = 8, /// Hopper and later. Sixteen = 16, } impl LeafCount { pub(super) const fn into_u32(self) -> u32 { // CAST: `LeafCount` is `repr(u32)`, so the cast is lossless. self as u32 } pub(super) const fn into_raw(self) -> usize { num::u32_as_usize(self.into_u32()) } /// Returns the number of subtrees a tree of this size implements. pub(super) const fn subtree_count(self) -> u32 { self.into_u32() / LEAVES_PER_SUBTREE } /// Returns the set of every subtree a tree of this size implements. pub(super) const fn subtree_set(self) -> SubtreeSet { SubtreeSet((1u32 << self.subtree_count()) - 1) } /// Returns the number of vectors a tree of this size carries. pub(super) const fn vector_count(self) -> u32 { self.into_u32() * VECTORS_PER_LEAF } /// Returns every leaf a tree of this size implements. pub(super) fn iter(self) -> impl Iterator { (0..self.into_raw()).filter_map(LeafIndex::try_new) } } // `VECTOR_BITS` and `LeafCount::Sixteen` are written separately. This assert keeps them in // agreement about the widest supported tree. static_assert!(1 << VECTOR_BITS == LeafCount::Sixteen.vector_count()); /// Set of vectors within one leaf, one bit per vector. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct LeafMask(u32); impl LeafMask { /// Returns the mask with every vector set. pub(super) const fn all() -> Self { Self(u32::MAX) } #[cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))] pub(super) const fn from_raw(raw: u32) -> Self { Self(raw) } #[cfg_attr( not(any(CONFIG_NOVA_CORE_SELFTESTS, CONFIG_KUNIT = "y")), expect(dead_code) )] pub(super) const fn into_raw(self) -> u32 { self.0 } pub(super) const fn is_empty(self) -> bool { self.0 == 0 } /// Returns whether every vector in `other` is also in this mask. pub(super) const fn contains(self, other: Self) -> bool { self.0 & other.0 == other.0 } } impl From> for LeafMask { fn from(vectors: Bounded) -> Self { Self(vectors.get()) } } impl From for Bounded { fn from(vectors: LeafMask) -> Self { vectors.0.into() } } /// One subtree, held as the `TOP` bit that covers it. /// /// # Invariants /// /// Exactly one bit is set. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) struct Subtree(u32); impl Subtree { /// Returns the subtree at index `idx`. const fn new(idx: u32) -> Self { // INVARIANT: shifting `1` left leaves exactly one bit set. Self(1 << idx) } /// Returns this subtree's index within the tree. pub(super) const fn index(self) -> u32 { self.0.trailing_zeros() } pub(super) const fn into_raw(self) -> u32 { self.0 } } /// Set of subtrees, one bit per subtree, in the layout of the `TOP` registers. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(crate) struct SubtreeSet(u32); impl SubtreeSet { pub(super) const fn contains(self, subtree: Subtree) -> bool { self.0 & subtree.into_raw() != 0 } pub(super) const fn is_empty(self) -> bool { self.0 == 0 } pub(super) const fn intersection(self, other: Self) -> Self { Self(self.0 & other.0) } /// Returns one more than the highest index in this set, or `0` for an empty set. An MSI-X /// allocation that covers the set needs this many entries. pub(super) const fn span(self) -> u32 { u32::BITS - self.0.leading_zeros() } /// Returns the subtrees of this set, lowest index first. pub(super) fn iter(self) -> impl Iterator { (0..u32::BITS) .map(Subtree::new) .filter(move |subtree| self.contains(*subtree)) } } impl From for SubtreeSet { fn from(subtree: Subtree) -> Self { Self(subtree.into_raw()) } } impl From> for SubtreeSet { fn from(subtrees: Bounded) -> Self { Self(subtrees.get()) } } impl From for Bounded { fn from(subtrees: SubtreeSet) -> Self { subtrees.0.into() } } /// A GIN interrupt vector, bounded to the widest tree that any supported chipset implements. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub(super) struct GinVector(Bounded); impl GinVector { /// Returns vector number `VECTOR`. /// /// Fails to compile if `VECTOR` is beyond the widest supported tree. pub(super) const fn new() -> Self { Self(Bounded::::new::()) } #[cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))] pub(super) const fn into_raw(self) -> u32 { self.0.get() } /// Returns this vector's leaf. pub(super) fn leaf_index(self) -> LeafIndex { // CALC: `self.0 / VECTORS_PER_LEAF`. self.0.shr::<{ VECTORS_PER_LEAF.ilog2() }, _>().cast() } /// Returns this vector's bit within its leaf. pub(super) const fn leaf_mask(self) -> LeafMask { LeafMask(1 << (self.0.get() % VECTORS_PER_LEAF)) } /// Returns this vector's subtree. pub(super) const fn subtree(self) -> Subtree { Subtree::new(self.0.get() / (VECTORS_PER_LEAF * LEAVES_PER_SUBTREE)) } /// Checks that a tree with `leaves` leaves implements this vector. /// /// # Errors /// /// `EINVAL` if it does not. pub(super) const fn validate(self, leaves: LeafCount) -> Result { if self.0.get() >= leaves.vector_count() { Err(EINVAL) } else { Ok(()) } } } impl From for Bounded { fn from(vector: GinVector) -> Self { vector.0.extend() } } /// Disables `vectors` in `leaf`. fn clear_leaf_enables(bar: Bar0<'_>, leaf: LeafIndex, vectors: LeafMask) { bar.write( Array::at(*leaf), NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_EN_CLEAR::zeroed().with_vectors(vectors), ); } /// Disables the subtrees in `serviced` at `TOP`. fn clear_top_enables(bar: Bar0<'_>, serviced: SubtreeSet) { bar.write_reg(NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_CLEAR::zeroed().with_subtrees(serviced)); } /// The CPU tree of one PCIe function, the subtrees that nova-core services, and the PCI vectors /// that deliver them. /// /// A subtree may be enabled at `TOP` only once a handler is registered on the PCI vector that /// delivers it. See "The serviced-subtree invariant" in /// `Documentation/gpu/nova/core/interrupts.rst`. pub(crate) struct Tree<'a> { bar: Bar0<'a>, hal: &'static dyn CpuInterruptHal, serviced: SubtreeSet, /// The interrupt type that Linux granted for `vectors`. pub(super) msi_type: MsiType, vectors: pci::IrqVectorRegistration<'a>, } impl<'a> Tree<'a> { /// Creates the tree of `chipset` and allocates the PCI vectors for the subtrees in `serviced`. /// /// Requests MSI-X entries `0` through the highest subtree in `serviced`, since an allocation /// cannot be sparse, and falls back to a single MSI message for the whole tree. /// /// # Errors /// /// `EINVAL` if `serviced` is empty or names a subtree that `chipset` does not implement. /// Otherwise, when neither interrupt type could be allocated, the error from the MSI request. pub(crate) fn new( pdev: &'a pci::Device, bar: Bar0<'a>, chipset: Chipset, serviced: SubtreeSet, ) -> Result { let hal = cpu_interrupt_hal(chipset); let num_leaves = hal.leaf_count(); if serviced.is_empty() || serviced.intersection(num_leaves.subtree_set()) != serviced { return Err(EINVAL); } let entries = serviced.span(); let (vectors, msi_type) = pdev .alloc_irq_vectors(entries, entries, IrqType::MsiX.into()) .map(|vectors| (vectors, MsiType::MsiX)) // Every subtree raises the one MSI message, so one MSI vector serves the whole tree. // See "Delivery over PCI" in `Documentation/gpu/nova/core/interrupts.rst`. .or_else(|_| { pdev.alloc_irq_vectors(1, 1, IrqType::Msi.into()) .map(|vectors| (vectors, MsiType::Msi)) })?; Ok(Self { bar, hal, serviced, msi_type, vectors, }) } /// Returns the [`irq::IrqRequest`] for the PCI vector that delivers `subtree`. /// /// # Errors /// /// `EINVAL` if `subtree` is not one of the serviced subtrees. pub(super) fn request_for(&self, subtree: Subtree) -> Result> { 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) } /// Rearms PCI interrupt delivery to the CPU after servicing `subtree`, the one subtree that /// the calling handler serves. /// /// A handler must call this before returning, or it receives no further interrupts. pub(super) fn rearm_pci_irq(&self, subtree: Subtree) { self.hal .rearm_pci_irq(self.bar, self.msi_type, self.serviced, subtree); } /// Enables the serviced subtrees at `TOP`. /// /// Each of them must have a handler registered on its PCI vector. pub(super) fn enable_top(&self) { self.bar.write_reg( NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_TOP_EN_SET::zeroed().with_subtrees(self.serviced), ); } /// Disables the serviced subtrees at `TOP`. pub(super) fn disable_top(&self) { clear_top_enables(self.bar, self.serviced); } /// Enables the serviced subtrees at `TOP` until the returned guard drops. pub(crate) fn enable_top_guarded(&self) -> TopEnableGuard<'a> { self.enable_top(); TopEnableGuard { bar: self.bar, serviced: self.serviced, } } /// Enables `vectors` in `leaf`. pub(super) fn enable_leaf(&self, leaf: LeafIndex, vectors: LeafMask) { self.bar.write( Array::at(*leaf), NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_EN_SET::zeroed().with_vectors(vectors), ); } /// Disables `vectors` in `leaf`. pub(super) fn disable_leaf(&self, leaf: LeafIndex, vectors: LeafMask) { clear_leaf_enables(self.bar, leaf, vectors); } /// Enables `vectors` in `leaf` until the returned guard drops. pub(super) fn enable_leaf_guarded( &self, leaf: LeafIndex, vectors: LeafMask, ) -> LeafEnableGuard<'a> { self.enable_leaf(leaf, vectors); LeafEnableGuard { bar: self.bar, leaf, vectors, } } /// Reads the pending bits of `leaf`, and returns the handle that clears them. pub(super) fn read_pending(&self, leaf: LeafIndex) -> LeafPending<'a> { let pending = self .bar .read(NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF::at(*leaf)) .vectors(); LeafPending { bar: self.bar, leaf, pending, } } /// Sets the pending bit of `vector` in its leaf, as if the vector's source had raised it. /// /// # Errors /// /// `EINVAL` if this tree does not implement `vector`. #[cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))] pub(super) fn trigger(&self, vector: GinVector) -> Result { vector.validate(self.hal.leaf_count())?; self.bar.write_reg( NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF_TRIGGER::zeroed().with_vector(vector), ); Ok(()) } /// Disables every vector in every implemented leaf, including the subtrees that nova-core does /// not service. Call this only during probe. pub(super) fn disable_all_leaves(&self) { for leaf in self.hal.leaf_count().iter() { self.disable_leaf(leaf, LeafMask::all()); } } /// Clears every pending bit in every implemented leaf, including the subtrees that nova-core /// does not service. /// /// The serviced subtrees are disabled at `TOP` on return. Call this only during probe, with no /// interrupt handler registered. pub(super) fn drain(&self) { self.disable_top(); // A vector that latched while disabled does not show in `TOP`, so read every leaf rather // than descending from it. for leaf in self.hal.leaf_count().iter() { self.read_pending(leaf).clear(); } } /// Disables every vector, clears every pending bit, and rearms PCI interrupt delivery. /// /// The serviced subtrees are disabled at `TOP` on return. Call this only during probe, with /// no interrupt handler registered. pub(super) fn reset(&self) { self.disable_all_leaves(); self.drain(); for subtree in self.serviced.iter() { self.rearm_pci_irq(subtree); } // A `TOP`-enable rearm leaves the subtrees that it cycles enabled, and a subtree may be // enabled only once a handler is registered on its vector. self.disable_top(); } } /// The pending bits of one leaf as they were read, and the handle that clears them. pub(super) struct LeafPending<'a> { bar: Bar0<'a>, leaf: LeafIndex, pending: LeafMask, } impl LeafPending<'_> { pub(super) fn vectors(&self) -> LeafMask { self.pending } /// Clears the vectors that were pending at the read. A vector that latched since stays pending. pub(super) fn clear(&self) { self.clear_vectors(self.pending); } /// Clears `vectors` and no other bit. pub(super) fn clear_vectors(&self, vectors: LeafMask) { if !vectors.is_empty() { self.bar.write( Array::at(*self.leaf), NV_VIRTUAL_FUNCTION_PRIV_CPU_INTR_LEAF::zeroed().with_vectors(vectors), ); } } } /// Disables a set of vectors in one leaf when dropped. pub(super) struct LeafEnableGuard<'a> { bar: Bar0<'a>, leaf: LeafIndex, vectors: LeafMask, } impl Drop for LeafEnableGuard<'_> { fn drop(&mut self) { clear_leaf_enables(self.bar, self.leaf, self.vectors); } } /// Disables the serviced subtrees at `TOP` when dropped. pub(crate) struct TopEnableGuard<'a> { bar: Bar0<'a>, serviced: SubtreeSet, } impl Drop for TopEnableGuard<'_> { fn drop(&mut self) { clear_top_enables(self.bar, self.serviced); } } #[kunit_tests(nova_core_gin_tree)] mod tests { use super::*; /// A leaf index cannot name a leaf beyond the widest supported tree. #[test] fn leaf_index_bounds() { assert!(LeafIndex::try_new(0).is_some()); assert!(LeafIndex::try_new(15).is_some()); assert!(LeafIndex::try_new(16).is_none()); } /// The subtree count, the implemented-subtree set, and the vector count follow the leaf count. #[test] fn leaf_count_derives_subtrees_and_vectors() { assert_eq!(LeafCount::Eight.subtree_count(), 4); assert_eq!( Bounded::::from(LeafCount::Eight.subtree_set()).get(), 0x0f ); assert_eq!(LeafCount::Eight.vector_count(), 256); assert_eq!(LeafCount::Sixteen.subtree_count(), 8); assert_eq!( Bounded::::from(LeafCount::Sixteen.subtree_set()).get(), 0xff ); assert_eq!(LeafCount::Sixteen.vector_count(), 512); } /// A tree enumerates every leaf that it implements, in order, and no more. #[test] fn leaf_count_iter_covers_the_tree() { for (count, expected) in [(LeafCount::Eight, 8usize), (LeafCount::Sixteen, 16)] { let mut seen = 0; for (index, leaf) in count.iter().enumerate() { assert_eq!(leaf.get(), index); seen += 1; } assert_eq!(seen, expected); } } /// A vector maps to its leaf, its bit within that leaf, and its subtree. The doorbell (129) /// and the GSP event (155) share a subtree. #[test] fn vector_maps_to_leaf_bit_and_subtree() { let doorbell = GinVector::new::<129>(); let gsp = GinVector::new::<155>(); assert_eq!(doorbell.leaf_index().get(), 4); assert_eq!(doorbell.leaf_mask().into_raw(), 1 << 1); assert_eq!(doorbell.subtree().index(), 2); assert_eq!(gsp.leaf_index().get(), 4); assert_eq!(gsp.leaf_mask().into_raw(), 1 << 27); assert_eq!(gsp.subtree().index(), 2); assert_eq!(doorbell.subtree(), gsp.subtree()); } /// Both fixed vectors are within the 8-leaf tree, so every supported chipset implements them. #[test] fn fixed_vectors_fit_the_narrowest_tree() { assert!(GinVector::new::<129>().validate(LeafCount::Eight).is_ok()); assert!(GinVector::new::<155>().validate(LeafCount::Eight).is_ok()); // The first vector beyond an 8-leaf tree. assert!(GinVector::new::<256>().validate(LeafCount::Eight).is_err()); assert!(GinVector::new::<256>().validate(LeafCount::Sixteen).is_ok()); } /// A subtree set reports membership, intersection, and its span from subtree 0. #[test] fn subtree_set_operations() { let gsp = GinVector::new::<155>().subtree(); assert!(LeafCount::Eight.subtree_set().contains(gsp)); assert!(!LeafCount::Eight.subtree_set().is_empty()); // The GSP needs no subtree above 2, so an MSI-X request covers entries 0 through 2. assert_eq!(SubtreeSet::from(gsp).span(), 3); // A 16-leaf tree implements every subtree that an 8-leaf tree does. assert_eq!( LeafCount::Sixteen .subtree_set() .intersection(LeafCount::Eight.subtree_set()), LeafCount::Eight.subtree_set() ); } /// Iterating a subtree set yields each subtree once, lowest index first, and nothing for an /// empty set. #[test] fn subtree_set_iterates_its_members() { assert!(LeafCount::Eight .subtree_set() .iter() .map(Subtree::index) .eq([0u32, 1, 2, 3])); let gsp = SubtreeSet::from(GinVector::new::<155>().subtree()); assert!(gsp.iter().map(Subtree::index).eq([2u32])); let empty = SubtreeSet::from(Bounded::::new::<0>()); assert_eq!(empty.iter().count(), 0); } /// Every supported chipset implements the subtree that carries the GSP event. #[test] fn gsp_subtree_is_implemented_everywhere() { for &chipset in Chipset::ALL { assert!(cpu_interrupt_hal(chipset) .leaf_count() .subtree_set() .contains(crate::irq::gsp::GSP_SUBTREE)); } } }