Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for node labels to report GPU mode #768

Merged
merged 12 commits into from
Jun 27, 2024
Merged
1 change: 1 addition & 0 deletions docs/gpu-feature-discovery/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ their meaning:
| nvidia.com/gpu.machine | String | Machine type | DGX-1 |
| nvidia.com/gpu.memory | Integer | Memory of the GPU in Mb | 2048 |
| nvidia.com/gpu.product | String | Model of the GPU | GeForce-GT-710 |
| nvidia.com/gpu.mode | String | Display or Compute Mode of the GPU | compute |
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 provide more information on what this value means?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Agreed. Working on it

Copy link
Member

Choose a reason for hiding this comment

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

We still need to add a reference as to what this means. From the documentation it is also only applicable to specific device types. Do we want to link to the relevant documentation here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

FIxed


Depending on the MIG strategy used, the following set of labels may also be
available (or override the default values for some of the labels listed above):
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ go 1.22.2

require (
github.com/NVIDIA/go-gpuallocator v0.5.0
github.com/NVIDIA/go-nvlib v0.5.0
github.com/NVIDIA/go-nvlib v0.6.0
github.com/NVIDIA/go-nvml v0.12.4-0
github.com/NVIDIA/nvidia-container-toolkit v1.15.1-0.20240528113255-e4b46a09a77e
github.com/fsnotify/fsnotify v1.7.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7
github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
github.com/NVIDIA/go-gpuallocator v0.5.0 h1:166ICvPv2dU9oZ2J3kJ4y3XdbGCi6LhXgFZJtrqeu3A=
github.com/NVIDIA/go-gpuallocator v0.5.0/go.mod h1:zos5bTIN01hpQioOyu9oRKglrznImMQvm0bZllMmckw=
github.com/NVIDIA/go-nvlib v0.5.0 h1:951KGrfr+p3cs89alO9z/ZxPPWKxwht9tx9rxiADoLI=
github.com/NVIDIA/go-nvlib v0.5.0/go.mod h1:87z49ULPr4GWPSGfSIp3taU4XENRYN/enIg88MzcL4k=
github.com/NVIDIA/go-nvlib v0.6.0 h1:zAMBzCYT9xeyRQo0tb7HJbStkzajD6e5joyaQqJ2OGU=
github.com/NVIDIA/go-nvlib v0.6.0/go.mod h1:9UrsLGx/q1OrENygXjOuM5Ey5KCtiZhbvBlbUIxtGWY=
github.com/NVIDIA/go-nvml v0.12.4-0 h1:4tkbB3pT1O77JGr0gQ6uD8FrsUPqP1A/EOEm2wI1TUg=
github.com/NVIDIA/go-nvml v0.12.4-0/go.mod h1:8Llmj+1Rr+9VGGwZuRer5N/aCjxGuR5nPb/9ebBiIEQ=
github.com/NVIDIA/nvidia-container-toolkit v1.15.1-0.20240528113255-e4b46a09a77e h1:1xxK51xOXiqqziqMdgEMWVmjfvRu1b265LgQQZBqRaE=
Expand Down
64 changes: 64 additions & 0 deletions internal/lm/nvml.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
"strconv"
"strings"

"github.com/NVIDIA/go-nvlib/pkg/nvpci"

spec "github.com/NVIDIA/k8s-device-plugin/api/config/v1"
"github.com/NVIDIA/k8s-device-plugin/internal/resource"
)
Expand Down Expand Up @@ -71,12 +73,18 @@ func NewDeviceLabeler(manager resource.Manager, config *spec.Config) (Labeler, e
return nil, fmt.Errorf("error creating resource labeler: %v", err)
}

gpuModeLabeler, err := newGPUModeLabeler(manager)
if err != nil {
return nil, fmt.Errorf("error creating resource labeler: %v", err)
}

l := Merge(
machineTypeLabeler,
versionLabeler,
migCapabilityLabeler,
sharingLabeler,
resourceLabeler,
gpuModeLabeler,
)

return l, nil
Expand Down Expand Up @@ -193,3 +201,59 @@ func isMPSCapable(manager resource.Manager) (bool, error) {
}
return true, nil
}

func newGPUModeLabeler(manager resource.Manager) (Labeler, error) {
devices, err := manager.GetDevices()
if err != nil {
return nil, err
}
if len(devices) == 0 {
// no devices, return empty labels
return empty{}, nil
}
classes, err := getDeviceClasses(devices)
if err != nil {
return nil, err
}
gpuMode := getModeForClasses(classes)
labels := Labels{
"nvidia.com/gpu.mode": gpuMode,
Copy link
Member

Choose a reason for hiding this comment

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

Question: Since we also extract this label for a MIG device do we expect different labels per MIG profile?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Devices that support MIG do not support changing the GPU mode. They are always going to return compute PCI class. We can actually hard code the class of MIG devices to be compute.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, let's rather do that. It would simplify the implementation quite a bit.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the implementation

}
return labels, nil
}

func getModeForClasses(classes []uint32) string {
if len(classes) == 0 {
return "unknown"
}
for _, class := range classes {
if class != classes[0] {
return "unknown"
Copy link
Member

Choose a reason for hiding this comment

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

Not a blocker for this PR, but we may want to log the content of classes here as a warning.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added.

}
}
switch classes[0] {
case nvpci.PCIVgaControllerClass:
return "graphics"
case nvpci.PCI3dControllerClass:
return "compute"
default:
return "unknown"
}
}

func getDeviceClasses(devices []resource.Device) ([]uint32, error) {
seenClasses := make(map[uint32]bool)
for _, d := range devices {
class, err := d.GetPIEClass()
if err != nil {
return nil, err
Copy link
Member

Choose a reason for hiding this comment

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

Question: Do we want to treat errors in getting the class as fatal? This will crash GFD and cause NO labels to be generated. Should we rather return unknown as the label in this case?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to keep this behavior consistent with other labeler, that is the reason I am returning the error instead instead of labeling it unknown .

}
seenClasses[class] = true
}

var classes []uint32
for class := range seenClasses {
classes = append(classes, class)
}
return classes, nil
}
4 changes: 4 additions & 0 deletions internal/resource/cuda-device.go
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,7 @@ func (d *cudaDevice) IsMigCapable() (bool, error) {
func (d *cudaDevice) IsMigEnabled() (bool, error) {
return false, nil
}

func (d *cudaDevice) GetPIEClass() (uint32, error) {
return 0, nil
}
37 changes: 37 additions & 0 deletions internal/resource/device_mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions internal/resource/nvml-device.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"

"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
"github.com/NVIDIA/go-nvlib/pkg/nvpci"
"github.com/NVIDIA/go-nvml/pkg/nvml"
)

Expand Down Expand Up @@ -86,3 +87,15 @@ func (d nvmlDevice) GetTotalMemoryMB() (uint64, error) {
}
return info.Total / (1024 * 1024), nil
}

func (d nvmlDevice) GetPIEClass() (uint32, error) {
pciBusID, err := d.GetPCIBusID()
if err != nil {
return 0, err
}
nvDevice, err := nvpci.New().GetGPUByPciBusID(pciBusID)
if err != nil {
return 0, err
}
return nvDevice.Class, nil
}
21 changes: 21 additions & 0 deletions internal/resource/nvml-mig-device.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"strings"

"github.com/NVIDIA/go-nvlib/pkg/nvlib/device"
"github.com/NVIDIA/go-nvlib/pkg/nvpci"
"github.com/NVIDIA/go-nvml/pkg/nvml"
)

Expand Down Expand Up @@ -132,3 +133,23 @@ func totalMemory(attr map[string]interface{}) (uint64, error) {
return 0, fmt.Errorf("unsupported attribute type %v", t)
}
}

func (d nvmlMigDevice) GetPIEClass() (uint32, error) {
info, retVal := d.MigDevice.GetPciInfo()
Copy link
Member

Choose a reason for hiding this comment

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

Question: Is PCI info valid for a MIG device? How does the busid differ from that of the parent?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is the busID of the parent.

if retVal != nvml.SUCCESS {
return 0, retVal
}
var bytes []byte
for _, char := range info.BusId {
if char == 0 {
break
}
bytes = append(bytes, byte(char))
}
Copy link
Member

Choose a reason for hiding this comment

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

Instead of implemention this here this should be pulled into go-nvlib if it is valid.

Copy link
Member

Choose a reason for hiding this comment

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

Happy to do this in a follow-up though.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can decide where to put this behavior once we decide what behavior is expected for MIG devices.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Because of the hardcoding of the PCI class for MIG devices this code is removed

pciID := strings.ToLower(strings.TrimPrefix(string(bytes), "0000"))
nvDevice, err := nvpci.New().GetGPUByPciBusID(pciID)
if err != nil {
return 0, err
}
return nvDevice.Class, nil
}
4 changes: 4 additions & 0 deletions internal/resource/sysfs-device.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,7 @@ func (d vfioDevice) IsMigEnabled() (bool, error) {
func (d vfioDevice) IsMigCapable() (bool, error) {
return false, nil
}

func (d vfioDevice) GetPIEClass() (uint32, error) {
return d.nvidiaPCIDevice.Class, nil
}
1 change: 1 addition & 0 deletions internal/resource/testing/resource-testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ func NewDeviceMock(migEnabled bool) *DeviceMock {
IsMigEnabledFunc: func() (bool, error) { return migEnabled, nil },
IsMigCapableFunc: func() (bool, error) { return migEnabled, nil },
GetMigDevicesFunc: func() ([]resource.Device, error) { return nil, nil },
GetPIEClassFunc: func() (uint32, error) { return 0x030000, nil },
}}
return &d
}
Expand Down
1 change: 1 addition & 0 deletions internal/resource/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ type Device interface {
GetTotalMemoryMB() (uint64, error)
GetDeviceHandleFromMigDeviceHandle() (Device, error)
GetCudaComputeCapability() (int, int, error)
GetPIEClass() (uint32, error)
}
1 change: 1 addition & 0 deletions tests/expected-output-mig-mixed.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nvidia\.com\/gpu\.sharing-strategy=[none|mps|time-slicing]
nvidia\.com\/gpu\.product=[A-Za-z_-]+
nvidia\.com\/gpu\.memory=[0-9]+
nvidia\.com\/gpu\.family=[a-z]+
nvidia\.com\/gpu\.mode=[unknown|compute|graphics]
nvidia\.com\/mig\.capable=[true|false]
nvidia\.com\/gpu\.compute\.major=[0-9]+
nvidia\.com\/gpu\.compute\.minor=[0-9]+
Expand Down
1 change: 1 addition & 0 deletions tests/expected-output-mig-none.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nvidia\.com\/gpu\.sharing-strategy=[none|mps|time-slicing]
nvidia\.com\/gpu\.product=[A-Za-z_-]+
nvidia\.com\/gpu\.memory=[0-9]+
nvidia\.com\/gpu\.family=[a-z]+
nvidia\.com\/gpu\.mode=[unknown|compute|graphics]
nvidia\.com\/mig\.capable=[true|false]
nvidia\.com\/gpu\.compute\.major=[0-9]+
nvidia\.com\/gpu\.compute\.minor=[0-9]+
Expand Down
1 change: 1 addition & 0 deletions tests/expected-output-mig-single.txt
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ nvidia\.com\/gpu\.engines\.jpeg=[0-9]+
nvidia\.com\/gpu\.engines\.ofa=[0-9]+
nvidia\.com\/gpu\.slices\.gi=[0-9]+
nvidia\.com\/gpu\.slices\.ci=[0-9]+
nvidia\.com\/gpu\.mode=[unknown|compute|graphics]
Copy link
Member

Choose a reason for hiding this comment

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

nit: this can only be compute now, correct? Not a blocker.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

nvidia\.com\/mps\.capable=[true|false]
1 change: 1 addition & 0 deletions tests/expected-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ nvidia\.com\/gpu\.sharing-strategy=[none|mps|time-slicing]
nvidia\.com\/gpu\.product=[A-Za-z_-]+
nvidia\.com\/gpu\.memory=[0-9]+
nvidia\.com\/gpu\.family=[a-z]+
nvidia\.com\/gpu\.mode=[unknown|compute|graphics]
nvidia\.com\/mig\.capable=[true|false]
nvidia\.com\/gpu\.compute\.major=[0-9]+
nvidia\.com\/gpu\.compute\.minor=[0-9]+
Expand Down
25 changes: 25 additions & 0 deletions vendor/github.com/NVIDIA/go-nvlib/pkg/nvlib/device/device.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading