Repository navigation
Place vGPU profiles the driver hides from creatable lists - #511
Conversation
73b6029 to
2c9c09c
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2c9c09c. Configure here.
2c9c09c to
9f5011e
Compare
sjmiller609
left a comment
There was a problem hiding this comment.
WDYT? open to push back on this. But the main concern is that "hypeman resources" output would not be accurate. I would be best if we know up front how many of any given profile can fit, not just discovering what doesn't fit after trying.
potential issues with this approach:
hypeman resourcesstill reports inaccurate remaining slots per profile.- allocation discovers capacity through rejected writes rather than shared capacity accounting.
potential solutions:
- share capacity accounting between allocation and
hypeman resources.- calculate remaining slots from usable VRAM minus configured reservations, accounting for placement constraints and free, healthy VFs.
- keep driver-rejection handling as a fallback.
-->
✱ stlc build✅ go code · compare
✅ python code · compare
✅ typescript code · compare
Diagnostics: ❗ 0 new / 1 total error, 💡 0 new / 5 total note
Build metadata
This comment is auto-generated by stlc and is kept up to date as you push. |
656a7d5 to
63d6f41
Compare
In mixed-size mode the driver drops a profile from creatable_vgpu_types once its GPU holds as many vGPUs as that profile's equal-size limit (12 for L40S-4Q), even while a placement for it is free, so 4Q claims failed on busy GPUs and /resources under-reported. Ask NVML for each GPU's creatable placements instead (the query behind nvidia-smi vgpu -c -v), which stays accurate in that state. libnvidia-ml is loaded at runtime through purego, so builds need no cgo and hosts without the driver never load it; a query takes about 20 ms. Placement picks a GPU with a free placement for the profile, and /resources counts per GPU the smaller of free placements and free healthy VFs. Both fall back to the sysfs lists if NVML cannot be loaded, fails, times out, or a GPU is not in mixed-size mode. A timed-out query is abandoned rather than waited for, and queries skip NVML until it returns, so a hung driver costs one timeout rather than one per claim. Fallbacks are counted by reason in hypeman_resources_gpu_nvml_fallbacks_total. Admission only needs the free-VF count, so it no longer lists profiles. The type is now written under the allocation lock, before the claim is saved, so the next claim's query sees the placement as taken. A failed configure or claim save resets the VF.
63d6f41 to
ab8ee1e
Compare
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
NVML parses the PCI domain numerically, so the sysfs address works as is and the four-digit-domain prefix only broke wider domains. The creatable placements query now sets the heterogeneous mode by name rather than by zero value.
A GPU that NVML fails for, or that is not in mixed-size mode, no longer sends every GPU on the host back to the sysfs lists. The others keep NVML's creatable placements, so the profiles sysfs hides stay placeable there. A query that cannot load NVML or times out still falls back for every GPU.
A GPU drops to equal-size mode after a driver reload or GPU reset, and NVML then refuses its creatable placements. That fell back under the error reason, the same as NVML failing outright, so a drifted host kept the error count climbing on every claim and resources refresh. Count it as equal_size when every GPU NVML failed for is in equal-size mode, so error stays a signal for NVML itself.
A claim now writes the type before it saves the claim, so a restart between the two leaves a configured VF that no instance claims. It holds a placement and is left out of /resources, and nothing reset it until a later claim happened to pick it or found no VF for its profile. Reset such VFs once at startup, under the allocation lock and behind the same open-VFIO-handle check the allocator's repair uses.
Vendor VFIO is Linux-only, and on macOS GetDeviceSysfsPath returns an empty path, so the test's expected reset path cannot match there.
An initialized NVML holds every GPU's device nodes open, the same nodes DCGM holds, so keeping it initialized for hypeman's lifetime would block cycling SR-IOV on a wedged GPU and unloading the driver until hypeman is stopped. Initialize NVML for each query and shut it down after; the library stays loaded. On a two-GPU L40S host the query now takes about 55 ms rather than 20 ms, and no NVML handle is left open between queries. This also means a query never runs on NVML state left over from before a driver event.
A host on the sysfs lists, such as one with a GPU drifted to equal-size mode, logged a warning on every claim, resources read and monitoring refresh. Keep counting every fallback, but log only when a fallback starts or its error changes, and log once when NVML answers for every GPU again.
Queries that each initialize and shut down NVML fail when they overlap: on the dev L40S host (driver 580.178.05), 20 concurrent queries returned Invalid Argument, Insufficient Resources and GPU access blocked errors, which would fall placement and /resources back to the sysfs lists whenever a resources read overlaps a claim. Hold a lock from initialization to shutdown. Concurrent queries on one initialization took no less time than serialized ones, so this costs no throughput.
Initializing NVML for each query held no GPU device nodes between queries, but it nearly tripled the query (about 55 ms against 20 ms), so the last of 20 concurrent creates waited about 1.2 s for the allocation lock instead of 0.4 s, and it needed the queries serialized. The device nodes only matter for the SR-IOV cycle that fixes a wedged VF, which already stops the services holding the GPU, so keep NVML initialized as before and add hypeman to the services that procedure stops. Stopping hypeman leaves running VMs alone. This reverts the code of 79357c9 and 8f421f3.
…idden-profiles # Conflicts: # lib/oapi/oapi.go
sjmiller609
left a comment
There was a problem hiding this comment.
- cache GPU capacity for
/resources; update allocation records on successful configure/release, refresh profile counts asynchronously after changes, and periodically reconcile with NVML.- follow
lib/instances/admission_allocations.go: startup hydration, lifecycle-updated allocation records, periodic reconciliation, and in-memory reads. profile counts need placement-aware recalculation, not simple increments/decrements.
- follow
- bound concurrent NVML calls before starting them; the current timeout guard allows many requests to enter a hung driver simultaneously.
- retry orphaned VF cleanup periodically; a temporary startup failure can leave capacity stranded and prevent new allocations (not sure - wdyt?)
The stuck check only stopped new queries once one had timed out, so callers arriving before that each entered a hung driver. Take a single slot before starting a query and give it back when the driver returns; waiting for the slot counts against the same timeout.
/resources and the resource metrics queried NVML on every read. Cache the driver's placement data instead: load it on first use, refresh it in the background after every vendor VFIO configure or reset, and expose a refresh for the reconciler. Reads recompute counts against the caller's VFs and quarantine, so a claim or release counts at once, and refreshes coalesce and run one at a time so an older snapshot never replaces a newer one.
The unclaimed VF reset ran once at startup, so a reset refused then, or an orphan left later by a failed claim whose reset also failed, held its placement until the allocator next selected that VF, which it may never do if placement skips the host. Run it on every pass, count refusals under the reset_unclaimed_vfs reconcile stage, and reload the cached vGPU placements at the end of each pass.
|
all three addressed
|

Problem
In mixed-size vGPU mode, the driver drops a profile from
creatable_vgpu_typesonce a GPU holds as many vGPUs as that profile's equal-size limit (12 for L40S-4Q), even when a placement for it is still free. Hypeman trusts those lists, so 4Q creates fail on busy cards and/resourcesis wrong in both directions. On one busy production day, 36% of 4Q claim failures had a free placement the driver had stopped listing.Will release cold start gpus after this PR.
Fix
NVML's creatable-placements query (what
nvidia-smi vgpu -c -vshows) stays accurate in that state (L40S, driver 580.178.05):/resourcesread capacity from NVML.libnvidia-ml.so.1is loaded at runtime with purego, so no cgo is needed. A query takes about 20 ms./resourcesand the resource metrics read cached placements instead: the cache loads at startup, refreshes in the background after every vendor VFIO configure or reset, and refreshes again on every one-minute reconcile pass. Free and quarantined VFs are read fresh on each request, so a claim or release counts at once; the placements it frees or takes catch up when the background refresh lands. Refreshes coalesce and run one at a time, so an older snapshot never replaces a newer one.claimVGPUwrites the type under the allocation lock, before saving the claim, so the next claim sees the placement as taken. If configure or save fails, the VF is reset. A restart between the write and the save, or a failed claim whose reset also failed, leaves a configured VF with no claim; the reconciler resets those on every pass, so a refused reset is retried, and counts refusals ashypeman_instances_vgpu_reconcile_failures_total{stage="reset_unclaimed_vfs"}./resourcesavailableis, summed over GPUs, the smaller of free placements and free healthy VFs. Counts aren't additive across profiles; the OpenAPI description says so.Fallback
A GPU that NVML fails for, or that isn't in mixed-size mode, uses the sysfs lists as on main; the other GPUs keep NVML. If NVML can't load or times out (5 s), every GPU falls back. Each fallback increments
hypeman_resources_gpu_nvml_fallbacks_total{reason}, and a warning is logged when a fallback starts or its error changes (plus one info line when NVML recovers); a query whose only failures are GPUs in equal-size mode (drift after a driver reload or GPU reset) counts asequal_size, noterror. NVML queries run one at a time, and waiting for the slot counts against the same 5 s, so callers queued behind a hung query never enter the driver. A timed-out call is abandoned and later queries skip NVML until it returns, so a hung driver costs one timeout, not one per claim.Testing
/resourcescounts, per-GPU and whole-host fallback, timeout and stuck handling, claim ordering, and resets.go test ./lib/devices ./lib/resourcesandgo test ./lib/instances -run 'VGPU|VendorVFIO'pass, also with-race./resourcesmatched NVML's free placements at every step (4Q 7, 2Q 14; main reported 26 and 45)./resourcesplacement cache (in-memory reads, coalesced refreshes, refresh after configure and reset) and the one-at-a-time NVML slot have unit tests, all mutation-checked; none of them has run on a GPU host.Notes
CGO_ENABLED=0builds (goreleaser) become dynamically linked against glibc. Fleet builds already are.hypeman_resources_gpu_profile_slotsdrops on busy hosts after deploy (it now counts placements, not VFs). Anything using it as a free-VF count should readhypeman_resources_gpu_slotsinstead, which is unchanged./dev/nvidia0..N,/dev/nvidiactland/dev/nvidia-caps/*open (checked on the dev host), the same nodes DCGM holds. The SR-IOV cycle in GPU.md that fixes a wedged VF now stops hypeman along with DCGM and persistenced; stopping hypeman leaves running VMs alone. The driver upgrade runbook already stops hypeman. The fds are close-on-exec, so VMMs don't inherit them, and switching a GPU's vGPU mode still works while hypeman holds them.Note
High Risk
Changes core vGPU allocation, reconciliation, and capacity reporting on production GPU hosts; NVML holds device nodes and mis-fallback or stuck-query behavior could block or mis-advertise placements until recovery.
Overview
Vendor VFIO vGPU placement and
/resourcesprofile counts now use NVML creatable placements (via runtimelibnvidia-ml.so.1+ purego) instead of per-VFcreatable_vgpu_types, which under-report busy mixed-size GPUs. Claims still query NVML on each allocation;/resourcesand metrics read a cached placement snapshot refreshed after configure/reset and on reconcile, with sysfs fallback, 5s timeouts, serialized queries, andhypeman_resources_gpu_nvml_fallbacks_total.Claim lifecycle on vendor VFIO writes
current_vgpu_typeunder the allocation lock before persisting metadata, resets the VF if configure or save fails, and the reconciler resets unclaimed dirty VFs every pass (newreset_unclaimed_vfsfailure stage). Profileavailableis redefined as min(free placements, free VFs) per GPU, non-additive across profiles.Admission and lightweight GPU status skip profile/NVML work (
GetGPUStatuswithProfilesflag). Docs and OpenAPI describe the new semantics; SR-IOV recovery runbooks note stopping hypeman because NVML holds GPU device nodes.Reviewed by Cursor Bugbot for commit 9fe16b3. Bugbot is set up for automated code reviews on this repo. Configure here.