From eda63274cd16d12fb4824e2f0304834645e75799 Mon Sep 17 00:00:00 2001 From: Evan Lezar Date: Fri, 28 Nov 2025 16:45:22 +0100 Subject: [PATCH] Resolve a tegra platform for a single iGPU Instead of requiring that all GPUs on an Tegra platform with NVML be iGPUs, we resolve it as a tegra platform if at least one device is an iGPU. Signed-off-by: Evan Lezar --- pkg/nvlib/info/api.go | 6 +- pkg/nvlib/info/property-extractor.go | 36 ++--- pkg/nvlib/info/property-extractor_mock.go | 154 ++++++---------------- pkg/nvlib/info/resolver.go | 6 +- pkg/nvlib/info/resolver_test.go | 28 ++-- 5 files changed, 78 insertions(+), 152 deletions(-) diff --git a/pkg/nvlib/info/api.go b/pkg/nvlib/info/api.go index bcc9cb6..48abff5 100644 --- a/pkg/nvlib/info/api.go +++ b/pkg/nvlib/info/api.go @@ -35,9 +35,5 @@ type PropertyExtractor interface { HasDXCore() (bool, string) HasNvml() (bool, string) HasTegraFiles() (bool, string) - // Deprecated: Use HasTegraFiles instead. - IsTegraSystem() (bool, string) - // Deprecated: Use HasOnlyIntegratedGPUs - UsesOnlyNVGPUModule() (bool, string) - HasOnlyIntegratedGPUs() (bool, string) + HasAnIntegratedGPU() (bool, string) } diff --git a/pkg/nvlib/info/property-extractor.go b/pkg/nvlib/info/property-extractor.go index 9d8e6de..3ef582d 100644 --- a/pkg/nvlib/info/property-extractor.go +++ b/pkg/nvlib/info/property-extractor.go @@ -90,25 +90,20 @@ func (i *propertyExtractor) HasTegraFiles() (bool, string) { return false, fmt.Sprintf("%v has no 'tegra' prefix", tegraFamilyFile) } -// UsesOnlyNVGPUModule checks whether the only the nvgpu module is used. -// -// Deprecated: UsesOnlyNVGPUModule is deprecated, use HasOnlyIntegratedGPUs instead. -func (i *propertyExtractor) UsesOnlyNVGPUModule() (uses bool, reason string) { - return i.HasOnlyIntegratedGPUs() -} - -// HasOnlyIntegratedGPUs checks whether all GPUs are iGPUs that use NVML. +// HasAnIntegratedGPU checks whether any of the GPUs reported by NVML is an +// integrated GPU. // // As of Orin-based systems iGPUs also support limited NVML queries. -// In the absence of a robust API, we rely on heuristics to make this decision. +// In the absence of a robust API, we rely on heuristics based on the device +// name to make this decision. // -// The following device names are checked: +// Devices with the following names are considered integrated GPUs: // // GPU 0: Orin (nvgpu) (UUID: 54d0709b-558d-5a59-9c65-0c5fc14a21a4) // GPU 0: NVIDIA Thor (UUID: 54d0709b-558d-5a59-9c65-0c5fc14a21a4) // -// This function returns true if ALL devices are detected as iGPUs. -func (i *propertyExtractor) HasOnlyIntegratedGPUs() (uses bool, reason string) { +// (Where this shows the nvidia-smi -L output on these systems). +func (i *propertyExtractor) HasAnIntegratedGPU() (uses bool, reason string) { // We ensure that this function never panics defer func() { if err := recover(); err != nil { @@ -144,14 +139,23 @@ func (i *propertyExtractor) HasOnlyIntegratedGPUs() (uses bool, reason string) { } for _, name := range names { - if !isIntegratedGPUName(name) { - return false, fmt.Sprintf("device %q does not use nvgpu module", name) + if IsIntegratedGPUName(name) { + return true, fmt.Sprintf("device %q is an integrated GPU", name) } } - return true, "all devices use nvgpu module" + return false, "no integrated GPUs found" } -func isIntegratedGPUName(name string) bool { +// IsIntegratedGPUName checks whether the specified device name is associated +// with a known integrated GPU. +// +// Devices with the following names are considered integrated GPUs: +// +// GPU 0: Orin (nvgpu) (UUID: 54d0709b-558d-5a59-9c65-0c5fc14a21a4) +// GPU 0: NVIDIA Thor (UUID: 54d0709b-558d-5a59-9c65-0c5fc14a21a4) +// +// (Where this shows the nvidia-smi -L output on these systems). +func IsIntegratedGPUName(name string) bool { if strings.Contains(name, "(nvgpu)") { return true } diff --git a/pkg/nvlib/info/property-extractor_mock.go b/pkg/nvlib/info/property-extractor_mock.go index bd7d413..708c6e8 100644 --- a/pkg/nvlib/info/property-extractor_mock.go +++ b/pkg/nvlib/info/property-extractor_mock.go @@ -17,24 +17,18 @@ var _ PropertyExtractor = &PropertyExtractorMock{} // // // make and configure a mocked PropertyExtractor // mockedPropertyExtractor := &PropertyExtractorMock{ +// HasAnIntegratedGPUFunc: func() (bool, string) { +// panic("mock out the HasAnIntegratedGPU method") +// }, // HasDXCoreFunc: func() (bool, string) { // panic("mock out the HasDXCore method") // }, // HasNvmlFunc: func() (bool, string) { // panic("mock out the HasNvml method") // }, -// HasOnlyIntegratedGPUsFunc: func() (bool, string) { -// panic("mock out the HasOnlyIntegratedGPUs method") -// }, // HasTegraFilesFunc: func() (bool, string) { // panic("mock out the HasTegraFiles method") // }, -// IsTegraSystemFunc: func() (bool, string) { -// panic("mock out the IsTegraSystem method") -// }, -// UsesOnlyNVGPUModuleFunc: func() (bool, string) { -// panic("mock out the UsesOnlyNVGPUModule method") -// }, // } // // // use mockedPropertyExtractor in code that requires PropertyExtractor @@ -42,51 +36,64 @@ var _ PropertyExtractor = &PropertyExtractorMock{} // // } type PropertyExtractorMock struct { + // HasAnIntegratedGPUFunc mocks the HasAnIntegratedGPU method. + HasAnIntegratedGPUFunc func() (bool, string) + // HasDXCoreFunc mocks the HasDXCore method. HasDXCoreFunc func() (bool, string) // HasNvmlFunc mocks the HasNvml method. HasNvmlFunc func() (bool, string) - // HasOnlyIntegratedGPUsFunc mocks the HasOnlyIntegratedGPUs method. - HasOnlyIntegratedGPUsFunc func() (bool, string) - // HasTegraFilesFunc mocks the HasTegraFiles method. HasTegraFilesFunc func() (bool, string) - // IsTegraSystemFunc mocks the IsTegraSystem method. - IsTegraSystemFunc func() (bool, string) - - // UsesOnlyNVGPUModuleFunc mocks the UsesOnlyNVGPUModule method. - UsesOnlyNVGPUModuleFunc func() (bool, string) - // calls tracks calls to the methods. calls struct { + // HasAnIntegratedGPU holds details about calls to the HasAnIntegratedGPU method. + HasAnIntegratedGPU []struct { + } // HasDXCore holds details about calls to the HasDXCore method. HasDXCore []struct { } // HasNvml holds details about calls to the HasNvml method. HasNvml []struct { } - // HasOnlyIntegratedGPUs holds details about calls to the HasOnlyIntegratedGPUs method. - HasOnlyIntegratedGPUs []struct { - } // HasTegraFiles holds details about calls to the HasTegraFiles method. HasTegraFiles []struct { } - // IsTegraSystem holds details about calls to the IsTegraSystem method. - IsTegraSystem []struct { - } - // UsesOnlyNVGPUModule holds details about calls to the UsesOnlyNVGPUModule method. - UsesOnlyNVGPUModule []struct { - } } - lockHasDXCore sync.RWMutex - lockHasNvml sync.RWMutex - lockHasOnlyIntegratedGPUs sync.RWMutex - lockHasTegraFiles sync.RWMutex - lockIsTegraSystem sync.RWMutex - lockUsesOnlyNVGPUModule sync.RWMutex + lockHasAnIntegratedGPU sync.RWMutex + lockHasDXCore sync.RWMutex + lockHasNvml sync.RWMutex + lockHasTegraFiles sync.RWMutex +} + +// HasAnIntegratedGPU calls HasAnIntegratedGPUFunc. +func (mock *PropertyExtractorMock) HasAnIntegratedGPU() (bool, string) { + if mock.HasAnIntegratedGPUFunc == nil { + panic("PropertyExtractorMock.HasAnIntegratedGPUFunc: method is nil but PropertyExtractor.HasAnIntegratedGPU was just called") + } + callInfo := struct { + }{} + mock.lockHasAnIntegratedGPU.Lock() + mock.calls.HasAnIntegratedGPU = append(mock.calls.HasAnIntegratedGPU, callInfo) + mock.lockHasAnIntegratedGPU.Unlock() + return mock.HasAnIntegratedGPUFunc() +} + +// HasAnIntegratedGPUCalls gets all the calls that were made to HasAnIntegratedGPU. +// Check the length with: +// +// len(mockedPropertyExtractor.HasAnIntegratedGPUCalls()) +func (mock *PropertyExtractorMock) HasAnIntegratedGPUCalls() []struct { +} { + var calls []struct { + } + mock.lockHasAnIntegratedGPU.RLock() + calls = mock.calls.HasAnIntegratedGPU + mock.lockHasAnIntegratedGPU.RUnlock() + return calls } // HasDXCore calls HasDXCoreFunc. @@ -143,33 +150,6 @@ func (mock *PropertyExtractorMock) HasNvmlCalls() []struct { return calls } -// HasOnlyIntegratedGPUs calls HasOnlyIntegratedGPUsFunc. -func (mock *PropertyExtractorMock) HasOnlyIntegratedGPUs() (bool, string) { - if mock.HasOnlyIntegratedGPUsFunc == nil { - panic("PropertyExtractorMock.HasOnlyIntegratedGPUsFunc: method is nil but PropertyExtractor.HasOnlyIntegratedGPUs was just called") - } - callInfo := struct { - }{} - mock.lockHasOnlyIntegratedGPUs.Lock() - mock.calls.HasOnlyIntegratedGPUs = append(mock.calls.HasOnlyIntegratedGPUs, callInfo) - mock.lockHasOnlyIntegratedGPUs.Unlock() - return mock.HasOnlyIntegratedGPUsFunc() -} - -// HasOnlyIntegratedGPUsCalls gets all the calls that were made to HasOnlyIntegratedGPUs. -// Check the length with: -// -// len(mockedPropertyExtractor.HasOnlyIntegratedGPUsCalls()) -func (mock *PropertyExtractorMock) HasOnlyIntegratedGPUsCalls() []struct { -} { - var calls []struct { - } - mock.lockHasOnlyIntegratedGPUs.RLock() - calls = mock.calls.HasOnlyIntegratedGPUs - mock.lockHasOnlyIntegratedGPUs.RUnlock() - return calls -} - // HasTegraFiles calls HasTegraFilesFunc. func (mock *PropertyExtractorMock) HasTegraFiles() (bool, string) { if mock.HasTegraFilesFunc == nil { @@ -196,57 +176,3 @@ func (mock *PropertyExtractorMock) HasTegraFilesCalls() []struct { mock.lockHasTegraFiles.RUnlock() return calls } - -// IsTegraSystem calls IsTegraSystemFunc. -func (mock *PropertyExtractorMock) IsTegraSystem() (bool, string) { - if mock.IsTegraSystemFunc == nil { - panic("PropertyExtractorMock.IsTegraSystemFunc: method is nil but PropertyExtractor.IsTegraSystem was just called") - } - callInfo := struct { - }{} - mock.lockIsTegraSystem.Lock() - mock.calls.IsTegraSystem = append(mock.calls.IsTegraSystem, callInfo) - mock.lockIsTegraSystem.Unlock() - return mock.IsTegraSystemFunc() -} - -// IsTegraSystemCalls gets all the calls that were made to IsTegraSystem. -// Check the length with: -// -// len(mockedPropertyExtractor.IsTegraSystemCalls()) -func (mock *PropertyExtractorMock) IsTegraSystemCalls() []struct { -} { - var calls []struct { - } - mock.lockIsTegraSystem.RLock() - calls = mock.calls.IsTegraSystem - mock.lockIsTegraSystem.RUnlock() - return calls -} - -// UsesOnlyNVGPUModule calls UsesOnlyNVGPUModuleFunc. -func (mock *PropertyExtractorMock) UsesOnlyNVGPUModule() (bool, string) { - if mock.UsesOnlyNVGPUModuleFunc == nil { - panic("PropertyExtractorMock.UsesOnlyNVGPUModuleFunc: method is nil but PropertyExtractor.UsesOnlyNVGPUModule was just called") - } - callInfo := struct { - }{} - mock.lockUsesOnlyNVGPUModule.Lock() - mock.calls.UsesOnlyNVGPUModule = append(mock.calls.UsesOnlyNVGPUModule, callInfo) - mock.lockUsesOnlyNVGPUModule.Unlock() - return mock.UsesOnlyNVGPUModuleFunc() -} - -// UsesOnlyNVGPUModuleCalls gets all the calls that were made to UsesOnlyNVGPUModule. -// Check the length with: -// -// len(mockedPropertyExtractor.UsesOnlyNVGPUModuleCalls()) -func (mock *PropertyExtractorMock) UsesOnlyNVGPUModuleCalls() []struct { -} { - var calls []struct { - } - mock.lockUsesOnlyNVGPUModule.RLock() - calls = mock.calls.UsesOnlyNVGPUModule - mock.lockUsesOnlyNVGPUModule.RUnlock() - return calls -} diff --git a/pkg/nvlib/info/resolver.go b/pkg/nvlib/info/resolver.go index 0454d8a..8243738 100644 --- a/pkg/nvlib/info/resolver.go +++ b/pkg/nvlib/info/resolver.go @@ -48,13 +48,13 @@ func (p platformResolver) ResolvePlatform() Platform { hasNVML, reason := p.propertyExtractor.HasNvml() p.logger.Debugf("Is NVML-based system? %v: %v", hasNVML, reason) - hasOnlyIntegratedGPUs, reason := p.propertyExtractor.HasOnlyIntegratedGPUs() - p.logger.Debugf("Has only integrated GPUs? %v: %v", hasOnlyIntegratedGPUs, reason) + hasAnIntegratedGPU, reason := p.propertyExtractor.HasAnIntegratedGPU() + p.logger.Debugf("Has an integrated GPU? %v: %v", hasAnIntegratedGPU, reason) switch { case hasDXCore: return PlatformWSL - case (hasTegraFiles && !hasNVML), hasOnlyIntegratedGPUs: + case (hasTegraFiles && !hasNVML), hasAnIntegratedGPU: return PlatformTegra case hasNVML: return PlatformNVML diff --git a/pkg/nvlib/info/resolver_test.go b/pkg/nvlib/info/resolver_test.go index 7aeff38..357c8f9 100644 --- a/pkg/nvlib/info/resolver_test.go +++ b/pkg/nvlib/info/resolver_test.go @@ -25,12 +25,12 @@ import ( func TestResolvePlatform(t *testing.T) { testCases := []struct { - platform string - hasTegraFiles bool - hasDXCore bool - hasNVML bool - hasOnlyIntegratedGPUs bool - expected string + platform string + hasTegraFiles bool + hasDXCore bool + hasNVML bool + hasAnIntegratedGPU bool + expected string }{ { platform: "auto", @@ -59,12 +59,12 @@ func TestResolvePlatform(t *testing.T) { expected: "nvml", }, { - platform: "auto", - hasDXCore: false, - hasTegraFiles: true, - hasNVML: true, - hasOnlyIntegratedGPUs: true, - expected: "tegra", + platform: "auto", + hasDXCore: false, + hasTegraFiles: true, + hasNVML: true, + hasAnIntegratedGPU: true, + expected: "tegra", }, { platform: "nvml", @@ -97,8 +97,8 @@ func TestResolvePlatform(t *testing.T) { HasTegraFilesFunc: func() (bool, string) { return tc.hasTegraFiles, "" }, - HasOnlyIntegratedGPUsFunc: func() (bool, string) { - return tc.hasOnlyIntegratedGPUs, "" + HasAnIntegratedGPUFunc: func() (bool, string) { + return tc.hasAnIntegratedGPU, "" }, }), WithPlatform(Platform(tc.platform)),