Skip to content

Commit bccbbb9

Browse files
committed
Adapt vGPU liveness guards to the identity struct resolver
The claim guard and startup reconcile protection predate the HypervisorProcessIdentity struct and the removal of the standalone identity-exists helpers. Route both through resolveLiveHypervisorPID: the claim guard keeps failing closed on unresolvable ownership, and reconcile protection gets a fail-open HypervisorMayBeAlive wrapper so unresolvable ownership still protects the device.
1 parent de7e8f8 commit bccbbb9

7 files changed

Lines changed: 44 additions & 34 deletions

File tree

cmd/api/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func liveInstanceVGPUDevicePaths(ctx context.Context, instanceManager instances.
196196
if inst.GPUDevicePath == "" {
197197
continue
198198
}
199-
if inst.HypervisorPID != nil && instances.HypervisorProcessIdentityExists(*inst.HypervisorPID, inst.HypervisorStartTime, inst.HypervisorBootID, inst.SocketPath) {
199+
if inst.HypervisorPID != nil && instances.HypervisorMayBeAlive(inst.HypervisorProcessIdentity, inst.SocketPath) {
200200
protected[inst.GPUDevicePath] = struct{}{}
201201
continue
202202
}

cmd/api/main_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,8 @@ func TestLiveInstanceVGPUDevicePathsBoundsStartupProtection(t *testing.T) {
362362
{StoredMetadata: instances.StoredMetadata{Id: "booting", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4", GPUAssignedAt: &recent}},
363363
{StoredMetadata: instances.StoredMetadata{Id: "orphaned", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.5", GPUAssignedAt: &stale}},
364364
{StoredMetadata: instances.StoredMetadata{Id: "legacy", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.6"}},
365-
{StoredMetadata: instances.StoredMetadata{Id: "dead", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.7", HypervisorPID: &deadPID}},
366-
{StoredMetadata: instances.StoredMetadata{Id: "stale-pid-booting", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.8", HypervisorPID: &deadPID, GPUAssignedAt: &recent}},
365+
{StoredMetadata: instances.StoredMetadata{Id: "dead", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.7", HypervisorProcessIdentity: instances.HypervisorProcessIdentity{HypervisorPID: &deadPID}}},
366+
{StoredMetadata: instances.StoredMetadata{Id: "stale-pid-booting", GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.8", HypervisorProcessIdentity: instances.HypervisorProcessIdentity{HypervisorPID: &deadPID}, GPUAssignedAt: &recent}},
367367
}}
368368

369369
protected, retryAfter, err := liveInstanceVGPUDevicePaths(context.Background(), manager)

lib/instances/lifecycle_noop_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -258,17 +258,17 @@ func TestDeleteDropsStaleVGPUClaimedByLiveInstance(t *testing.T) {
258258
require.NoError(t, err)
259259
defer listener.Close()
260260
require.NoError(t, m.saveMetadata(&metadata{StoredMetadata: StoredMetadata{
261-
Id: claimantID,
262-
Name: claimantID,
263-
Image: "test-image",
264-
CreatedAt: now,
265-
HypervisorType: lifecycleNoopHypervisorType,
266-
HypervisorPID: &pid,
267-
SocketPath: socketPath,
268-
DataDir: m.paths.InstanceDir(claimantID),
269-
GPUProfile: "NVIDIA L40S-2Q",
270-
GPUFramework: devices.VGPUFrameworkVendorVFIO,
271-
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
261+
Id: claimantID,
262+
Name: claimantID,
263+
Image: "test-image",
264+
CreatedAt: now,
265+
HypervisorType: lifecycleNoopHypervisorType,
266+
HypervisorProcessIdentity: HypervisorProcessIdentity{HypervisorPID: &pid},
267+
SocketPath: socketPath,
268+
DataDir: m.paths.InstanceDir(claimantID),
269+
GPUProfile: "NVIDIA L40S-2Q",
270+
GPUFramework: devices.VGPUFrameworkVendorVFIO,
271+
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
272272
}}))
273273

274274
require.NoError(t, m.DeleteInstance(context.Background(), id))

lib/instances/process_identity.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,16 @@ func classifyResolvedHypervisorOwner(socketPath string, stored, resolved int, er
162162
return 0, fmt.Errorf("cannot confirm ownership of socket %s: %w", socketPath, err)
163163
}
164164

165+
// HypervisorMayBeAlive reports whether the recorded hypervisor process may
166+
// still be running. It fails open: when ownership cannot be resolved it
167+
// returns true, which is the safe direction for its callers (reconcile
168+
// protection and claim checks, where true means "protect"). Do not use it to
169+
// authorize teardown.
170+
func HypervisorMayBeAlive(id HypervisorProcessIdentity, socketPath string) bool {
171+
pid, err := resolveLiveHypervisorPID(id, socketPath)
172+
return err != nil || pid > 0
173+
}
174+
165175
// ProcessExists reports whether pid belongs to a live, non-zombie process.
166176
func ProcessExists(pid int) bool {
167177
if pid <= 0 {

lib/instances/process_identity_linux_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ func TestRefreshHypervisorPIDResolvesSocketOwnerWhenStoredPIDIsDead(t *testing.T
435435

436436
func TestVGPUAssignmentClaimedByLiveInstanceProtectsReusedPIDClaim(t *testing.T) {
437437
socketPath := filepath.Join(t.TempDir(), "test.sock")
438-
owner := exec.Command(os.Args[0], "-test.run=^TestHypervisorProcessExistsWithReboundSocketPathHelper$")
438+
owner := exec.Command(os.Args[0], "-test.run=^TestSocketListenerHelper$")
439439
owner.Env = append(os.Environ(), "HYPERVISOR_SOCKET_HELPER=1", "HYPERVISOR_SOCKET_PATH="+socketPath)
440440
stdin, err := owner.StdinPipe()
441441
require.NoError(t, err)
@@ -462,11 +462,11 @@ func TestVGPUAssignmentClaimedByLiveInstanceProtectsReusedPIDClaim(t *testing.T)
462462
stalePID := stale.Process.Pid
463463
require.NoError(t, m.ensureDirectories("live-claimant"))
464464
require.NoError(t, m.saveMetadata(&metadata{StoredMetadata: StoredMetadata{
465-
Id: "live-claimant",
466-
GPUFramework: devices.VGPUFrameworkVendorVFIO,
467-
GPUDevicePath: devicePath,
468-
HypervisorPID: &stalePID,
469-
SocketPath: socketPath,
465+
Id: "live-claimant",
466+
GPUFramework: devices.VGPUFrameworkVendorVFIO,
467+
GPUDevicePath: devicePath,
468+
HypervisorProcessIdentity: HypervisorProcessIdentity{HypervisorPID: &stalePID},
469+
SocketPath: socketPath,
470470
}}))
471471

472472
claimed, err := m.vgpuAssignmentClaimedByLiveInstance(context.Background(), "other-instance", devicePath)

lib/instances/vgpu.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ func (m *manager) vgpuAssignmentClaimedByLiveInstance(ctx context.Context, exclu
201201
}
202202
return false, fmt.Errorf("cannot confirm liveness of recent vGPU claimant %s on %s: no persisted hypervisor PID", id, devicePath)
203203
}
204-
pid, err := resolveLiveHypervisorPID(stored.HypervisorPID, stored.HypervisorStartTime, stored.HypervisorBootID, stored.SocketPath)
204+
pid, err := resolveLiveHypervisorPID(stored.HypervisorProcessIdentity, stored.SocketPath)
205205
if err != nil {
206206
return false, fmt.Errorf("cannot confirm liveness of vGPU claimant %s on %s: %w", id, devicePath, err)
207207
}

lib/instances/vgpu_test.go

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -483,10 +483,10 @@ func TestVGPUAssignmentClaimedByLiveInstanceNormalizesLegacyMdevPath(t *testing.
483483
require.NoError(t, m.ensureDirectories("legacy-claimant"))
484484
pid := os.Getpid()
485485
require.NoError(t, m.saveMetadata(&metadata{StoredMetadata: StoredMetadata{
486-
Id: "legacy-claimant",
487-
Name: "legacy-claimant",
488-
GPUMdevUUID: "legacy-uuid",
489-
HypervisorPID: &pid,
486+
Id: "legacy-claimant",
487+
Name: "legacy-claimant",
488+
GPUMdevUUID: "legacy-uuid",
489+
HypervisorProcessIdentity: HypervisorProcessIdentity{HypervisorPID: &pid},
490490
}}))
491491

492492
claimed, err := m.vgpuAssignmentClaimedByLiveInstance(context.Background(), "other-instance", "/sys/bus/mdev/devices/legacy-uuid")
@@ -538,11 +538,11 @@ func TestVGPUAssignmentClaimedByLiveInstanceGracesRecentDeadPIDClaim(t *testing.
538538
require.False(t, ProcessExists(deadPID))
539539
assignedAt := time.Now().UTC()
540540
require.NoError(t, m.saveMetadata(&metadata{StoredMetadata: StoredMetadata{
541-
Id: claimantID,
542-
HypervisorPID: &deadPID,
543-
GPUFramework: devices.VGPUFrameworkVendorVFIO,
544-
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
545-
GPUAssignedAt: &assignedAt,
541+
Id: claimantID,
542+
HypervisorProcessIdentity: HypervisorProcessIdentity{HypervisorPID: &deadPID},
543+
GPUFramework: devices.VGPUFrameworkVendorVFIO,
544+
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
545+
GPUAssignedAt: &assignedAt,
546546
}}))
547547

548548
// Same bounded grace as startup reconcile: a recent claim whose PID is
@@ -569,10 +569,10 @@ func TestVGPUAssignmentClaimedByLiveInstanceIgnoresDeadClaim(t *testing.T) {
569569
require.NoError(t, m.ensureDirectories("dead-claimant"))
570570
deadPID := 1 << 30
571571
require.NoError(t, m.saveMetadata(&metadata{StoredMetadata: StoredMetadata{
572-
Id: "dead-claimant",
573-
Name: "dead-claimant",
574-
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
575-
HypervisorPID: &deadPID,
572+
Id: "dead-claimant",
573+
Name: "dead-claimant",
574+
GPUDevicePath: "/sys/bus/pci/devices/0000:82:00.4",
575+
HypervisorProcessIdentity: HypervisorProcessIdentity{HypervisorPID: &deadPID},
576576
}}))
577577

578578
claimed, err := m.vgpuAssignmentClaimedByLiveInstance(context.Background(), "other-instance", "/sys/bus/pci/devices/0000:82:00.4")

0 commit comments

Comments
 (0)