Skip to content

Conversation

@cdesiniotis
Copy link
Collaborator

@cdesiniotis cdesiniotis commented Nov 11, 2025

Rather than always binding GPUs to the vfio-pci driver, this commit
introduces logic to see if the running kernel has a VFIO variant
driver available that is a better match for the device. This is required
on Grace-based systems where the nvgrace_gpu_vfio_pci module is required
to be used in favor of the vfio-pci module.

We read the mod.alias file for a given device, then we look through
/lib/modules/${kernel_version}/modules.alias for the vfio_pci alias
that matches with the least number of wildcard ('*') fields.

The code introduced in this commit is inspired by:

https://gitlab.com/libvirt/libvirt/-/commit/82e2fac297105f554f57fb589002933231b4f711

Depends on #127

Testing

On a GB200 compute tray:

# cat /sys/bus/pci/devices/0019\:01\:00.0/modalias 
pci:v000010DEd00002941sv000010DEsd00002046bc03sc02i00

# grep "v000010DEd00002941" /lib/modules/$(uname -r)/modules.alias
alias vfio_pci:v000010DEd00002941sv*sd*bc*sc*i* nvgrace_gpu_vfio_pci

# ./vfio-manage bind -a
INFO[2025-11-25T01:56:36Z] Binding device 0008:01:00.0                  
INFO[2025-11-25T01:56:36Z] Binding device 0008:01:00.0 to driver: nvgrace_gpu_vfio_pci 
INFO[2025-11-25T01:56:36Z] Binding device 0009:01:00.0                  
INFO[2025-11-25T01:56:36Z] Binding device 0009:01:00.0 to driver: nvgrace_gpu_vfio_pci 
INFO[2025-11-25T01:56:36Z] Binding device 0018:01:00.0                  
INFO[2025-11-25T01:56:36Z] Binding device 0018:01:00.0 to driver: nvgrace_gpu_vfio_pci 
INFO[2025-11-25T01:56:36Z] Binding device 0019:01:00.0                  
INFO[2025-11-25T01:56:36Z] Binding device 0019:01:00.0 to driver: nvgrace_gpu_vfio_pci 

# lspci -nnk -d 10de:2941
0008:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel driver in use: nvgrace_gpu_vfio_pci
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0009:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel driver in use: nvgrace_gpu_vfio_pci
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0018:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel driver in use: nvgrace_gpu_vfio_pci
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0019:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel driver in use: nvgrace_gpu_vfio_pci
        Kernel modules: nvidiafb, nvidia_drm, nvidia
        
# ./vfio-manage unbind -a
INFO[2025-11-25T01:59:56Z] Unbinding device 0008:01:00.0                
INFO[2025-11-25T01:59:56Z] Unbinding device 0009:01:00.0                
INFO[2025-11-25T01:59:56Z] Unbinding device 0018:01:00.0                
INFO[2025-11-25T01:59:56Z] Unbinding device 0019:01:00.0

# lspci -nnk -d 10de:2941
0008:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0009:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0018:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel modules: nvidiafb, nvidia_drm, nvidia
0019:01:00.0 3D controller [0302]: NVIDIA Corporation Device [10de:2941] (rev a1)
        Subsystem: NVIDIA Corporation Device [10de:2046]
        Kernel modules: nvidiafb, nvidia_drm, nvidia

On a system with one L40 (configured in graphics mode) and one L4 GPU:

# ./vfio-manage bind --all
INFO[2025-11-25T02:00:35Z] Binding device 0000:17:00.0                  
INFO[2025-11-25T02:00:35Z] Binding device 0000:17:00.0 to driver: vfio-pci 
INFO[2025-11-25T02:00:35Z] Binding graphics auxiliary device 0000:17:00.1 to driver: vfio-pci 
INFO[2025-11-25T02:00:35Z] Binding device 0000:3d:00.0                  
INFO[2025-11-25T02:00:35Z] Binding device 0000:3d:00.0 to driver: vfio-pci

# lspci -nnk -d 10de:
17:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102GL [L40] [10de:26b5] (rev a1)
        Subsystem: NVIDIA Corporation AD102GL [L40] [10de:169d]
        Kernel driver in use: vfio-pci
        Kernel modules: nvidiafb, nouveau
17:00.1 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1)
        Subsystem: NVIDIA Corporation AD102 High Definition Audio Controller [10de:169d]
        Kernel driver in use: vfio-pci
        Kernel modules: snd_hda_intel
3d:00.0 3D controller [0302]: NVIDIA Corporation AD104GL [L4] [10de:27b8] (rev a1)
        Subsystem: NVIDIA Corporation AD104GL [L4] [10de:16ca]
        Kernel driver in use: vfio-pci
        Kernel modules: nvidiafb, nouveau

# ./vfio-manage unbind --all
INFO[2025-11-25T02:01:08Z] Unbinding device 0000:17:00.0                
INFO[2025-11-25T02:01:08Z] Unbinding device 0000:3d:00.0 

# lspci -nnk -d 10de:
17:00.0 VGA compatible controller [0300]: NVIDIA Corporation AD102GL [L40] [10de:26b5] (rev a1)
        Subsystem: NVIDIA Corporation AD102GL [L40] [10de:169d]
        Kernel modules: nvidiafb, nouveau
17:00.1 Audio device [0403]: NVIDIA Corporation AD102 High Definition Audio Controller [10de:22ba] (rev a1)
        Subsystem: NVIDIA Corporation AD102 High Definition Audio Controller [10de:169d]
        Kernel modules: snd_hda_intel
3d:00.0 3D controller [0302]: NVIDIA Corporation AD104GL [L4] [10de:27b8] (rev a1)
        Subsystem: NVIDIA Corporation AD104GL [L4] [10de:16ca]
        Kernel modules: nvidiafb, nouveau

@coveralls
Copy link

coveralls commented Nov 11, 2025

Pull Request Test Coverage Report for Build 19655564782

Details

  • 90 of 221 (40.72%) changed or added relevant lines in 4 files are covered.
  • 3 unchanged lines in 1 file lost coverage.
  • Overall coverage increased (+6.9%) to 6.907%

Changes Missing Coverage Covered Lines Changed/Added Lines %
cmd/vfio-manage/bind.go 0 4 0.0%
cmd/vfio-manage/unbind.go 0 4 0.0%
internal/nvpci/modalias.go 90 130 69.23%
internal/nvpci/nvpci.go 0 83 0.0%
Files with Coverage Reduction New Missed Lines %
internal/nvpci/nvpci.go 3 0.0%
Totals Coverage Status
Change from base Build 19587013754: 6.9%
Covered Lines: 90
Relevant Lines: 1303

💛 - Coveralls

@cdesiniotis cdesiniotis force-pushed the support-variant-vfio-modules branch 2 times, most recently from fcfa6cf to 7829c7f Compare November 18, 2025 18:48
Comment on lines 208 to 256
modAliasPath := filepath.Join(d.Path, "modalias")
modAliasContent, err := os.ReadFile(modAliasPath)
if err != nil {
return "", fmt.Errorf("failed to read modalias file for %s: %w", d.Address, err)
}

modAliasStr := strings.TrimSpace(string(modAliasContent))
modAlias, err := parseModAliasString(modAliasStr)
if err != nil {
return "", fmt.Errorf("failed to parse modalias string %q for device %q: %w", modAliasStr, d.Address, err)
}
logrus.Debugf("modalias for device %q: %+v", d.Address, modAlias)

kernelVersion, err := getKernelVersion()
if err != nil {
return "", fmt.Errorf("failed to get kernel version: %w", err)
}
logrus.Debugf("kernel version: %s", kernelVersion)

modulesAliasFilePath := filepath.Join("/lib/modules", kernelVersion, "modules.alias")
Copy link
Member

Choose a reason for hiding this comment

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

Could we extract our file paths into constants?

const (
	kernelModulesRoot = "/lib/modules"
	modulesAliasFileName = "modules.alias"
)

We can also create helper functions to create the paths:

func getModulesAliasPath(kernelVersion string) string {
	return filepath.Join(kernelModulesRoot, kernelVersion, modulesAliasFileName)
}

func getDeviceModaliasPath(devicePath string) string {
	return filepath.Join(devicePath, "modalias")
}

This way, callers don't need to know the exact path structure.

Comment on lines 203 to 261
if matches, score := matchField(deviceModAlias.vendor, patternModAlias.vendor); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.device, patternModAlias.device); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.subvendor, patternModAlias.subvendor); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.subdevice, patternModAlias.subdevice); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.baseClass, patternModAlias.baseClass); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.subClass, patternModAlias.subClass); !matches {
return false, 0
} else {
specificity += score
}

if matches, score := matchField(deviceModAlias.interface_, patternModAlias.interface_); !matches {
return false, 0
} else {
specificity += score
}

return true, specificity
Copy link
Member

Choose a reason for hiding this comment

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

Would it make sense to use slices of getters here to shorten this code?

type fieldGetter func(*modAlias) string

fields := []struct {
    getter fieldGetter
}{
    {func(m *modAlias) string { return m.vendor }},
    {func(m *modAlias) string { return m.device }},
    // ... etc
}

for _, field := range fields {
    deviceVal := field.getter(deviceModAlias)
    patternVal := field.getter(patternModAlias)
    if matches, score := matchField(deviceVal, patternVal); !matches {
        return false, 0
    }
    specificity += score
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I have updated the implementation significantly. I believe it is simpler now. Let me know what you think.

for _, line := range lines {
line = strings.TrimSpace(line)

if !strings.HasPrefix(line, "alias vfio_pci:") {
Copy link
Member

Choose a reason for hiding this comment

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

Can we extract this to a named constant?

@cdesiniotis cdesiniotis marked this pull request as ready for review November 18, 2025 21:42
@cdesiniotis cdesiniotis force-pushed the support-variant-vfio-modules branch 2 times, most recently from ff28e1c to b5da7d9 Compare November 22, 2025 00:28
@cdesiniotis cdesiniotis changed the title wip: update vfio-manage to choose best VFIO driver Update vfio-manage to choose best VFIO driver Nov 22, 2025
@cdesiniotis cdesiniotis force-pushed the support-variant-vfio-modules branch from b5da7d9 to a95b741 Compare November 25, 2025 01:51
Rather than always binding GPUs to the vfio-pci driver, this commit
introduces logic to see if the running kernel has a VFIO variant
driver available that is a better match for the device. This is required
on Grace-based systems where the nvgrace_gpu_vfio_pci module is required
to be used in favor of the vfio-pci module.

We read the mod.alias file for a given device, then we look through
/lib/modules/${kernel_version}/modules.alias for the vfio_pci alias
that matches with the least number of wildcard ('*') fields.

The code introduced in this commit is inspired by:

https://gitlab.com/libvirt/libvirt/-/commit/82e2fac297105f554f57fb589002933231b4f711

Signed-off-by: Christopher Desiniotis <[email protected]>
@cdesiniotis cdesiniotis force-pushed the support-variant-vfio-modules branch from a95b741 to ad775b1 Compare November 25, 2025 01:54
// extractField extracts the value before the next delimiter from the input string.
// Returns the extracted value, the remaining string (without the delimiter), and any error.
func extractField(input, delimiter string) (string, string, error) {
idx := strings.Index(input, delimiter)
Copy link
Contributor

Choose a reason for hiding this comment

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

There might be other methods in the strings package you could use to simplify this further

subdevice string // sd
baseClass string // bc
subClass string // sc
interface_ string // i
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
interface_ string // i
pciDevInterface string // i

OR

Suggested change
interface_ string // i
deviceInterface string // i

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.

5 participants