From 4bd958e5b2451c756cb4a378b0fdb3465168d5e3 Mon Sep 17 00:00:00 2001 From: yummybomb <19238148+yummybomb@users.noreply.github.com> Date: Thu, 30 Jul 2026 17:34:40 +0000 Subject: [PATCH] Parse creatable_vgpu_types in the format the driver emits The driver prints an "ID : vGPU Name" table, so the previous name-then-ID parse rejected every line and no profiles were enumerated. A GPU with no framebuffer left prints only the header, which now yields zero profiles instead of an error. --- lib/devices/vendor_vfio_linux.go | 17 ++++++++++++----- lib/devices/vendor_vfio_linux_test.go | 21 +++++++++++++++++---- 2 files changed, 29 insertions(+), 9 deletions(-) diff --git a/lib/devices/vendor_vfio_linux.go b/lib/devices/vendor_vfio_linux.go index a0107d7c..2c11ecf1 100644 --- a/lib/devices/vendor_vfio_linux.go +++ b/lib/devices/vendor_vfio_linux.go @@ -331,21 +331,28 @@ func (s vendorVFIOSysfs) vfioDeviceInUse(vfAddress string) (bool, error) { return false, nil } +// parseCreatableVGPUTypes reads the driver's creatable_vgpu_types listing: +// +// ID : vGPU Name +// 1147 : NVIDIA L40S-1Q func parseCreatableVGPUTypes(value string) ([]profileMetadata, error) { profiles := make([]profileMetadata, 0) for lineNumber, line := range strings.Split(value, "\n") { - fields := strings.Fields(line) - if len(fields) == 0 { + if strings.TrimSpace(line) == "" { continue } - if len(fields) < 2 { + typeID, name, found := strings.Cut(line, ":") + typeID = strings.TrimSpace(typeID) + name = strings.TrimSpace(name) + if !found || name == "" { return nil, fmt.Errorf("parse creatable vGPU types line %d: %q", lineNumber+1, line) } - typeID := fields[len(fields)-1] + if typeID == "ID" { + continue + } if _, err := strconv.Atoi(typeID); err != nil { return nil, fmt.Errorf("parse vGPU type ID %q: %w", typeID, err) } - name := strings.Join(fields[:len(fields)-1], " ") profiles = append(profiles, profileMetadata{ TypeName: typeID, Name: name, diff --git a/lib/devices/vendor_vfio_linux_test.go b/lib/devices/vendor_vfio_linux_test.go index 1a1ae3bd..3ae69683 100644 --- a/lib/devices/vendor_vfio_linux_test.go +++ b/lib/devices/vendor_vfio_linux_test.go @@ -12,9 +12,10 @@ import ( "github.com/stretchr/testify/require" ) -const testCreatableTypes = `NVIDIA L40S-1Q 1147 -NVIDIA L40S-2Q 1148 -NVIDIA L40S-48Q 1159 +const testCreatableTypes = `ID : vGPU Name +1147 : NVIDIA L40S-1Q +1148 : NVIDIA L40S-2Q +1159 : NVIDIA L40S-48Q ` func TestParseCreatableVGPUTypes(t *testing.T) { @@ -101,10 +102,22 @@ func TestVendorVFIOReconcilePreservesLegacyGroupFD(t *testing.T) { assertFileValue(t, filepath.Join(sysfs.pciDevicesPath, "0000:e3:00.4", "nvidia", "current_vgpu_type"), "1148") } +// A GPU with no framebuffer left prints the header and nothing else. +func TestParseCreatableVGPUTypesHeaderOnly(t *testing.T) { + t.Parallel() + + profiles, err := parseCreatableVGPUTypes("ID : vGPU Name\n") + require.NoError(t, err) + assert.Empty(t, profiles) +} + func TestParseCreatableVGPUTypesRejectsMalformedLine(t *testing.T) { t.Parallel() - _, err := parseCreatableVGPUTypes("NVIDIA L40S-1Q not-an-id") + _, err := parseCreatableVGPUTypes("NVIDIA L40S-1Q 1147") + require.Error(t, err) + + _, err = parseCreatableVGPUTypes("not-an-id : NVIDIA L40S-1Q") require.Error(t, err) }