Skip to content

Commit 2ae1fa0

Browse files
authored
Merge pull request #104 from vcr1311/vikash_rai_dev
feat: add pass-device-specs flag for volcano-vgpu-device-plugin
2 parents df5343d + 20ee968 commit 2ae1fa0

3 files changed

Lines changed: 119 additions & 0 deletions

File tree

cmd/vgpu/main.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,10 @@ func init() {
6868
rootCmd.Flags().UintVar(&config.DeviceSplitCount, "device-split-count", 2, "the number for NVIDIA device split")
6969
rootCmd.Flags().UintVar(&config.GPUMemoryFactor, "gpu-memory-factor", 1, "the default gpu memory block size is 1MB")
7070
rootCmd.Flags().Float64Var(&config.DeviceCoresScaling, "device-cores-scaling", 1.0, "the ratio for NVIDIA device cores scaling")
71+
// Add pass-device-specs flag to enable explicit device mounting via kubelet Device Plugin API
72+
// When enabled, device files (/dev/nvidia*, /dev/nvidiactl, etc.) are mounted directly by kubelet
73+
// without requiring nvidia-container-runtime, making it compatible with standard OCI runtimes
74+
rootCmd.Flags().BoolVar(&config.PassDeviceSpecs, "pass-device-specs", false, "pass the list of DeviceSpecs to the kubelet on Allocate()")
7175
rootCmd.Flags().StringVar(&config.NodeName, "node-name", viper.GetString("node-name"), "node name")
7276

7377
rootCmd.PersistentFlags().AddGoFlagSet(util.GlobalFlagSet())

pkg/plugin/vgpu/config/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ type NvidiaConfig struct {
3939
DisableCoreLimit bool `yaml:"disableCoreLimit"`
4040
MigGeometriesList []AllowedMigGeometries `yaml:"knownMigGeometries"`
4141
GPUMemoryFactor uint `yaml:"gpuMemoryFactor"`
42+
// PassDeviceSpecs enables explicit device mounting via kubelet Device Plugin API
43+
// When true, device files are passed to kubelet via DeviceSpec, allowing containers
44+
// to access GPU devices without requiring nvidia-container-runtime
45+
PassDeviceSpecs bool `yaml:"passDeviceSpecs"`
4246
}
4347

4448
var (
@@ -77,6 +81,10 @@ var (
7781
NodeName string
7882
RuntimeSocketFlag string
7983
DisableCoreLimit bool
84+
// PassDeviceSpecs controls whether GPU device files are explicitly passed to kubelet
85+
// via the DeviceSpec field in ContainerAllocateResponse, enabling GPU access without
86+
// nvidia-container-runtime (compatible with standard OCI runtimes like containerd/docker)
87+
PassDeviceSpecs bool
8088
)
8189

8290
type MigTemplate struct {

pkg/plugin/vgpu/plugin.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -509,6 +509,16 @@ func (m *NvidiaDevicePlugin) Allocate(ctx context.Context, reqs *pluginapi.Alloc
509509
ReadOnly: true},
510510
)
511511
}
512+
513+
// If pass-device-specs is enabled, explicitly mount GPU device nodes via kubelet
514+
// This allows containers to access GPU devices without requiring nvidia-container-runtime
515+
// making it compatible with standard OCI runtimes (containerd, docker, etc.)
516+
if config.PassDeviceSpecs {
517+
deviceSpecs := m.GetDeviceSpecs(devreq)
518+
response.Devices = append(response.Devices, deviceSpecs...)
519+
klog.V(3).Infof("Added %d device specs to allocation response for pod %s",
520+
len(deviceSpecs), current.Name)
521+
}
512522
}
513523
responses.ContainerResponses = append(responses.ContainerResponses, &response)
514524
}
@@ -662,6 +672,103 @@ func (m *NvidiaDevicePlugin) GetContainerDeviceStrArray(c util.ContainerDevices)
662672
return tmp
663673
}
664674

675+
// GetDeviceSpecs returns a list of pluginapi.DeviceSpec for the given container devices
676+
// This method is used when PassDeviceSpecs is enabled to explicitly mount GPU device nodes
677+
// via kubelet's Device Plugin API, enabling GPU access without nvidia-container-runtime
678+
func (m *NvidiaDevicePlugin) GetDeviceSpecs(containerDevices util.ContainerDevices) []*pluginapi.DeviceSpec {
679+
// Define optional control devices that should be checked for existence before adding
680+
// These devices may not be present on all systems
681+
optionalDevices := map[string]bool{
682+
"/dev/nvidiactl": true,
683+
"/dev/nvidia-uvm": true,
684+
"/dev/nvidia-uvm-tools": true,
685+
"/dev/nvidia-modeset": true,
686+
}
687+
688+
var deviceSpecs []*pluginapi.DeviceSpec
689+
devicePathsMap := make(map[string]bool) // Track unique paths to avoid duplicates
690+
691+
// Get all available devices from the cache to lookup device paths by UUID
692+
var allDevices []*Device
693+
if m.migStrategy == "none" {
694+
allDevices = m.deviceCache.GetCache()
695+
} else if m.migStrategy == "mixed" {
696+
allDevices = m.cachedDevices
697+
}
698+
699+
// For each requested device, find its Device object and extract paths
700+
for _, containerDevice := range containerDevices {
701+
deviceUUID := containerDevice.UUID
702+
703+
// Handle MIG devices (UUIDs contain "[")
704+
if strings.Contains(deviceUUID, "[") {
705+
// For MIG devices, get the actual MIG UUID after template generation
706+
devtype, devindex := util.GetIndexAndTypeFromUUID(deviceUUID)
707+
position, needsReset := m.GenerateMigTemplate(devtype, devindex, containerDevice)
708+
if needsReset {
709+
m.ApplyMigTemplate()
710+
}
711+
deviceUUID = util.GetMigUUIDFromIndex(deviceUUID, position)
712+
}
713+
714+
// Find the Device object matching this UUID
715+
for _, device := range allDevices {
716+
if device.ID == deviceUUID {
717+
// Add all paths from this device
718+
for _, path := range device.Paths {
719+
if !devicePathsMap[path] {
720+
devicePathsMap[path] = true
721+
spec := &pluginapi.DeviceSpec{
722+
ContainerPath: path,
723+
HostPath: path, // Use same path for both container and host
724+
Permissions: "rw",
725+
}
726+
deviceSpecs = append(deviceSpecs, spec)
727+
klog.V(4).Infof("Added device spec for GPU device: %s", path)
728+
}
729+
}
730+
break
731+
}
732+
}
733+
}
734+
735+
// Add control devices (nvidiactl, nvidia-uvm, etc.) that are shared across all GPUs
736+
// These are required for CUDA to function properly
737+
controlDevicePaths := []string{
738+
"/dev/nvidiactl",
739+
"/dev/nvidia-uvm",
740+
"/dev/nvidia-uvm-tools",
741+
"/dev/nvidia-modeset",
742+
}
743+
744+
for _, path := range controlDevicePaths {
745+
// Skip if already added
746+
if devicePathsMap[path] {
747+
continue
748+
}
749+
750+
// For optional devices, check if they exist on the host before adding
751+
if optionalDevices[path] {
752+
if _, err := os.Stat(path); err != nil {
753+
klog.V(4).Infof("Skipping optional device %s: not present on host", path)
754+
continue
755+
}
756+
}
757+
758+
devicePathsMap[path] = true
759+
spec := &pluginapi.DeviceSpec{
760+
ContainerPath: path,
761+
HostPath: path,
762+
Permissions: "rw",
763+
}
764+
deviceSpecs = append(deviceSpecs, spec)
765+
klog.V(4).Infof("Added device spec for control device: %s", path)
766+
}
767+
768+
klog.V(3).Infof("Generated %d device specs for container", len(deviceSpecs))
769+
return deviceSpecs
770+
}
771+
665772
func (m *NvidiaDevicePlugin) GenerateMigTemplate(devtype string, devindex int, val util.ContainerDevice) (int, bool) {
666773
needsreset := false
667774
position := -1 // Initialize to an invalid position

0 commit comments

Comments
 (0)