mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
gpu: nova-core: add KUnit tests for the interrupt tree
The vector arithmetic and the leaf and subtree sets touch no hardware, so KUnit can cover them without a GPU. A wrong leaf, bit or subtree for a vector would otherwise show up only as a lost interrupt on hardware. Add the nova_core_gin_tree suite. It checks that a leaf index stops at the widest supported tree, that a leaf count implies the right number of subtrees and vectors, that a leaf count enumerates every leaf in order, that a vector maps to the right leaf, bit and subtree, and that a vector beyond an 8-leaf tree is rejected there and accepted in a 16-leaf tree. It also exercises the subtree set operations and checks that every supported chipset implements the subtree that carries the GSP event. Assisted-by: LLM Signed-off-by: John Hubbard <jhubbard@nvidia.com> Acked-by: Danilo Krummrich <dakr@kernel.org> Link: https://patch.msgid.link/20260930034148.590687-15-jhubbard@nvidia.com Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
This commit is contained in:
parent
476abf9f66
commit
306279ce97
|
|
@ -129,7 +129,10 @@ pub(super) const fn from_raw(raw: u32) -> Self {
|
|||
Self(raw)
|
||||
}
|
||||
|
||||
#[cfg_attr(not(CONFIG_NOVA_CORE_SELFTESTS), expect(dead_code))]
|
||||
#[cfg_attr(
|
||||
not(any(CONFIG_NOVA_CORE_SELFTESTS, CONFIG_KUNIT = "y")),
|
||||
expect(dead_code)
|
||||
)]
|
||||
pub(super) const fn into_raw(self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
|
@ -554,3 +557,126 @@ 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::<u32, 32>::from(LeafCount::Eight.subtree_set()).get(),
|
||||
0x0f
|
||||
);
|
||||
assert_eq!(LeafCount::Eight.vector_count(), 256);
|
||||
|
||||
assert_eq!(LeafCount::Sixteen.subtree_count(), 8);
|
||||
assert_eq!(
|
||||
Bounded::<u32, 32>::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::<u32, 32>::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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user