From 09989a87a5f62a2bb4983c17a71f7ff9ca1b6025 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 21:51:03 +0000 Subject: [PATCH 1/4] Initial plan From 453c84da594eab44bfab443baa76dae8acf95090 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 21:59:43 +0000 Subject: [PATCH 2/4] Add virtual function detection to NIC table - Modified NIC info script to detect virtual functions via physfn symlink - Added IsVirtual field to nicInfo struct - Updated parseNicInfo to parse Virtual Function field - Updated nicTableValues to annotate virtual interfaces with "(virtual)" - Added comprehensive tests for virtual function detection Co-authored-by: harp-intel <78619061+harp-intel@users.noreply.github.com> --- internal/report/table_defs.go | 7 ++- internal/report/table_helpers.go | 7 +++ internal/report/table_helpers_test.go | 79 +++++++++++++++++++++++++++ internal/script/script_defs.go | 6 ++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/internal/report/table_defs.go b/internal/report/table_defs.go index 2050e38a..c2b33b78 100644 --- a/internal/report/table_defs.go +++ b/internal/report/table_defs.go @@ -1623,7 +1623,12 @@ func nicTableValues(outputs map[string]script.ScriptOutput) []Field { {Name: "tx-usecs", Description: "Sets the delay, in microseconds, before an interrupt is generated after transmitting a packet. Higher values reduce CPU usage (by batching packets), but increase latency. Lower values reduce latency, but increase interrupt rate and CPU load."}, } for _, nicInfo := range allNicsInfo { - fields[0].Values = append(fields[0].Values, nicInfo.Name) + // Annotate interface name with (virtual) if it's a virtual function + nicName := nicInfo.Name + if nicInfo.IsVirtual { + nicName += " (virtual)" + } + fields[0].Values = append(fields[0].Values, nicName) fields[1].Values = append(fields[1].Values, nicInfo.Vendor) if nicInfo.VendorID != "" { fields[1].Values[len(fields[1].Values)-1] += fmt.Sprintf(" (%s)", nicInfo.VendorID) diff --git a/internal/report/table_helpers.go b/internal/report/table_helpers.go index 2052545e..fe358538 100644 --- a/internal/report/table_helpers.go +++ b/internal/report/table_helpers.go @@ -1235,6 +1235,7 @@ type nicInfo struct { TxUsecs string Card string Port string + IsVirtual bool } func parseNicInfo(scriptOutput string) []nicInfo { @@ -1275,6 +1276,12 @@ func parseNicInfo(scriptOutput string) []nicInfo { } continue } + // Check if this is a virtual function + if strings.HasPrefix(line, "Virtual Function: ") { + value := strings.TrimSpace(strings.TrimPrefix(line, "Virtual Function: ")) + nic.IsVirtual = (value == "yes") + continue + } for prefix, fieldPtr := range fieldMap { if after, ok := strings.CutPrefix(line, prefix); ok { *fieldPtr = after diff --git a/internal/report/table_helpers_test.go b/internal/report/table_helpers_test.go index 6364517e..583b5a76 100644 --- a/internal/report/table_helpers_test.go +++ b/internal/report/table_helpers_test.go @@ -669,6 +669,9 @@ func TestParseNicInfo(t *testing.T) { if first.TxUsecs != "150" { t.Errorf("expected TxUsecs '150', got '%s'", first.TxUsecs) } + if first.IsVirtual { + t.Errorf("expected IsVirtual to be false for first NIC") + } // Spot check second NIC second := nics[1] @@ -704,6 +707,79 @@ func TestParseNicInfo(t *testing.T) { } } +func TestParseNicInfoWithVirtualFunction(t *testing.T) { + nicinfoWithVF := ` +Interface: eth0 +Vendor: Intel Corporation +Vendor ID: 8086 +Model: Ethernet Adaptive Virtual Function +Model ID: 1889 +Speed: 10000Mb/s +Link detected: yes +driver: iavf +version: 6.13.7-061307-generic +firmware-version: N/A +bus-info: 0000:c0:11.0 +MAC Address: 00:11:22:33:44:55 +NUMA Node: 1 +Virtual Function: yes +CPU Affinity: 100:0-63; +IRQ Balance: Enabled +Adaptive RX: on TX: on +rx-usecs: 100 +tx-usecs: 100 +---------------------------------------- +Interface: eth1 +Vendor: Intel Corporation +Vendor ID: 8086 +Model: Ethernet Controller E810-C +Model ID: 1592 +Speed: 25000Mb/s +Link detected: yes +driver: ice +version: 6.13.7-061307-generic +firmware-version: 4.20 +bus-info: 0000:c0:00.0 +MAC Address: aa:bb:cc:dd:ee:ff +NUMA Node: 1 +Virtual Function: no +CPU Affinity: 200:0-63; +IRQ Balance: Enabled +Adaptive RX: off TX: off +rx-usecs: 50 +tx-usecs: 50 +---------------------------------------- +` + nics := parseNicInfo(nicinfoWithVF) + if len(nics) != 2 { + t.Fatalf("expected 2 NICs, got %d", len(nics)) + } + + // Test virtual function + vf := nics[0] + if vf.Name != "eth0" { + t.Errorf("expected Name 'eth0', got '%s'", vf.Name) + } + if !vf.IsVirtual { + t.Errorf("expected IsVirtual to be true for eth0") + } + if vf.Model != "Ethernet Adaptive Virtual Function" { + t.Errorf("expected Model 'Ethernet Adaptive Virtual Function', got '%s'", vf.Model) + } + + // Test physical function + pf := nics[1] + if pf.Name != "eth1" { + t.Errorf("expected Name 'eth1', got '%s'", pf.Name) + } + if pf.IsVirtual { + t.Errorf("expected IsVirtual to be false for eth1") + } + if pf.Model != "Ethernet Controller E810-C" { + t.Errorf("expected Model 'Ethernet Controller E810-C', got '%s'", pf.Model) + } +} + var nicinfo = ` Interface: ens7f0np0 Vendor: Broadcom Inc. and subsidiaries @@ -761,6 +837,7 @@ tx-usecs-irq: 0 tx-frames-irq: 0 MAC Address: 04:32:01:f3:e1:a4 NUMA Node: 0 +Virtual Function: no CPU Affinity: 124:0-143;125:0-143;126:0-143;127:0-143;128:0-143;129:0-143;130:0-143;131:0-143;132:0-143;133:0-143;134:0-143;135:0-143;136:0-143;137:0-143;138:0-143;139:0-143;140:0-143;141:0-143;142:0-143;143:0-143;144:0-143;145:0-143;146:0-143;147:0-143;148:0-143;149:0-143;150:0-143;151:0-143;152:0-143;153:0-143;154:0-143;155:0-143;156:0-143;157:0-143;158:0-143;159:0-143;160:0-143;161:0-143;162:0-143;163:0-143;164:0-143;165:0-143;166:0-143;167:0-143;168:0-143;169:0-143;170:0-143;171:0-143;172:0-143;173:0-143;174:0-143;175:0-143;176:0-143;177:0-143;178:0-143;179:0-143;180:0-143;181:0-143;182:0-143;184:0-143;185:0-143;186:0-143;187:0-143;188:0-143;189:0-143;190:0-143;191:0-143;192:0-143;193:0-143;194:0-143;195:0-143;196:0-143;197:0-143;198:0-143; IRQ Balance: Disabled ---------------------------------------- @@ -819,6 +896,7 @@ tx-usecs-irq: 0 tx-frames-irq: 0 MAC Address: 04:32:01:f3:e1:a5 NUMA Node: 0 +Virtual Function: no CPU Affinity: 454:0-143;455:0-143;456:0-143;457:0-143;458:0-143;459:0-143;460:0-143;461:0-143;462:0-143;463:0-143;464:0-143;465:0-143;466:0-143;467:0-143;468:0-143;469:0-143;470:0-143;471:0-143;472:0-143;473:0-143;474:0-143;475:0-143;476:0-143;477:0-143;478:0-143;479:0-143;480:0-143;481:0-143;482:0-143;483:0-143;484:0-143;485:0-143;486:0-143;487:0-143;488:0-143;489:0-143;490:0-143;491:0-143;492:0-143;493:0-143;494:0-143;495:0-143;496:0-143;497:0-143;498:0-143;499:0-143;500:0-143;501:0-143;502:0-143;503:0-143;504:0-143;505:0-143;506:0-143;507:0-143;508:0-143;509:0-143;510:0-143;511:0-143;512:0-143;513:0-143;514:0-143;515:0-143;516:0-143;517:0-143;518:0-143;519:0-143;520:0-143;521:0-143;522:0-143;523:0-143;524:0-143;525:0-143;526:0-143;527:0-143; IRQ Balance: Disabled ---------------------------------------- @@ -857,6 +935,7 @@ supports-register-dump: no supports-priv-flags: no MAC Address: 2a:ec:f9:27:02:ac NUMA Node: +Virtual Function: no CPU Affinity: IRQ Balance: Disabled ---------------------------------------- diff --git a/internal/script/script_defs.go b/internal/script/script_defs.go index 6bc8264f..7b1fd36d 100644 --- a/internal/script/script_defs.go +++ b/internal/script/script_defs.go @@ -804,6 +804,12 @@ rdmsr 0x2FFE fi echo "MAC Address: $(cat /sys/class/net/"$ifc"/address 2>/dev/null)" echo "NUMA Node: $(cat /sys/class/net/"$ifc"/device/numa_node 2>/dev/null)" + # Check if this is a virtual function + if [ -L /sys/class/net/"$ifc"/device/physfn ]; then + echo "Virtual Function: yes" + else + echo "Virtual Function: no" + fi echo -n "CPU Affinity: " intlist=$( grep -e "$ifc" /proc/interrupts | cut -d':' -f1 | sed -e 's/^[[:space:]]*//' ) for int in $intlist; do From ba5117c1e58648cdc7cbb3a12ae4cafc172c53a5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 22:01:47 +0000 Subject: [PATCH 3/4] Add test for NIC table with virtual function annotation - Added TestNicTableValuesWithVirtualFunction to verify "(virtual)" annotation - Test ensures virtual functions are properly annotated in table output - Test ensures physical functions are not annotated Co-authored-by: harp-intel <78619061+harp-intel@users.noreply.github.com> --- internal/report/table_helpers_test.go | 75 +++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/internal/report/table_helpers_test.go b/internal/report/table_helpers_test.go index 583b5a76..b088666e 100644 --- a/internal/report/table_helpers_test.go +++ b/internal/report/table_helpers_test.go @@ -780,6 +780,81 @@ tx-usecs: 50 } } +func TestNicTableValuesWithVirtualFunction(t *testing.T) { + nicinfoWithVF := ` +Interface: eth0 +Vendor: Intel Corporation +Vendor ID: 8086 +Model: Ethernet Adaptive Virtual Function +Model ID: 1889 +Speed: 10000Mb/s +Link detected: yes +driver: iavf +version: 6.13.7-061307-generic +firmware-version: N/A +bus-info: 0000:c0:11.0 +MAC Address: 00:11:22:33:44:55 +NUMA Node: 1 +Virtual Function: yes +CPU Affinity: 100:0-63; +IRQ Balance: Enabled +Adaptive RX: on TX: on +rx-usecs: 100 +tx-usecs: 100 +---------------------------------------- +Interface: eth1 +Vendor: Intel Corporation +Vendor ID: 8086 +Model: Ethernet Controller E810-C +Model ID: 1592 +Speed: 25000Mb/s +Link detected: yes +driver: ice +version: 6.13.7-061307-generic +firmware-version: 4.20 +bus-info: 0000:c0:00.0 +MAC Address: aa:bb:cc:dd:ee:ff +NUMA Node: 1 +Virtual Function: no +CPU Affinity: 200:0-63; +IRQ Balance: Enabled +Adaptive RX: off TX: off +rx-usecs: 50 +tx-usecs: 50 +---------------------------------------- +` + + outputs := map[string]script.ScriptOutput{ + script.NicInfoScriptName: {Stdout: nicinfoWithVF}, + } + + fields := nicTableValues(outputs) + + if len(fields) == 0 { + t.Fatal("Expected fields, got empty slice") + } + + // Find the Name field + nameField := fields[0] + if nameField.Name != "Name" { + t.Fatalf("Expected first field to be 'Name', got '%s'", nameField.Name) + } + + if len(nameField.Values) != 2 { + t.Fatalf("Expected 2 NIC names, got %d", len(nameField.Values)) + } + + // Check that the virtual function has "(virtual)" annotation + if nameField.Values[0] != "eth0 (virtual)" { + t.Errorf("Expected 'eth0 (virtual)', got '%s'", nameField.Values[0]) + } + + // Check that the physical function does not have "(virtual)" annotation + if nameField.Values[1] != "eth1" { + t.Errorf("Expected 'eth1', got '%s'", nameField.Values[1]) + } +} + var nicinfo = ` Interface: ens7f0np0 Vendor: Broadcom Inc. and subsidiaries From 0384802ec2157e1d6ce588bd6cf1945adedd6401 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 13:27:33 +0000 Subject: [PATCH 4/4] Replace HasPrefix + TrimPrefix with CutPrefix for go-modernize Use CutPrefix instead of HasPrefix followed by TrimPrefix for virtual function detection, following modern Go idioms. Co-authored-by: harp-intel <78619061+harp-intel@users.noreply.github.com> --- internal/report/table_helpers.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/report/table_helpers.go b/internal/report/table_helpers.go index fe358538..338474e7 100644 --- a/internal/report/table_helpers.go +++ b/internal/report/table_helpers.go @@ -1277,9 +1277,8 @@ func parseNicInfo(scriptOutput string) []nicInfo { continue } // Check if this is a virtual function - if strings.HasPrefix(line, "Virtual Function: ") { - value := strings.TrimSpace(strings.TrimPrefix(line, "Virtual Function: ")) - nic.IsVirtual = (value == "yes") + if value, ok := strings.CutPrefix(line, "Virtual Function: "); ok { + nic.IsVirtual = (strings.TrimSpace(value) == "yes") continue } for prefix, fieldPtr := range fieldMap {