Skip to content

fix(node-agent): serve the mock PCI tree at the kernel paths - #742

Open
giuliocalzo wants to merge 9 commits into
NVIDIA:mainfrom
giuliocalzo:feat/issue-673-agent-pci-sysfs
Open

fix(node-agent): serve the mock PCI tree at the kernel paths#742
giuliocalzo wants to merge 9 commits into
NVIDIA:mainfrom
giuliocalzo:feat/issue-673-agent-pci-sysfs

Conversation

@giuliocalzo

@giuliocalzo giuliocalzo commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

What This PR Does

Makes the simulated PCI tree and machine type visible to Go consumers, so a mock node labels itself the way a real one does.

  • Serves the rendered PCI tree at the kernel paths. The agent's nvidia.com/gpu CDI spec bind-mounts sys/devices and sys/bus/pci/devices read-only, as a pair. Fixes nvidia.com/gpu.mode=unknown.
  • Reproduces the node's DMI attributes inside the tree. Serving /sys/devices replaces what /sys/class/dmi/id resolves into, so without this a container reads ENOENT where the node has values — and under kind, whose node image bind-mounts its product files into every container, the pod fails to start outright.
  • Renders only the GPUs NVML reports. gpu.count caps the device list without touching pcie_topology, so a capped node used to render PCI entries that read as GPUs with no NVML device behind them.
  • Serves a machine-type file and points GFD at it via NRI. Fixes nvidia.com/gpu.machine=unknown.

Trade-off: /sys/devices is served whole, so a served container no longer sees the host's other device classes, CPU topology among them. Narrowing it needs mountpoints the runtime cannot create on a read-only sysfs; #689 tracks removing it.

Known gap: the CDI path still needs GFD_MACHINE_TYPE_FILE in the operator's values, because the runtime applies the spec's mounts but drops its env — filed as #747. NRI needs no override.

Why

Fixes #673. pcibus rendered the tree into the agent's overlay, reachable only through the libpcisysfs.so LD_PRELOAD shim. That covers libc consumers like lspci, but Go bypasses it — os.Open issues openat directly — so GPU Feature Discovery and the NVIDIA DRA driver read the node's real /sys and found no mock GPUs. GFD resolved each GPU's BDF from NVML, failed to read its class, and labelled the node nvidia.com/gpu.mode=unknown; the DRA driver omitted dra.k8s.io/pcieRoot for the same reason.

Fixes #681. nvidia.com/gpu.machine read unknown for a separate reason: GFD's default source for it is a DMI path no mock can own under kind, and one that does not exist at all on hosts without DMI.

Checklist

  • Commits are signed off (git commit -s)
  • Tests pass (go test -v -race ./...) — plus make helm-tests (175 tests, 15 snapshots) and go vet including under the e2e build tag
  • Linter passes (make lint-fix) — golangci-lint reports 0 issues; the target's govulncheck stage fails on Go 1.26.5 stdlib advisories fixed in 1.26.6, pre-existing and reached only through code this PR does not touch
  • New code has SPDX license headers
  • Documentation updated (if applicable) — docs/helm-chart.md
  • CHANGELOG.md updated (if user-facing change)

Verified with tilt ci -- --gpu-operator --gpu-profile gb300 on kind: both workers reach gpu.mode=compute and gpu.machine=NVIDIA-GB300-NVL, and all four GPUs resolve at the kernel paths from inside a served container. Not verified locally: kind's DMI mount-target hook, which cannot fire on Docker Desktop (no DMI) and is covered by Linux CI; and dra.k8s.io/pcieRoot, which needs a --dra run.

@giuliocalzo
giuliocalzo marked this pull request as draft August 27, 2026 12:02
@greptile-apps

greptile-apps Bot commented Aug 27, 2026

Copy link
Copy Markdown

Greptile Summary

The change serves the simulated PCI and machine-type data at paths used by Go consumers.

  • Reconciles rendered PCI entries with the active NVML device list.
  • Publishes paired sysfs mounts and preserves required DMI mount targets.
  • Configures NRI and end-to-end assertions for machine type and PCI visibility.

Reviews (8): Last reviewed commit: "Merge branch 'main' into feat/issue-673-..." | Re-trigger Greptile

Comment thread internal/agent/state.go Outdated
@giuliocalzo
giuliocalzo force-pushed the feat/issue-673-agent-pci-sysfs branch from 1f38fba to 05df40c Compare August 27, 2026 12:55
@giuliocalzo giuliocalzo self-assigned this Aug 27, 2026
@giuliocalzo giuliocalzo added the kind/feature Feature request or enhancement. label Aug 27, 2026
@giuliocalzo
giuliocalzo marked this pull request as ready for review August 27, 2026 12:59

@ArangoGutierrez ArangoGutierrez left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

roma-glushko
roma-glushko previously approved these changes Aug 27, 2026
Comment thread deployments/nvml-mock/helm/nvml-mock/templates/daemonset.yaml Outdated
Comment thread local/gpu-operator/gpu-operator.values.yaml
Comment thread CHANGELOG.md Outdated
Comment thread CHANGELOG.md Outdated
Comment thread internal/agent/cdi/spec_test.go
Comment thread internal/agent/gpudriver/gpudriver_test.go Outdated
Comment thread internal/agent/gpudriver/gpudriver_test.go Outdated
Comment thread internal/agent/pcibus/stage.go Outdated
Comment thread CHANGELOG.md Outdated
Comment thread internal/agent/state.go Outdated
Comment thread internal/agent/state.go
The pcibus simulator renders the tree into the agent's overlay, where
libpcisysfs.so redirects reads of /sys/bus/pci/devices to it. Go's os package
issues openat directly, so the shim never sees the open and the process reads
the node's real /sys. GPU Feature Discovery resolves each GPU's BDF from NVML
and then reads its class from sysfs, so it labelled every mock node
nvidia.com/gpu.mode=unknown; the DRA driver leaves dra.k8s.io/pcieRoot off its
ResourceSlices for the same reason.

The cdi simulator now bind-mounts the rendered tree read-only at the kernel
paths in the nvidia.com CDI spec, which is the channel that already delivers
the mock libnvidia-ml.so.1 to the operands. Both entries go together: the
lookup directory holds relative symlinks into ../../../devices/pciDDDD:BB, so
serving it alone yields entries that list and whose every attribute read
returns ENOENT. They are emitted only when the state declares a topology,
matching the condition pcibus renders on, because a mount with a missing
source fails container creation for the whole pod.

/sys/devices is served whole — narrowing it to a profile's root complexes
needs mountpoints the runtime cannot create on a read-only sysfs, and profiles
routinely declare roots the node does not have. A served container therefore
no longer sees the host's other device classes; NVIDIA#689 tracks that. Shadowing
that directory also takes /sys/class/dmi/id with it, so pcibus reproduces the
attributes kind's createContainer hook bind-mounts, or every served pod dies
on a missing mount target. That is target compatibility only, not a
machine-type mock (NVIDIA#681), and needs the node's /sys on the agent.

Renders now converge on an empty topology instead of returning early, so a
config that declares no bus_id cannot leave the previous profile's GPUs
mounted at the kernel paths.

Fixes NVIDIA#673

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
nvidia.com/gpu.machine read unknown on every mock node. GFD derives it from
--machine-type-file, and its default /sys/class/dmi/id/product_name is a path no
mock can own under kind: the node image writes "kind" there and re-binds it into
every container after the container's own mounts are set up, while hosts without
DMI have no such path at all.

The agent writes the machine type to driver/config/machine-type, which the CDI
config mount already serves at /etc/nvml-mock, and the chart's GPU Operator
values point GFD_MACHINE_TYPE_FILE there. CDI's own env edits are not a usable
channel: the runtime applies the spec's mounts but drops its env, so nothing in
containerEdits.env reaches a served container today.

The value is the profile's GPU product name for want of a platform name in the
profiles, so the label reads NVIDIA-GB300-NVL, matching gpu.product, rather
than the NVIDIA-GB300-NVL72 a real compute tray reports.

Also compacts the comments added for NVIDIA#673, which had grown to restate the
issue rather than record intent.

Fixes NVIDIA#681

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
Nothing in that container reads it. It is there so a `kubectl exec` can compare
the node's own sysfs against the tree the agent renders beside it, which is the
first thing worth checking when a consumer resolves the wrong devices.

Read-only, like the node-agent's copy.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
…le's

pcie_topology is compiled from the profile whole while GPU_COUNT truncates the
device list, so a capped node rendered BDFs with no NVML device behind them.
Every attribute of those entries is plausible — vendor falls back to 0x10de and
the class to a 3D controller — which was harmless only while nothing could read
the tree, and stopped being harmless when it started being served at the kernel
paths.

Reconcile the layout against the devices instead, in one place both the renderer
and the mount gate read: declared BDFs no device claims are dropped along with
any root they leave empty, and a device no root claims is adopted by the first,
since an unplaced GPU cannot be resolved at all where a misplaced one only
misreports its pcieRoot. HasPCITopology now answers from that same
reconciliation, so the predicate cannot drift from what gets rendered.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
Review feedback: pointing GFD at the mock's machine-type file is simulation
logic, not something to ask an operator's values file for. The NRI plugin
already injects the overlay's config path the same way, so add
GFD_MACHINE_TYPE_FILE beside it as a default a workload can still override.

The overlays keep the override, because they drive the CDI path where the
runtime applies the spec's mounts and drops its env. Also drop the /host/sys
mount from the mock container, which was a debugging aid nothing there reads,
and stop the DMI comments reading as if kind were the only deployment target:
the reproduction keeps DMI attributes readable on any cluster, and only the
mount-target failure is kind's.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
The dropped containerEdits.env is now NVIDIA#747, so the operator-values overrides
and the docs say which defect they are working around. The spec's env block
carries the warning too: its values are correct but the channel does nothing,
so nothing new should be built on it.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
A device whose BDF the profile's pcie_topology omits was appended to the first
declared root, which handed the DRA driver and the device plugin a specific
PCIe root and NUMA node for a GPU the profile never placed. Locality is the
thing that tree gets read for, and a confident wrong answer there is worse than
none: it cannot be re-derived, and it silently misgroups GPUs.

Such a device now renders under the root its own address implies, joining a
declared root only where the address names it, and otherwise reporting
numa_node -1 — what Linux writes for a device it has no proximity information
for, so consumers read "unknown" in the encoding they already handle.

It is still rendered, because a GPU missing from the tree is one no consumer
can resolve from the BDF NVML hands it, which is the failure this path exists
to fix.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
The comments said "the runtime" drops containerEdits.env, which reads as CDI
env being useless everywhere and contradicts a passing e2e assertion: the
nri-cdi-inject leg proves NVML_MOCK_DEVICE_SOURCE arrives from the NRI spec's
containerEdits when containerd resolves it natively, and MEP-0002 records the
same measured on stock kind.

What drops env is the toolkit resolving nvidia.com/gpu, so say that — it points
whoever picks up NVIDIA#747 at the right component instead of the runtime that
demonstrably works.

Signed-off-by: Giulio Calzolari <gcalzolari@nvidia.com>
@giuliocalzo
giuliocalzo force-pushed the feat/issue-673-agent-pci-sysfs branch from 6d246d9 to 4d84de7 Compare August 28, 2026 08:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/feature Feature request or enhancement.

Projects

None yet

3 participants