Skip to content

feat: activate NVLink fabric partitions for vGPU VFs on NVSwitch systems#193

Open
lexfrei wants to merge 8 commits into
NVIDIA:masterfrom
lexfrei:feat/fabric-partition-activation
Open

feat: activate NVLink fabric partitions for vGPU VFs on NVSwitch systems#193
lexfrei wants to merge 8 commits into
NVIDIA:masterfrom
lexfrei:feat/fabric-partition-activation

Conversation

@lexfrei

@lexfrei lexfrei commented Jul 4, 2026

Copy link
Copy Markdown

Stacked on #192 (feat/vendor-vfio-vgpu); the diff includes that PR's VF-discovery changes until it merges, after which this branch is rebased to a fabric-only diff.

Addresses #133.

On NVSwitch systems (HGX H100/H200) running SR-IOV vGPU with Fabric Manager in FABRIC_MODE=2, a whole-card (NVLink-enabled) guest cannot initialise CUDA — cuInit fails with error 802 ("system not yet initialized") — until its VF's NVLink fabric partition is activated through the Fabric Manager SDK. The device plugin hands the VF to the virt-launcher pod but nothing activates the fabric partition, so those guests fail CUDA on these systems.

This change activates the VF's single-GPU fabric partition during Allocate, before the guest starts, giving working CUDA from the first init with per-VM NVLink isolation:

  • A self-contained pkg/fabric wraps libnvfm (the nv_fm_agent API) via cgo, with a portable stub so non-linux / CGO_ENABLED=0 builds are unaffected. The FM ABI is declared in the binding (headers not vendored).
  • Allocate resolves the VF → parent PF → FM physicalId (NVML module id) → single-GPU partition, matched strictly on physicalId (never PCI order), then activates the partition for exactly that VF (fmActivateFabricPartitionWithVFs takes one VF per GPU).
  • MIG-mode GPUs have NVLink disabled and are not in the fabric, so their VFs need no activation and are skipped (nvmlDeviceGetMigMode). Only whole-card VFs are activated.
  • Activation is gated by FABRIC_PARTITION_ACTIVATION (default auto), a no-op on non-NVSwitch systems and classic passthrough GPUs. FABRIC_FAIL_MODE (default closed) chooses whether an FM error fails the allocation or is logged and allowed. A pod-resources reconciler deactivates a partition once its VF is gone.
  • The runtime image bundles a pinned libnvfm.so.1; the pod needs hostNetwork (to reach the FM command API on 127.0.0.1:6666) and the pod-resources socket mounted, and FM in FABRIC_MODE=2.

See docs/fabric-partition-activation.md for the full design.

Scope

This PR activates single-GPU fabric partitions — one partition per allocated VF, resolved from the VF's parent physical GPU. Multi-GPU partition activation (giving one VM several whole-card vGPUs with NVLink P2P between them — which requires activating a predefined multi-GPU partition with one VF per member GPU, plus GetPreferredAllocation support so the allocated VFs land on GPUs that form a defined partition) is deliberately out of scope here and implemented in the follow-up #195. MIG-backed vGPUs need no activation at all: MIG disables NVLink, so the GPU is not on the fabric and CUDA initializes standalone.

@copy-pr-bot

copy-pr-bot Bot commented Jul 4, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@rthallisey

Copy link
Copy Markdown
Collaborator

Can you replace the fabric bindings with https://github.com/NVIDIA/go-nvfm ?

@lexfrei

lexfrei commented Jul 7, 2026

Copy link
Copy Markdown
Author

Done — switched the fabric bindings from the inline cgo ABI to go-nvfm. The plugin now drives libnvfm through go-nvfm, which dlopen's it at runtime and ships the SDK headers, so the hand-maintained ABI transcription and the link-time dependency are both gone. Behaviour is unchanged, and it builds/vets/tests clean under linux + cgo.

lexfrei added 8 commits July 20, 2026 02:07
…work

Ada/Hopper+ GPUs (vGPU 17+) no longer expose vGPU instances through
mdev; instead, SR-IOV Virtual Functions stay bound to the "nvidia"
host driver and are configured directly on the PCI function via
sysfs. The existing vGPU discovery only walks /sys/bus/mdev/devices,
so it finds nothing on these GPUs and every vGPU VF ends up
indistinguishable from a whole-card passthrough device, since both
share the same PCI vendor:device id.

Add a second discovery path that walks /sys/bus/pci/devices looking
for Nvidia functions bound to the "nvidia" driver, skips Physical
Functions (identified by sriov_totalvfs), and treats a Virtual
Function as a configured vGPU when its current_vgpu_type is
non-zero. The profile name is resolved by matching that type id
against the creatable_vgpu_types catalog, merged per physical card
(scoped by each VF's physfn parent) since a configured VF's own list
is frequently empty or reduced to the active entry, and since vGPU
type ids are only unique within a single physical card.

Because these VFs are ordinary PCI/IOMMU-group devices, discovered
functions are added to the same iommu/BDF maps used by GPU
passthrough and dispatched through the existing GenericDevicePlugin,
grouped by vGPU profile name instead of PCI device id. No changes
were needed to the allocate path or device contract; the mdev-backed
vGPU discovery is untouched.

Document the new hardware path in the README alongside the existing
mdev instructions, since it applies to a different GPU generation
and neither replaces the other.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
A fully consumed card reduces the creatable_vgpu_types list of every
one of its functions down to the header, so the per-card catalog
cannot resolve the types of already-configured VFs and the plugin
advertised nothing exactly when all capacity was allocated. Fall back
to a host-wide catalog merged across all cards; numeric ids that
different cards map to different names stay ambiguous and are still
skipped rather than guessed.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
On a host where every card is fully consumed, no creatable_vgpu_types
catalog lists anything at all — sysfs alone cannot resolve configured
type ids to profile names no matter how catalogs are merged. Resolve
through NVML as the last resort: the supported-vGPU list of a card
does not shrink as capacity is allocated. Scoped per physical card
like the sysfs catalogs, cached per scan, and requires the host
driver library to be loadable in the container.

Signed-off-by: Aleksei Sviridkin <f@lex.la>
…ation

On NVSwitch systems running SR-IOV vGPU with Fabric Manager in
FABRIC_MODE=2, a whole-card guest cannot initialise CUDA until its VF's
NVLink fabric partition is activated through the Fabric Manager SDK
(cuInit fails with error 802 otherwise).

Add a self-contained pkg/fabric wrapping libnvfm (the nv_fm_agent API):

- cgo binding (linux && cgo) linking libnvfm.so.1, with a portable stub
  for other platforms and CGO_ENABLED=0 so the whole module still builds
  and unit-tests on developer machines. The FM ABI is declared in the
  binding; NVIDIA's proprietary headers are not vendored.
- Connect / GetSupportedPartitions / ActivateWithVFs / Deactivate / Close
  with sizeof-based struct-version handling and a full fmReturn_t -> error
  mapping. The connection address type (Unix socket vs host:port TCP) is
  selected from the address form.
- NVML helpers to resolve a physical GPU's FM physicalId (module id) and
  MIG mode, and a VF -> single-GPU partition resolver keyed strictly on
  physicalId, never on PCI enumeration order.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Activate a whole-card vGPU VF's NVLink fabric partition during
GenericDevicePlugin.Allocate, before the device is handed to the
virt-launcher pod, so the guest's CUDA init succeeds with per-VM NVLink
isolation from the first init.

For each allocated VF the plugin resolves its single-GPU fabric partition
(VF -> parent PF -> FM physicalId -> partition) and activates it for that
one VF; fmActivateFabricPartitionWithVFs takes one VF per GPU, so a
single-GPU partition is always activated with exactly one VF. MIG-mode
GPUs have NVLink disabled and are not in the fabric, so their VFs need no
activation and are skipped (nvmlDeviceGetMigMode). The feature is a no-op
on non-fabric systems and classic passthrough GPUs.

Configuration via environment: FABRIC_PARTITION_ACTIVATION (auto/off),
FABRIC_MANAGER_ADDRESS (default the FM TCP command interface),
FABRIC_FAIL_MODE (closed: an FM error fails the allocation; open: log and
allow), and FABRIC_RECONCILE_INTERVAL. A pod-resources reconciler
deactivates a partition once its VF is gone, since the device plugin API
has no release callback. Unit-tested with a fake Fabric Manager client.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
The fabric partition activation binding links libnvfm.so.1 (NEEDED in the
binary), so both the builder and the runtime image need it. Fetch a
pinned libnvfm from the CUDA repository package at build time (only the
versioned shared object; the FM ABI is declared in-tree, headers are not
required), link against it, and copy it into the distroless runtime image
on the loader search path. The version is a build ARG so it can track the
target host's Fabric Manager daemon.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Document the fabric partition activation design: the FM SDK binding, the
VF -> single-GPU partition resolver, single-VF activation (one VF per
GPU), MIG-skip, the pod-resources reconciler for deactivation, the
configuration, the runtime requirements (hostNetwork for the FM TCP
command interface, pod-resources mount, FABRIC_MODE=2), and libnvfm image
bundling. Includes a draft upstream PR description.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
Replace the hand-written inline cgo Fabric Manager SDK binding with the
official github.com/NVIDIA/go-nvfm library. The previous binding declared
the nv_fm_agent struct layouts and entry points by hand in the cgo preamble
(NVIDIA's headers cannot be redistributed) and linked libnvfm.so.1 at build
time.

go-nvfm ships the FM SDK headers and dlopen's libnvfm at runtime, so both the
hand-maintained ABI transcription and the link-time dependency are gone.
Behaviour is unchanged: same Client interface and call sequence, same
single-VF partition activation, same physicalId-based resolution and MIG-mode
skip, same 5s connect timeout, same TCP/Unix address handling, and the same
return-code to sentinel-error mapping the allocation logic branches on.

The runtime image still bundles the versioned libnvfm.so.1; go-nvfm is
pointed at that SONAME because the unversioned -dev symlink is not shipped in
the distroless image.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Aleksei Sviridkin <f@lex.la>
@lexfrei
lexfrei force-pushed the feat/fabric-partition-activation branch from 495619e to a8bf9ae Compare July 20, 2026 00:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants