Skip to content

Integrate vendor VFIO vGPUs into the instance lifecycle - #321

Merged
yummybomb merged 85 commits into
hypeship/vendor-vfio-backendfrom
hypeship/vendor-vfio-vgpu
Sep 2, 2026
Merged

Integrate vendor VFIO vGPUs into the instance lifecycle#321
yummybomb merged 85 commits into
hypeship/vendor-vfio-backendfrom
hypeship/vendor-vfio-vgpu

Conversation

@yummybomb

@yummybomb yummybomb commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Top layer of the vendor VFIO vGPU stack (generalize-vgpu-device #322hypervisor-liveness #363vendor-vfio-backend #364this). The backend itself is in #364; this layer wires it into the instance lifecycle using claim-first allocation: GPUDevicePath in instance metadata is the authoritative VF claim, persisted before any hardware is touched.

  • Claim-first allocation — under a single allocation lock, the allocator lists instance metadata, selects an unclaimed VF (least-loaded placement preserved), and persists the claim; only then is the VF configured. Within a GPU, clean VFs are preferred over dirty ones, and a dirty VF that refuses its reset is skipped in favor of another candidate. Metadata presence reserves the VF, so a stale claim can never clear a VF out from under a live owner — the allocator simply never hands out a claimed VF.
  • Idempotent configuration with dirty-VF repair — a dirty VF selected by the allocator is reset at claim time, before the claim is persisted and regardless of whether its leftover type matches the requested profile, gated on the open-VFIO-handle in-use check; a refused reset fails the allocation. ConfigureVGPU then no-ops when the VF already has the requested type and keeps a reset-and-recreate backstop for a type mismatch. When the leftover type consumes enough framebuffer that the requested profile disappears from every creatable list, the allocator resets unclaimed dirty VFs and retries selection once. This replaces the periodic vendor VFIO device sweep.
  • Reset-before-unclaim everywhere — every release path (create cleanup, start rollback, stop, delete, reconciler) resets the VF while the metadata claim is still on disk and removes the claim only afterward. Failure cleanup confirms the VMM is dead (socket-ownership liveness from Harden hypervisor process liveness checks #363) before touching the device; ambiguous liveness preserves the claim. If a hardware reset fails, the claim is still removed and the next allocation repairs the dirty VF. When create cleanup cannot confirm the VMM is dead, the claim and instance data are retained with an error log; deleting the instance retries the release.
  • Simplified reconciler — a periodic pass releases metadata claims whose VMM is confirmed dead, under the instance lock then the allocation lock. The device-level sweep remains for mdevs only. Removed entirely: GPUAssignedAt, GPURetainedForCleanup, delete-only retention records, vgpu_cleanup_pending errors, the assignment grace period, the in-memory owners map, and the live-claim release guard.
  • Hypervisor scope — hypervisor selection remains a caller policy, preserving the framework layer's existing mdev behavior (mdevs are created fresh per assignment, so claim-first applies to vendor VFIO only). The production rollout continues to place vGPU instances on QEMU; the Cloud Hypervisor vendor-VFIO limitation (upstream cloud-hypervisor#7572) is documented in lib/devices/GPU.md (in the base PR Add the vendor VFIO vGPU device backend #364).
  • API — the instance API exposes gpu.device_path, the assignment identity on vendor VFIO hosts where mdev_uuid is empty.

The key invariant: cleanup never resets a VF after removing its claim, and all recovery routes through the allocator, which rechecks current metadata and VFIO liveness.

Testing

  • go build ./..., go vet ./... clean
  • go test ./lib/devices/ and the lib/instances vGPU/reconcile suites pass; go test -race passes for the new concurrency and crash-recovery tests (concurrent claims select distinct VFs; a crash between claim and configure is reconciled at startup; dirty-VF repair with and without open VFIO handles, including a dirty VF that suppresses the profile from all creatable lists; release order under failed resets; clean-first VF selection with fallback when a dirty VF refuses reset; GPU ordering with unknown load). Claim-path tests are linux-tagged since they depend on sysfs paths; cleanup/release/selector tests remain cross-platform
  • Broader lib/instances / cmd/api/api suites have pre-existing environment failures (image pulls, network privileges) that reproduce identically on the unmodified base

Note

High Risk
Changes GPU allocation, release ordering, and hypervisor kill semantics on production hosts; incorrect liveness or claim handling could leak VFs or wedge hardware.

Overview
Vendor VFIO vGPUs are wired into create/start/stop/delete using metadata-first VF claims: the allocator persists GPUDevicePath under a global lock, then configures the VF (mdev creation stays on the existing path). Least-loaded GPU selection, clean-VF preference, dirty-VF repair at claim time, and removal of the in-memory owner map and periodic vendor VFIO device sweep move recovery into allocation and reconcile.

Reconciliation replaces startup-only mdev cleanup with StartVGPUReconciler (initial pass + ~1 minute): stale metadata claims are released when the VMM is confirmed dead; mdev sweeps skip UUIDs owned by running instances. New metrics cover reconcile and stale-release failures.

VF safety: QEMU and instance teardown SIGTERM before SIGKILL when VFIO/vGPU is attached (VFIOTermGrace), with docs on SIGKILL-during-init wedge risk. Instance API responses add gpu.device_path when set; integration tests allow vendor VFIO hosts.

Reviewed by Cursor Bugbot for commit fb132cd. Bugbot is set up for automated code reviews on this repo. Configure here.

@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from 199b1d1 to a93d009 Compare July 28, 2026 21:32
@yummybomb
yummybomb changed the base branch from main to hypeship/generalize-vgpu-device July 28, 2026 21:32
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch 4 times, most recently from d893fce to de4742a Compare July 29, 2026 15:15
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from de4742a to 18e047f Compare July 29, 2026 16:09
@yummybomb

yummybomb commented Aug 4, 2026

Copy link
Copy Markdown
Contributor Author

Trimmed over-engineering after an architectural review against the parent task (host kernel 6.8 / Ubuntu 24.04 support). Most of the removed weight came from earlier review-round additions rather than the original design:

@yummybomb
yummybomb marked this pull request as ready for review August 5, 2026 19:47
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from 7ef1f8d to bf21162 Compare August 5, 2026 19:57
Comment thread lib/instances/start.go Outdated
Comment thread integration/vgpu_test.go
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from bf21162 to 83c53ea Compare August 5, 2026 20:24
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from 83c53ea to 79eef07 Compare August 5, 2026 20:39
Comment thread cmd/api/main.go Outdated
Comment thread lib/devices/vendor_vfio_linux.go Outdated
Comment thread lib/instances/query.go Outdated
Comment thread lib/instances/create.go Outdated
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from ab2b0e8 to 19b9602 Compare August 6, 2026 17:08
Comment thread lib/instances/start.go
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from d92201d to f177335 Compare August 6, 2026 19:06
@yummybomb
yummybomb changed the base branch from hypeship/generalize-vgpu-device to hypeship/vendor-vfio-backend August 6, 2026 19:08
@yummybomb yummybomb changed the title Support vendor VFIO vGPU devices Integrate vendor VFIO vGPUs into the instance lifecycle Aug 6, 2026
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from f177335 to 1d875f2 Compare August 6, 2026 19:26
@yummybomb
yummybomb force-pushed the hypeship/vendor-vfio-vgpu branch from 1d875f2 to 23fb2f5 Compare August 6, 2026 19:40
yummybomb and others added 25 commits September 1, 2026 20:11
- Retry the vendor VFIO reconcile sweep with a bounded delay when the
  startup instance listing fails, instead of disabling orphan recovery
  until the next process restart. One pending retry at a time.
- Schedule the in-process orphaned-release retry when a rollback's
  retention record cannot be saved (create and start), instead of
  leaking the VF until restart. The retry scans claims without a
  self-exclusion because a restarted instance may hold the same VF.
- Reject snapshot restore into a vGPU retention stub, matching start,
  fork, and snapshot.
- Give passthrough PCI instances the same SIGTERM grace as vGPU
  instances on stop/delete, matching the QEMU-side vfioTermGraceFor.
- Render the vgpu_cleanup_pending API detail from the error itself
  instead of duplicating its prose; use the manager clock in the claim
  scan; collapse the create-rollback retention branch.
- Move ReconcileVGPUs off the Manager interface to a startup type
  assertion and unexport listInstancesForReconcile and
  hypervisorMayBeAlive.
Record the owning instance and assignment time for each vendor VFIO VF so
a reconcile sweep can run while instances are being created: recently
assigned VFs get a grace period before they are eligible (mirroring
orphanedMdevGracePeriod), and owned VFs are destroyed with their recorded
owner ID instead of failing the ownership check.
Run the fail-closed vGPU reconcile once at startup and every minute after,
skipping hosts without a vGPU framework. Each pass retries releases for
assignments whose owner is no longer live (re-verified under the instance
lock) and then sweeps device-level leftovers with no live metadata claim.

This deletes the per-path orphan retry goroutines - whose path-keyed
dedup could drop cleanup for a newer assignment reusing the same VF -
the CAS/timer retry in ReconcileVGPUs, the stopped-instance release
special case in StopInstance, the retention fallbacks that scheduled
background retries, and the orphan-abandoned metric.
An assignment could lose its persisted hypervisor PID while its VM stays
alive - a post-boot metadata save failure or a hypeman crash before the
save. The liveness checks gated the socket-ownership scan on a non-nil
PID, so once the startup grace expired the reconciler considered such an
assignment stale and could remove an mdev out from under the live VM
(DestroyMdev has no in-use guard).

Run the socket-ownership scan unconditionally: a live VMM always holds
its control-socket listener, and a missing listener still resolves to
not-alive, so genuinely stopped instances are released as before. The
claim scan in releaseStoredVGPUExcluding gets the same treatment.
The five-minute fresh-assignment protection existed three times: the
instances startup grace, the mdev orphan grace, and the vendor VFIO
sweep grace. Define it once in lib/devices and alias the instances
constant to it.
A wedged or leaked VF presents as reduced GPU capacity while /resources
still reports full capacity, and reconcile, stale-release, and retention
failures were visible only as log lines. Count failed reconcile stages,
failed stale releases, and retained assignments (by operation and
whether the retention record persisted) so sustained failure can alert.
When rollback release fails and the subsequent retention save also fails,
the assignment save from earlier in start may still hold the claim on
disk. Check for that surviving claim before reporting the retention as
unpersisted, so the API does not emit vgpu_unretained_instance for an
assignment that is still durably attributed.
A dirty VF's leftover type consumes framebuffer, which can remove the
requested profile from every creatable_vgpu_types list before the
selector's repair path can resolve it. When selection fails, reset
allocated VFs that no instance claims (in-use-gated, under the
allocation lock) and retry once.

Also move the claim-path tests behind a linux build tag: they depend on
GetDeviceSysfsPath, which returns an empty string on darwin.
Selection treats an unclaimed allocated VF as repairable, but configure
no-ops when the leftover type already matches the requested profile,
skipping the open-VFIO-handle check and the reset. A claim removed
after a failed reset could then hand the VF's stale device instance to
a new VMM while the previous holder still had it open.

Reset the selected VF at claim time when it is dirty, regardless of
type, under the allocation lock and gated on the in-use check inside
destroy. A refused reset fails the allocation without leaving a claim.
- Fall back to mdev when framework changes between unlocked and locked
  discovery in claimVGPU, instead of returning a hard error (TOCTOU)
- Scope SIGTERM-before-SIGKILL grace to vendor VFIO framework only,
  not all instances with a GPU profile (mdev does not need it)
- Log a warning when StartVGPUReconciler type assertion fails silently
- Remove dead setStoredVGPUDevice call for vendor VFIO in createInstance
- Extract vgpuCleanupGuard to deduplicate liveness + claim checks
  shared between cleanupCreateVGPU and cleanupStartVGPU
- Mention dirty VF repair in the zero-creatable-profiles error message

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Three changes from the code review of the vendor VFIO vGPU lifecycle:

1. Capture VFs from the pre-lock discovery call and only refresh VFs
   (not re-check framework) under the allocation lock. The framework
   cannot change between the two calls, so the second check and the
   fallback to createVGPUDevice were dead code.

2. Replace the linear VF scan in the dirty-VF repair path with a
   direct vfByAddress lookup — the code was scanning for a single
   known address.

3. Add a lock-ordering comment to cleanupStartVGPU documenting that
   the caller holds the instance lock before this function acquires
   vgpuAllocationMu.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Within a GPU the allocator now selects clean VFs before dirty ones, and
a dirty VF whose reset is refused is dropped from the candidates instead
of failing the claim. Log at error level when a failed create retains
its instance data because the vGPU claim could not be released, apply
the SIGTERM grace on the same vGPU predicate in the instance kill paths
as in the QEMU layer, and cover the unknown-load GPU ordering.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit fb132cd. Configure here.

Comment thread lib/instances/vgpu_reconcile.go
Comment thread lib/instances/vgpu.go
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