From 63ea9933497d06256434eb56d75f550bdcf3b6a0 Mon Sep 17 00:00:00 2001 From: Anthony Harivel Date: Tue, 10 Mar 2026 15:53:59 +0100 Subject: [PATCH] sysfs: add NetClassPCIDevice and PciDeviceVFAddress for SR-IOV support Add two new functions to support SR-IOV network VF metrics collection: - NetClassPCIDevice(iface string) (*PciDevice, error) in net_class.go: Resolves /sys/class/net//device symlink and returns the backing PciDevice using the existing PCI device parser. This gives callers access to all PCI device properties (NumaNode, link speed, SR-IOV fields, etc.). - PciDeviceVFAddress(device *PciDevice, vfIndex uint32) (string, error) in pci_device.go: Returns the PCI BDF address of a Virtual Function by resolving the virtfn symlink at /sys/bus/pci/devices//virtfn. Test fixtures added under testdata/fixtures/sys/class/net/enp3s0f0/ using the existing 0000:a2:00.0 PCI device fixture. Signed-off-by: Anthony Harivel --- sysfs/net_class.go | 12 +++++ sysfs/net_class_aer_test.go | 1 + sysfs/net_class_sriov_test.go | 93 +++++++++++++++++++++++++++++++++++ sysfs/net_class_test.go | 13 +++-- sysfs/pci_device.go | 13 +++++ testdata/fixtures.ttar | 90 ++++++++++++++++----------------- 6 files changed, 170 insertions(+), 52 deletions(-) create mode 100644 sysfs/net_class_sriov_test.go diff --git a/sysfs/net_class.go b/sysfs/net_class.go index 2c87fd37..4c05706e 100644 --- a/sysfs/net_class.go +++ b/sysfs/net_class.go @@ -97,6 +97,18 @@ func (fs FS) NetClassByIface(devicePath string) (*NetClassIface, error) { } // NetClass returns info for all net interfaces (iface) read from /sys/class/net/. +// NetClassPCIDevice returns the PciDevice backing the given network interface +// by resolving the /sys/class/net//device symlink and parsing the +// corresponding PCI device via the existing PCI device parser. +func (fs FS) NetClassPCIDevice(iface string) (*PciDevice, error) { + deviceSymlink := fs.sys.Path(netclassPath, iface, "device") + resolved, err := os.Readlink(deviceSymlink) + if err != nil { + return nil, fmt.Errorf("failed to resolve device symlink for %q: %w", iface, err) + } + return fs.parsePciDevice(filepath.Base(resolved)) +} + func (fs FS) NetClass() (NetClass, error) { devices, err := fs.NetClassDevices() if err != nil { diff --git a/sysfs/net_class_aer_test.go b/sysfs/net_class_aer_test.go index 747ae4de..07c128cc 100644 --- a/sysfs/net_class_aer_test.go +++ b/sysfs/net_class_aer_test.go @@ -50,6 +50,7 @@ func TestAerCounters(t *testing.T) { ac, _ := fs.AerCounters() aerCounters := AllAerCounters{ + "enp3s0f0": AerCounters{Name: "enp3s0f0"}, "eth0": AerCounters{ Name: "eth0", Correctable: CorrectableAerCounters{ diff --git a/sysfs/net_class_sriov_test.go b/sysfs/net_class_sriov_test.go new file mode 100644 index 00000000..983bf293 --- /dev/null +++ b/sysfs/net_class_sriov_test.go @@ -0,0 +1,93 @@ +// Copyright The Prometheus Authors +// 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. + +//go:build linux + +package sysfs + +import ( + "testing" +) + +func TestNetClassPCIDevice(t *testing.T) { + fs, err := NewFS(sysTestFixtures) + if err != nil { + t.Fatal(err) + } + + dev, err := fs.NetClassPCIDevice("enp3s0f0") + if err != nil { + t.Fatal(err) + } + + if want := 0xa2; dev.Location.Bus != want { + t.Errorf("NetClassPCIDevice() Bus = %#x, want %#x", dev.Location.Bus, want) + } + + if dev.NumaNode == nil { + t.Fatal("expected NumaNode to be set") + } + if want := int32(1); *dev.NumaNode != want { + t.Errorf("NumaNode = %d, want %d", *dev.NumaNode, want) + } +} + +func TestNetClassPCIDeviceMissing(t *testing.T) { + fs, err := NewFS(sysTestFixtures) + if err != nil { + t.Fatal(err) + } + + _, err = fs.NetClassPCIDevice("nonexistent") + if err == nil { + t.Error("expected error for non-existent interface, got nil") + } +} + +func TestPciDeviceVFAddress(t *testing.T) { + fs, err := NewFS(sysTestFixtures) + if err != nil { + t.Fatal(err) + } + + dev, err := fs.NetClassPCIDevice("enp3s0f0") + if err != nil { + t.Fatal(err) + } + + got, err := fs.PciDeviceVFAddress(dev, 0) + if err != nil { + t.Fatal(err) + } + + if want := "0000:a2:01.0"; got != want { + t.Errorf("PciDeviceVFAddress() = %q, want %q", got, want) + } +} + +func TestPciDeviceVFAddressMissing(t *testing.T) { + fs, err := NewFS(sysTestFixtures) + if err != nil { + t.Fatal(err) + } + + dev, err := fs.NetClassPCIDevice("enp3s0f0") + if err != nil { + t.Fatal(err) + } + + _, err = fs.PciDeviceVFAddress(dev, 99) + if err == nil { + t.Error("expected error for non-existent VF, got nil") + } +} diff --git a/sysfs/net_class_test.go b/sysfs/net_class_test.go index 5e0dc717..0704bc63 100644 --- a/sysfs/net_class_test.go +++ b/sysfs/net_class_test.go @@ -32,11 +32,15 @@ func TestNewNetClassDevices(t *testing.T) { t.Fatal(err) } - if len(devices) != 1 { - t.Errorf("Unexpected number of devices, want %d, have %d", 1, len(devices)) + found := false + for _, d := range devices { + if d == "eth0" { + found = true + break + } } - if devices[0] != "eth0" { - t.Errorf("Found unexpected device, want %s, have %s", "eth0", devices[0]) + if !found { + t.Errorf("expected device eth0 not found in %v", devices) } } @@ -94,6 +98,7 @@ func TestNetClass(t *testing.T) { ) netClass := NetClass{ + "enp3s0f0": {Name: "enp3s0f0"}, "eth0": { Address: "01:01:01:01:01:01", AddrAssignType: &addrAssignType, diff --git a/sysfs/pci_device.go b/sysfs/pci_device.go index 42e7344b..aef87336 100644 --- a/sysfs/pci_device.go +++ b/sysfs/pci_device.go @@ -103,6 +103,19 @@ func (pd PciDevice) Name() string { return pd.Location.String() } +// PciDeviceVFAddress returns the PCI BDF address of a Virtual Function by +// resolving the virtfn symlink at /sys/bus/pci/devices//virtfn. +func (fs FS) PciDeviceVFAddress(device *PciDevice, vfIndex uint32) (string, error) { + loc := device.Location + bdf := fmt.Sprintf("%04x:%02x:%02x.%x", loc.Segment, loc.Bus, loc.Device, loc.Function) + virtfnPath := fs.sys.Path(pciDevicesPath, bdf, fmt.Sprintf("virtfn%d", vfIndex)) + resolved, err := os.Readlink(virtfnPath) + if err != nil { + return "", fmt.Errorf("failed to read virtfn%d symlink for %q: %w", vfIndex, bdf, err) + } + return filepath.Base(resolved), nil +} + // PciDevices is a collection of every PCI device in // /sys/bus/pci/devices . // diff --git a/testdata/fixtures.ttar b/testdata/fixtures.ttar index 844757c9..6804ff33 100644 --- a/testdata/fixtures.ttar +++ b/testdata/fixtures.ttar @@ -6341,6 +6341,12 @@ Mode: 644 Directory: fixtures/sys/class/net Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Directory: fixtures/sys/class/net/enp3s0f0 +Mode: 755 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/class/net/enp3s0f0/device +SymlinkTo: ../../../devices/pci0000:a2/0000:a2:00.0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/class/net/eth0 Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -6684,32 +6690,6 @@ Lines: 1 Samsung SSD 970 PRO 512GB Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Directory: fixtures/sys/class/nvme/nvme0/nvme0c0n0 -Mode: 755 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/class/nvme/nvme0/nvme0c0n0/ana_state -Lines: 1 -optimized -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/class/nvme/nvme0/nvme0c0n0/nuse -Lines: 1 -488281250 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Directory: fixtures/sys/class/nvme/nvme0/nvme0c0n0/queue -Mode: 755 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/class/nvme/nvme0/nvme0c0n0/queue/logical_block_size -Lines: 1 -4096 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -Path: fixtures/sys/class/nvme/nvme0/nvme0c0n0/size -Lines: 1 -3906250000 -Mode: 644 -# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/class/nvme/nvme0/serial Lines: 1 S680HF8N190894I @@ -17390,6 +17370,9 @@ Lines: 1 0x8086 Mode: 444 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/virtfn0 +SymlinkTo: ../0000:a2:01.0 +# ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/devices/pci0000:a2/0000:a2:00.0/vpd Lines: 0 Mode: 600 @@ -18232,15 +18215,17 @@ Lines: 1 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/btree_write_stats -Lines: 2 -type count size -initial: 19088 1.0G +Lines: 3 + nr size +initial: 19088 108k +journal_reclaim: 541080 405 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/compression_stats -Lines: 2 -compression compressed uncompressed average -lz4: 1.0G 2.0G 4k +Lines: 3 +type compressed uncompressed average extent size +lz4: 19.3G 67.0G 112k +incompressible: 5.5G 5.5G 22k Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/counters @@ -18248,8 +18233,8 @@ Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/counters/btree_node_read Lines: 2 -since mount: 123 -since filesystem creation: 67890 +since mount: 12345 +since filesystem creation: 67890 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1 @@ -18257,36 +18242,45 @@ Mode: 755 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/bucket_size Lines: 1 -128k +1.0M Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/durability Lines: 1 -1 +2 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/io_done -Lines: 6 +Lines: 11 read: - btree: 4411097088 - data: 0 + + sb : 3989504 + btree : 4411097088 + user :5768222552064 + write: - btree: 0 - data: 0 + + sb : 31417344 + btree : 1171456 + user : 39196815360 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/io_errors -Lines: 5 +Lines: 9 IO errors since filesystem creation - read: 197346 - write: 0 -IO errors since last mount - read: 0 + + read: 197346 + write: 0 + checksum:0 + +IO errors since 8 y ago + + read: 197346 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/label Lines: 1 -testdev +data0 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/dev-1/nbuckets @@ -18301,7 +18295,7 @@ Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Path: fixtures/sys/fs/bcachefs/deadbeef-1234-5678-9012-abcdefabcdef/errors Lines: 1 -btree_node_read_err 5 0 +btree_node_read_err 5 1234567890 Mode: 644 # ttar - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Directory: fixtures/sys/fs/btrfs