Skip to content

Commit 881d378

Browse files
authored
Add in additional NIC info (#549)
* feat: Add MTU to NIC table This commit introduces MTU to the NIC table * feat: Add NIC queue counts and packet steering info
1 parent 716954c commit 881d378

File tree

4 files changed

+153
-6
lines changed

4 files changed

+153
-6
lines changed

cmd/report/report.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ var categories = []common.Category{
171171
{FlagName: flagElcName, FlagVar: &flagElc, Help: "Efficiency Latency Control Settings", TableNames: []string{report.ElcTableName}},
172172
{FlagName: flagMemoryName, FlagVar: &flagMemory, Help: "Memory Configuration", TableNames: []string{report.MemoryTableName}},
173173
{FlagName: flagDimmName, FlagVar: &flagDimm, Help: "DIMM Population", TableNames: []string{report.DIMMTableName}},
174-
{FlagName: flagNicName, FlagVar: &flagNic, Help: "Network Cards", TableNames: []string{report.NICTableName}},
174+
{FlagName: flagNicName, FlagVar: &flagNic, Help: "Network Cards", TableNames: []string{report.NICTableName, report.NICPacketSteeringTableName}},
175175
{FlagName: flagNetConfigName, FlagVar: &flagNetConfig, Help: "Network Configuration", TableNames: []string{report.NetworkConfigTableName}},
176176
{FlagName: flagNetIrqName, FlagVar: &flagNetIrq, Help: "Network IRQ to CPU Mapping", TableNames: []string{report.NetworkIRQMappingTableName}},
177177
{FlagName: flagDiskName, FlagVar: &flagDisk, Help: "Storage Devices", TableNames: []string{report.DiskTableName}},

internal/report/table_defs.go

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"log/slog"
1212
"regexp"
1313
"slices"
14+
"sort"
1415
"strconv"
1516
"strings"
1617
"time"
@@ -94,6 +95,7 @@ const (
9495
NICTableName = "NIC"
9596
NetworkIRQMappingTableName = "Network IRQ Mapping"
9697
NetworkConfigTableName = "Network Configuration"
98+
NICPacketSteeringTableName = "NIC Packet Steering"
9799
DiskTableName = "Disk"
98100
FilesystemTableName = "Filesystem"
99101
GPUTableName = "GPU"
@@ -380,6 +382,13 @@ var tableDefinitions = map[string]TableDefinition{
380382
script.SysctlScriptName,
381383
},
382384
FieldsFunc: networkConfigTableValues},
385+
NICPacketSteeringTableName: {
386+
Name: NICPacketSteeringTableName,
387+
HasRows: true,
388+
ScriptNames: []string{
389+
script.NicInfoScriptName,
390+
},
391+
FieldsFunc: nicPacketSteeringTableValues},
383392
NetworkIRQMappingTableName: {
384393
Name: NetworkIRQMappingTableName,
385394
HasRows: true,
@@ -1625,6 +1634,9 @@ func nicTableValues(outputs map[string]script.ScriptOutput) []Field {
16251634
{Name: "Driver"},
16261635
{Name: "Driver Version"},
16271636
{Name: "Firmware Version"},
1637+
{Name: "MTU", Description: "Maximum Transmission Unit. The largest size packet or frame, specified in octets (eight-bit bytes), that can be sent in a packet- or frame-based network such as the Internet."},
1638+
{Name: "TX Queues"},
1639+
{Name: "RX Queues"},
16281640
{Name: "IRQBalance", Description: "System level setting. Dynamically monitors system activity and spreads IRQs across available cores, aiming to balance CPU load, improve throughput, and reduce latency for interrupt-heavy workloads."},
16291641
{Name: "Adaptive RX", Description: "Enables dynamic adjustment of receive interrupt coalescing based on traffic patterns."},
16301642
{Name: "Adaptive TX", Description: "Enables dynamic adjustment of transmit interrupt coalescing based on traffic patterns."},
@@ -1660,15 +1672,80 @@ func nicTableValues(outputs map[string]script.ScriptOutput) []Field {
16601672
fields[9].Values = append(fields[9].Values, nicInfo.Driver)
16611673
fields[10].Values = append(fields[10].Values, nicInfo.DriverVersion)
16621674
fields[11].Values = append(fields[11].Values, nicInfo.FirmwareVersion)
1663-
fields[12].Values = append(fields[12].Values, nicInfo.IRQBalance)
1664-
fields[13].Values = append(fields[13].Values, nicInfo.AdaptiveRX)
1665-
fields[14].Values = append(fields[14].Values, nicInfo.AdaptiveTX)
1666-
fields[15].Values = append(fields[15].Values, nicInfo.RxUsecs)
1667-
fields[16].Values = append(fields[16].Values, nicInfo.TxUsecs)
1675+
fields[12].Values = append(fields[12].Values, nicInfo.MTU)
1676+
fields[13].Values = append(fields[13].Values, nicInfo.TXQueues)
1677+
fields[14].Values = append(fields[14].Values, nicInfo.RXQueues)
1678+
fields[15].Values = append(fields[15].Values, nicInfo.IRQBalance)
1679+
fields[16].Values = append(fields[16].Values, nicInfo.AdaptiveRX)
1680+
fields[17].Values = append(fields[17].Values, nicInfo.AdaptiveTX)
1681+
fields[18].Values = append(fields[18].Values, nicInfo.RxUsecs)
1682+
fields[19].Values = append(fields[19].Values, nicInfo.TxUsecs)
16681683
}
16691684
return fields
16701685
}
16711686

1687+
func nicPacketSteeringTableValues(outputs map[string]script.ScriptOutput) []Field {
1688+
allNicsInfo := parseNicInfo(outputs[script.NicInfoScriptName].Stdout)
1689+
if len(allNicsInfo) == 0 {
1690+
return []Field{}
1691+
}
1692+
1693+
fields := []Field{
1694+
{Name: "Interface"},
1695+
{Name: "Type", Description: "XPS (Transmit Packet Steering) and RPS (Receive Packet Steering) are software-based mechanisms that allow the selection of a specific logical CPU core to handle the transmission or processing of network packets for a given queue."},
1696+
{Name: "Queue:CPU(s) | Queue|CPU(s) | ..."},
1697+
}
1698+
1699+
for _, nicInfo := range allNicsInfo {
1700+
// XPS row
1701+
if nicInfo.TXQueues != "0" {
1702+
fields[0].Values = append(fields[0].Values, nicInfo.Name)
1703+
fields[1].Values = append(fields[1].Values, "xps_cpus")
1704+
fields[2].Values = append(fields[2].Values, formatQueueCPUMappings(nicInfo.XPSCPUs, "tx-"))
1705+
}
1706+
1707+
// RPS row
1708+
if nicInfo.RXQueues != "0" {
1709+
fields[0].Values = append(fields[0].Values, nicInfo.Name)
1710+
fields[1].Values = append(fields[1].Values, "rps_cpus")
1711+
fields[2].Values = append(fields[2].Values, formatQueueCPUMappings(nicInfo.RPSCPUs, "rx-"))
1712+
}
1713+
}
1714+
1715+
if len(fields[0].Values) == 0 {
1716+
return []Field{}
1717+
}
1718+
return fields
1719+
}
1720+
1721+
func formatQueueCPUMappings(mappings map[string]string, prefix string) string {
1722+
var queueMappings []string
1723+
1724+
// Extract and sort queue numbers to ensure consistent output
1725+
var queues []int
1726+
for queueStr := range mappings {
1727+
queueNum, err := strconv.Atoi(strings.TrimPrefix(queueStr, prefix))
1728+
if err == nil {
1729+
queues = append(queues, queueNum)
1730+
}
1731+
}
1732+
sort.Ints(queues)
1733+
1734+
for _, queueNum := range queues {
1735+
queueStr := fmt.Sprintf("%s%d", prefix, queueNum)
1736+
cpus := mappings[queueStr]
1737+
// a nil value can be returned from the map if the key does not exist, so check for that
1738+
if cpus != "" {
1739+
queueMappings = append(queueMappings, fmt.Sprintf("%d:%s", queueNum, cpus))
1740+
}
1741+
}
1742+
1743+
if len(queueMappings) == 0 {
1744+
return "N/A"
1745+
}
1746+
return strings.Join(queueMappings, " | ")
1747+
}
1748+
16721749
func networkIRQMappingTableValues(outputs map[string]script.ScriptOutput) []Field {
16731750
nicIRQMappings := nicIRQMappingsFromOutput(outputs)
16741751
if len(nicIRQMappings) == 0 {

internal/report/table_helpers.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/csv"
1010
"fmt"
1111
"log/slog"
12+
"math/big"
1213
"regexp"
1314
"sort"
1415
"strconv"
@@ -1260,7 +1261,12 @@ type nicInfo struct {
12601261
TxUsecs string
12611262
Card string
12621263
Port string
1264+
MTU string
12631265
IsVirtual bool
1266+
TXQueues string
1267+
RXQueues string
1268+
XPSCPUs map[string]string
1269+
RPSCPUs map[string]string
12641270
}
12651271

12661272
func parseNicInfo(scriptOutput string) []nicInfo {
@@ -1270,6 +1276,8 @@ func parseNicInfo(scriptOutput string) []nicInfo {
12701276
continue
12711277
}
12721278
var nic nicInfo
1279+
nic.XPSCPUs = make(map[string]string)
1280+
nic.RPSCPUs = make(map[string]string)
12731281
// Map of prefixes to field pointers
12741282
fieldMap := map[string]*string{
12751283
"Interface: ": &nic.Name,
@@ -1289,6 +1297,9 @@ func parseNicInfo(scriptOutput string) []nicInfo {
12891297
"IRQ Balance: ": &nic.IRQBalance,
12901298
"rx-usecs: ": &nic.RxUsecs,
12911299
"tx-usecs: ": &nic.TxUsecs,
1300+
"MTU: ": &nic.MTU,
1301+
"TX Queues: ": &nic.TXQueues,
1302+
"RX Queues: ": &nic.RXQueues,
12921303
}
12931304
for line := range strings.SplitSeq(nicOutput, "\n") {
12941305
line = strings.TrimSpace(line)
@@ -1306,6 +1317,23 @@ func parseNicInfo(scriptOutput string) []nicInfo {
13061317
nic.IsVirtual = (strings.TrimSpace(value) == "yes")
13071318
continue
13081319
}
1320+
// Special parsing for xps_cpus and rps_cpus
1321+
if strings.HasPrefix(line, "xps_cpus tx-") {
1322+
parts := strings.SplitN(line, ": ", 2)
1323+
if len(parts) == 2 {
1324+
queue := strings.TrimPrefix(parts[0], "xps_cpus ")
1325+
nic.XPSCPUs[queue] = hexBitmapToCPUList(parts[1])
1326+
}
1327+
continue
1328+
}
1329+
if strings.HasPrefix(line, "rps_cpus rx-") {
1330+
parts := strings.SplitN(line, ": ", 2)
1331+
if len(parts) == 2 {
1332+
queue := strings.TrimPrefix(parts[0], "rps_cpus ")
1333+
nic.RPSCPUs[queue] = hexBitmapToCPUList(parts[1])
1334+
}
1335+
continue
1336+
}
13091337
for prefix, fieldPtr := range fieldMap {
13101338
if after, ok := strings.CutPrefix(line, prefix); ok {
13111339
*fieldPtr = after
@@ -1322,6 +1350,35 @@ func parseNicInfo(scriptOutput string) []nicInfo {
13221350
return nics
13231351
}
13241352

1353+
func hexBitmapToCPUList(hexBitmap string) string {
1354+
if hexBitmap == "" {
1355+
return ""
1356+
}
1357+
1358+
// Remove commas to form a single continuous hex string.
1359+
// This assumes the comma-separated parts are in big-endian order.
1360+
fullHexBitmap := strings.ReplaceAll(hexBitmap, ",", "")
1361+
1362+
i := new(big.Int)
1363+
// The string is a hex string, so the base is 16.
1364+
if _, success := i.SetString(fullHexBitmap, 16); !success {
1365+
// If parsing fails, it might not be a hex string. Return as is.
1366+
return hexBitmap
1367+
}
1368+
1369+
var cpus []string
1370+
// Iterate through the bits of the big integer.
1371+
for bit := 0; bit < i.BitLen(); bit++ {
1372+
if i.Bit(bit) == 1 {
1373+
cpus = append(cpus, fmt.Sprintf("%d", bit))
1374+
}
1375+
}
1376+
if len(cpus) == 0 {
1377+
return ""
1378+
}
1379+
return strings.Join(cpus, ",")
1380+
}
1381+
13251382
// assignCardAndPort assigns card and port numbers to NICs based on their PCI addresses
13261383
func assignCardAndPort(nics []nicInfo) {
13271384
if len(nics) == 0 {

internal/script/script_defs.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -758,6 +758,7 @@ rdmsr 0x2FFE
758758
echo "Model ID: $(echo "$udevadm_out" | grep ID_MODEL_ID= | cut -d'=' -f2)"
759759
echo "Vendor: $(echo "$udevadm_out" | grep ID_VENDOR_FROM_DATABASE= | cut -d'=' -f2)"
760760
echo "Model: $(echo "$udevadm_out" | grep ID_MODEL_FROM_DATABASE= | cut -d'=' -f2)"
761+
echo "MTU: $(cat /sys/class/net/"$ifc"/mtu 2>/dev/null)"
761762
echo "$ethtool_out"
762763
echo "$ethtool_i_out"
763764
if ethtool_c_out=$(ethtool -c "$ifc" 2>/dev/null); then
@@ -779,6 +780,18 @@ rdmsr 0x2FFE
779780
done
780781
printf "\n"
781782
echo "IRQ Balance: $(pgrep irqbalance >/dev/null 2>&1 && echo "Enabled" || echo "Disabled")"
783+
echo "TX Queues: $(ls -d /sys/class/net/"$ifc"/queues/tx-* | wc -l)"
784+
echo "RX Queues: $(ls -d /sys/class/net/"$ifc"/queues/rx-* | wc -l)"
785+
for q in /sys/class/net/"$ifc"/queues/tx-*; do
786+
if [ -f "$q/xps_cpus" ]; then
787+
echo "xps_cpus $(basename "$q"): $(cat "$q/xps_cpus")"
788+
fi
789+
done
790+
for q in /sys/class/net/"$ifc"/queues/rx-*; do
791+
if [ -f "$q/rps_cpus" ]; then
792+
echo "rps_cpus $(basename "$q"): $(cat "$q/rps_cpus")"
793+
fi
794+
done
782795
echo "----------------------------------------"
783796
done
784797
`,

0 commit comments

Comments
 (0)