Skip to content

Commit 2a4ad53

Browse files
Copilotharp-intel
andauthored
Add virtual function detection and annotation to NIC table (#525)
* Initial plan * 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 <[email protected]> * 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 <[email protected]> * 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 <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: harp-intel <[email protected]>
1 parent 5323947 commit 2a4ad53

File tree

4 files changed

+172
-1
lines changed

4 files changed

+172
-1
lines changed

internal/report/table_defs.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1623,7 +1623,12 @@ func nicTableValues(outputs map[string]script.ScriptOutput) []Field {
16231623
{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."},
16241624
}
16251625
for _, nicInfo := range allNicsInfo {
1626-
fields[0].Values = append(fields[0].Values, nicInfo.Name)
1626+
// Annotate interface name with (virtual) if it's a virtual function
1627+
nicName := nicInfo.Name
1628+
if nicInfo.IsVirtual {
1629+
nicName += " (virtual)"
1630+
}
1631+
fields[0].Values = append(fields[0].Values, nicName)
16271632
fields[1].Values = append(fields[1].Values, nicInfo.Vendor)
16281633
if nicInfo.VendorID != "" {
16291634
fields[1].Values[len(fields[1].Values)-1] += fmt.Sprintf(" (%s)", nicInfo.VendorID)

internal/report/table_helpers.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1235,6 +1235,7 @@ type nicInfo struct {
12351235
TxUsecs string
12361236
Card string
12371237
Port string
1238+
IsVirtual bool
12381239
}
12391240

12401241
func parseNicInfo(scriptOutput string) []nicInfo {
@@ -1275,6 +1276,11 @@ func parseNicInfo(scriptOutput string) []nicInfo {
12751276
}
12761277
continue
12771278
}
1279+
// Check if this is a virtual function
1280+
if value, ok := strings.CutPrefix(line, "Virtual Function: "); ok {
1281+
nic.IsVirtual = (strings.TrimSpace(value) == "yes")
1282+
continue
1283+
}
12781284
for prefix, fieldPtr := range fieldMap {
12791285
if after, ok := strings.CutPrefix(line, prefix); ok {
12801286
*fieldPtr = after

internal/report/table_helpers_test.go

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,6 +669,9 @@ func TestParseNicInfo(t *testing.T) {
669669
if first.TxUsecs != "150" {
670670
t.Errorf("expected TxUsecs '150', got '%s'", first.TxUsecs)
671671
}
672+
if first.IsVirtual {
673+
t.Errorf("expected IsVirtual to be false for first NIC")
674+
}
672675

673676
// Spot check second NIC
674677
second := nics[1]
@@ -704,6 +707,154 @@ func TestParseNicInfo(t *testing.T) {
704707
}
705708
}
706709

710+
func TestParseNicInfoWithVirtualFunction(t *testing.T) {
711+
nicinfoWithVF := `
712+
Interface: eth0
713+
Vendor: Intel Corporation
714+
Vendor ID: 8086
715+
Model: Ethernet Adaptive Virtual Function
716+
Model ID: 1889
717+
Speed: 10000Mb/s
718+
Link detected: yes
719+
driver: iavf
720+
version: 6.13.7-061307-generic
721+
firmware-version: N/A
722+
bus-info: 0000:c0:11.0
723+
MAC Address: 00:11:22:33:44:55
724+
NUMA Node: 1
725+
Virtual Function: yes
726+
CPU Affinity: 100:0-63;
727+
IRQ Balance: Enabled
728+
Adaptive RX: on TX: on
729+
rx-usecs: 100
730+
tx-usecs: 100
731+
----------------------------------------
732+
Interface: eth1
733+
Vendor: Intel Corporation
734+
Vendor ID: 8086
735+
Model: Ethernet Controller E810-C
736+
Model ID: 1592
737+
Speed: 25000Mb/s
738+
Link detected: yes
739+
driver: ice
740+
version: 6.13.7-061307-generic
741+
firmware-version: 4.20
742+
bus-info: 0000:c0:00.0
743+
MAC Address: aa:bb:cc:dd:ee:ff
744+
NUMA Node: 1
745+
Virtual Function: no
746+
CPU Affinity: 200:0-63;
747+
IRQ Balance: Enabled
748+
Adaptive RX: off TX: off
749+
rx-usecs: 50
750+
tx-usecs: 50
751+
----------------------------------------
752+
`
753+
nics := parseNicInfo(nicinfoWithVF)
754+
if len(nics) != 2 {
755+
t.Fatalf("expected 2 NICs, got %d", len(nics))
756+
}
757+
758+
// Test virtual function
759+
vf := nics[0]
760+
if vf.Name != "eth0" {
761+
t.Errorf("expected Name 'eth0', got '%s'", vf.Name)
762+
}
763+
if !vf.IsVirtual {
764+
t.Errorf("expected IsVirtual to be true for eth0")
765+
}
766+
if vf.Model != "Ethernet Adaptive Virtual Function" {
767+
t.Errorf("expected Model 'Ethernet Adaptive Virtual Function', got '%s'", vf.Model)
768+
}
769+
770+
// Test physical function
771+
pf := nics[1]
772+
if pf.Name != "eth1" {
773+
t.Errorf("expected Name 'eth1', got '%s'", pf.Name)
774+
}
775+
if pf.IsVirtual {
776+
t.Errorf("expected IsVirtual to be false for eth1")
777+
}
778+
if pf.Model != "Ethernet Controller E810-C" {
779+
t.Errorf("expected Model 'Ethernet Controller E810-C', got '%s'", pf.Model)
780+
}
781+
}
782+
783+
func TestNicTableValuesWithVirtualFunction(t *testing.T) {
784+
nicinfoWithVF := `
785+
Interface: eth0
786+
Vendor: Intel Corporation
787+
Vendor ID: 8086
788+
Model: Ethernet Adaptive Virtual Function
789+
Model ID: 1889
790+
Speed: 10000Mb/s
791+
Link detected: yes
792+
driver: iavf
793+
version: 6.13.7-061307-generic
794+
firmware-version: N/A
795+
bus-info: 0000:c0:11.0
796+
MAC Address: 00:11:22:33:44:55
797+
NUMA Node: 1
798+
Virtual Function: yes
799+
CPU Affinity: 100:0-63;
800+
IRQ Balance: Enabled
801+
Adaptive RX: on TX: on
802+
rx-usecs: 100
803+
tx-usecs: 100
804+
----------------------------------------
805+
Interface: eth1
806+
Vendor: Intel Corporation
807+
Vendor ID: 8086
808+
Model: Ethernet Controller E810-C
809+
Model ID: 1592
810+
Speed: 25000Mb/s
811+
Link detected: yes
812+
driver: ice
813+
version: 6.13.7-061307-generic
814+
firmware-version: 4.20
815+
bus-info: 0000:c0:00.0
816+
MAC Address: aa:bb:cc:dd:ee:ff
817+
NUMA Node: 1
818+
Virtual Function: no
819+
CPU Affinity: 200:0-63;
820+
IRQ Balance: Enabled
821+
Adaptive RX: off TX: off
822+
rx-usecs: 50
823+
tx-usecs: 50
824+
----------------------------------------
825+
`
826+
827+
outputs := map[string]script.ScriptOutput{
828+
script.NicInfoScriptName: {Stdout: nicinfoWithVF},
829+
}
830+
831+
fields := nicTableValues(outputs)
832+
833+
if len(fields) == 0 {
834+
t.Fatal("Expected fields, got empty slice")
835+
}
836+
837+
// Find the Name field
838+
nameField := fields[0]
839+
if nameField.Name != "Name" {
840+
t.Fatalf("Expected first field to be 'Name', got '%s'", nameField.Name)
841+
}
842+
843+
if len(nameField.Values) != 2 {
844+
t.Fatalf("Expected 2 NIC names, got %d", len(nameField.Values))
845+
}
846+
847+
// Check that the virtual function has "(virtual)" annotation
848+
if nameField.Values[0] != "eth0 (virtual)" {
849+
t.Errorf("Expected 'eth0 (virtual)', got '%s'", nameField.Values[0])
850+
}
851+
852+
// Check that the physical function does not have "(virtual)" annotation
853+
if nameField.Values[1] != "eth1" {
854+
t.Errorf("Expected 'eth1', got '%s'", nameField.Values[1])
855+
}
856+
}
857+
707858
var nicinfo = `
708859
Interface: ens7f0np0
709860
Vendor: Broadcom Inc. and subsidiaries
@@ -761,6 +912,7 @@ tx-usecs-irq: 0
761912
tx-frames-irq: 0
762913
MAC Address: 04:32:01:f3:e1:a4
763914
NUMA Node: 0
915+
Virtual Function: no
764916
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;
765917
IRQ Balance: Disabled
766918
----------------------------------------
@@ -819,6 +971,7 @@ tx-usecs-irq: 0
819971
tx-frames-irq: 0
820972
MAC Address: 04:32:01:f3:e1:a5
821973
NUMA Node: 0
974+
Virtual Function: no
822975
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;
823976
IRQ Balance: Disabled
824977
----------------------------------------
@@ -857,6 +1010,7 @@ supports-register-dump: no
8571010
supports-priv-flags: no
8581011
MAC Address: 2a:ec:f9:27:02:ac
8591012
NUMA Node:
1013+
Virtual Function: no
8601014
CPU Affinity:
8611015
IRQ Balance: Disabled
8621016
----------------------------------------

internal/script/script_defs.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,12 @@ rdmsr 0x2FFE
804804
fi
805805
echo "MAC Address: $(cat /sys/class/net/"$ifc"/address 2>/dev/null)"
806806
echo "NUMA Node: $(cat /sys/class/net/"$ifc"/device/numa_node 2>/dev/null)"
807+
# Check if this is a virtual function
808+
if [ -L /sys/class/net/"$ifc"/device/physfn ]; then
809+
echo "Virtual Function: yes"
810+
else
811+
echo "Virtual Function: no"
812+
fi
807813
echo -n "CPU Affinity: "
808814
intlist=$( grep -e "$ifc" /proc/interrupts | cut -d':' -f1 | sed -e 's/^[[:space:]]*//' )
809815
for int in $intlist; do

0 commit comments

Comments
 (0)