From fe5c4cc5626e9f4c1857396f06e2f94189e28dd6 Mon Sep 17 00:00:00 2001 From: YoungjuKim Date: Sat, 17 Oct 2026 23:27:53 +0900 Subject: [PATCH 1/2] nvpci: do not warn when a GPU has no vfio-dev directory, and fix simpleLogger formatting A GPU that is not bound to vfio has no vfio-dev directory, which is the normal case, yet nvpci logged a warning for every such device on every scan. The warning was also garbled ("%!v(MISSING)") because simpleLogger passed its variadic arguments as a single slice. Treat a missing vfio-dev directory as "no IOMMU FD" without a warning, and forward the arguments with v... . Signed-off-by: YoungjuKim --- pkg/nvpci/logger.go | 2 +- pkg/nvpci/logger_test.go | 41 +++++++++++++++++++++++++++++++ pkg/nvpci/nvpci.go | 6 +++++ pkg/nvpci/nvpci_test.go | 52 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 pkg/nvpci/logger_test.go diff --git a/pkg/nvpci/logger.go b/pkg/nvpci/logger.go index 8b7b5ab..ff77976 100644 --- a/pkg/nvpci/logger.go +++ b/pkg/nvpci/logger.go @@ -25,5 +25,5 @@ type logger interface { type simpleLogger struct{} func (l simpleLogger) Warningf(format string, v ...interface{}) { - log.Printf("WARNING: "+format, v) + log.Printf("WARNING: "+format, v...) } diff --git a/pkg/nvpci/logger_test.go b/pkg/nvpci/logger_test.go new file mode 100644 index 0000000..aa88249 --- /dev/null +++ b/pkg/nvpci/logger_test.go @@ -0,0 +1,41 @@ +/** +# Copyright (c) NVIDIA CORPORATION. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +**/ + +package nvpci + +import ( + "bytes" + "log" + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSimpleLoggerWarningf(t *testing.T) { + var buf bytes.Buffer + flags := log.Flags() + log.SetOutput(&buf) + log.SetFlags(0) + defer func() { + log.SetOutput(os.Stderr) + log.SetFlags(flags) + }() + + simpleLogger{}.Warningf("unable to detect IOMMU FD for %s: %v", "0000:01:00.0", "no such file or directory") + + require.Equal(t, "WARNING: unable to detect IOMMU FD for 0000:01:00.0: no such file or directory\n", buf.String()) +} diff --git a/pkg/nvpci/nvpci.go b/pkg/nvpci/nvpci.go index f1fd841..06ea79f 100644 --- a/pkg/nvpci/nvpci.go +++ b/pkg/nvpci/nvpci.go @@ -566,6 +566,12 @@ func getDriver(devicePath string) (string, error) { func getIOMMUFD(devicePath string) (string, error) { content, err := os.ReadDir(path.Join(devicePath, "vfio-dev")) + // The vfio-dev directory only exists for devices that are bound to a VFIO + // driver on a kernel with VFIO device cdev support. For every other device, + // such as a GPU bound to the nvidia driver, there is no IOMMU FD to report. + if errors.Is(err, os.ErrNotExist) { + return "", nil + } if err != nil { return "", err } diff --git a/pkg/nvpci/nvpci_test.go b/pkg/nvpci/nvpci_test.go index 6963919..cc1b9dd 100644 --- a/pkg/nvpci/nvpci_test.go +++ b/pkg/nvpci/nvpci_test.go @@ -17,6 +17,7 @@ package nvpci import ( + "fmt" "os" "path/filepath" "testing" @@ -196,6 +197,57 @@ func TestNvpciIOMMUFD(t *testing.T) { } } +type recordingLogger struct { + warnings []string +} + +func (l *recordingLogger) Warningf(format string, v ...interface{}) { + l.warnings = append(l.warnings, fmt.Sprintf(format, v...)) +} + +func TestNvpciIOMMUFDNotBoundToVFIO(t *testing.T) { + nvpci, err := NewMockNvpci() + require.Nil(t, err, "Error creating NewMockNvpci") + defer nvpci.Cleanup() + + logger := &recordingLogger{} + nvpci.logger = logger + + err = nvpci.AddMockA100("0000:80:05.1", 0, nil) + require.Nil(t, err, "Error adding Mock A100 device to MockNvpci") + + // A GPU that is bound to the nvidia driver has no vfio-dev directory. + deviceDir := filepath.Join(nvpci.pciDevicesRoot, "0000:80:05.1") + require.NoError(t, os.RemoveAll(filepath.Join(deviceDir, "vfio-dev"))) + + devices, err := nvpci.GetGPUs() + require.Nil(t, err, "Error getting GPUs") + require.Equal(t, 1, len(devices), "Wrong number of GPU devices") + require.Equal(t, "", devices[0].IommuFD, "IOMMUFD should be empty without a vfio-dev directory") + require.Empty(t, logger.warnings, "No warning expected for a device without a vfio-dev directory") +} + +func TestNvpciIOMMUFDEmptyVFIODev(t *testing.T) { + nvpci, err := NewMockNvpci() + require.Nil(t, err, "Error creating NewMockNvpci") + defer nvpci.Cleanup() + + logger := &recordingLogger{} + nvpci.logger = logger + + err = nvpci.AddMockA100("0000:80:05.1", 0, nil) + require.Nil(t, err, "Error adding Mock A100 device to MockNvpci") + + deviceDir := filepath.Join(nvpci.pciDevicesRoot, "0000:80:05.1") + require.NoError(t, os.RemoveAll(filepath.Join(deviceDir, "vfio-dev", "vfio8"))) + + devices, err := nvpci.GetGPUs() + require.Nil(t, err, "Error getting GPUs") + require.Equal(t, 1, len(devices), "Wrong number of GPU devices") + require.Equal(t, "", devices[0].IommuFD, "IOMMUFD should be empty without a vfio device entry") + require.Equal(t, []string{"unable to detect IOMMU FD for 0000:80:05.1: no iommufd device found"}, logger.warnings) +} + func TestNvpciNUMANode(t *testing.T) { testCases := []struct { Description string From a18c6346c1a3b6debab5800a1622eaa367002874 Mon Sep 17 00:00:00 2001 From: YoungjuKim Date: Tue, 3 Nov 2026 15:02:42 +0900 Subject: [PATCH 2/2] nvpci: update the IOMMU FD warning comment getIOMMUFD no longer returns an error for a device without a vfio-dev directory, so the comment at the call site no longer describes when the warning is logged. Say which case is handled in getIOMMUFD and why the remaining errors are still only logged. Signed-off-by: YoungjuKim --- pkg/nvpci/nvpci.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pkg/nvpci/nvpci.go b/pkg/nvpci/nvpci.go index 06ea79f..d869b14 100644 --- a/pkg/nvpci/nvpci.go +++ b/pkg/nvpci/nvpci.go @@ -348,7 +348,10 @@ func (p *nvpci) getNvidiaDeviceByPciBusID(address string, cache map[string]*Nvid iommuFD, err := getIOMMUFD(devicePath) if err != nil { - // log a warning, do not return an error as this host may not have iommufd configured/supported + // A device without a vfio-dev directory (not bound to a VFIO driver, or + // a host without iommufd support) does not get here, see getIOMMUFD. + // Anything else is unexpected, but the IOMMU FD is optional, so log a + // warning instead of returning an error. p.logger.Warningf("unable to detect IOMMU FD for %s: %v", address, err) }