Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions utils/networkutils/networkutils.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,86 @@ func ParseEthtoolLOutput(output string) (*EthtoolQueueCounts, error) {
CurrentCombinedQueues: currentCombinedQueues,
}, nil
}

// EthtoolRingSizes contains ring parameters for an interface as returned by `ethtool -g`.
// Fields are set to -1 if the value is "n/a" or omitted.
type EthtoolRingSizes struct {
MaxRX int
MaxRXMini int
MaxRXJumbo int
MaxTX int
CurrentRX int
CurrentRXMini int
CurrentRXJumbo int
CurrentTX int
}

// ParseEthtoolGOutput parses the output of `ethtool -g` and returns an EthtoolRingSizes
// object. Populates fields with -1 if the value is "n/a" or omitted, returns an error if
// the output cannot be parsed.
func ParseEthtoolGOutput(output string) (*EthtoolRingSizes, error) {
idx := strings.Index(output, "Current hardware settings:")
if idx == -1 {
return nil, fmt.Errorf("parsing ethtool -g output: missing 'Current hardware settings:'")
}
preSetSection := output[:idx]
currentSection := output[idx:]

rxRe := regexp.MustCompile(`(?m)^\s*RX:\s*(\d+|n/a)`)
rxMiniRe := regexp.MustCompile(`(?m)^\s*RX Mini:\s*(\d+|n/a)`)
rxJumboRe := regexp.MustCompile(`(?m)^\s*RX Jumbo:\s*(\d+|n/a)`)
txRe := regexp.MustCompile(`(?m)^\s*TX:\s*(\d+|n/a)`)

parseVal := func(re *regexp.Regexp, s string) (int, error) {
m := re.FindStringSubmatch(s)
if len(m) < 2 || m[1] == "n/a" {
return -1, nil
}
return strconv.Atoi(m[1])
}

maxRX, err := parseVal(rxRe, preSetSection)
if err != nil {
return nil, fmt.Errorf("parsing max RX: %w", err)
}
maxRXMini, err := parseVal(rxMiniRe, preSetSection)
if err != nil {
return nil, fmt.Errorf("parsing max RX Mini: %w", err)
}
maxRXJumbo, err := parseVal(rxJumboRe, preSetSection)
if err != nil {
return nil, fmt.Errorf("parsing max RX Jumbo: %w", err)
}
maxTX, err := parseVal(txRe, preSetSection)
if err != nil {
return nil, fmt.Errorf("parsing max TX: %w", err)
}

currentRX, err := parseVal(rxRe, currentSection)
if err != nil {
return nil, fmt.Errorf("parsing current RX: %w", err)
}
currentRXMini, err := parseVal(rxMiniRe, currentSection)
if err != nil {
return nil, fmt.Errorf("parsing current RX Mini: %w", err)
}
currentRXJumbo, err := parseVal(rxJumboRe, currentSection)
if err != nil {
return nil, fmt.Errorf("parsing current RX Jumbo: %w", err)
}
currentTX, err := parseVal(txRe, currentSection)
if err != nil {
return nil, fmt.Errorf("parsing current TX: %w", err)
}

return &EthtoolRingSizes{
MaxRX: maxRX,
MaxRXMini: maxRXMini,
MaxRXJumbo: maxRXJumbo,
MaxTX: maxTX,
CurrentRX: currentRX,
CurrentRXMini: currentRXMini,
CurrentRXJumbo: currentRXJumbo,
CurrentTX: currentTX,
}, nil
}
115 changes: 42 additions & 73 deletions utils/networkutils/networkutils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -660,91 +660,60 @@ Combined: 16`,
})
}
}

func TestEthtoolQueueCountsEffectiveQueues(t *testing.T) {
func TestParseEthtoolGOutput(t *testing.T) {
cases := []struct {
name string
counts *EthtoolQueueCounts
wantEffectiveCurrentRX int
wantEffectiveCurrentTX int
wantEffectiveMaxRX int
wantEffectiveMaxTX int
name string
input string
want *EthtoolRingSizes
wantErr bool
}{
{
name: "separate rx and tx queues set",
counts: &EthtoolQueueCounts{
CurrentRXQueues: 2,
CurrentTXQueues: 4,
CurrentCombinedQueues: -1,
MaxRXQueues: 16,
MaxTXQueues: 32,
MaxCombinedQueues: -1,
},
wantEffectiveCurrentRX: 2,
wantEffectiveCurrentTX: 4,
wantEffectiveMaxRX: 16,
wantEffectiveMaxTX: 32,
},
{
name: "combined queues set",
counts: &EthtoolQueueCounts{
CurrentRXQueues: -1,
CurrentTXQueues: -1,
CurrentCombinedQueues: 8,
MaxRXQueues: -1,
MaxTXQueues: -1,
MaxCombinedQueues: 64,
name: "valid idpf output",
input: `Ring parameters for eth0:
Pre-set maximums:
RX: 8128
RX Mini: n/a
RX Jumbo: n/a
TX: 8160
TX push buff len: n/a
Current hardware settings:
RX: 512
RX Mini: n/a
RX Jumbo: n/a
TX: 512
RX Buf Len: n/a
CQE Size: n/a
TX Push: off
RX Push: off
TX push buff len: n/a
TCP data split: on`,
want: &EthtoolRingSizes{
MaxRX: 8128,
MaxRXMini: -1,
MaxRXJumbo: -1,
MaxTX: 8160,
CurrentRX: 512,
CurrentRXMini: -1,
CurrentRXJumbo: -1,
CurrentTX: 512,
},
wantEffectiveCurrentRX: 8,
wantEffectiveCurrentTX: 8,
wantEffectiveMaxRX: 64,
wantEffectiveMaxTX: 64,
},
{
name: "neither separate nor combined queues set",
counts: &EthtoolQueueCounts{
CurrentRXQueues: -1,
CurrentTXQueues: -1,
CurrentCombinedQueues: -1,
MaxRXQueues: -1,
MaxTXQueues: -1,
MaxCombinedQueues: -1,
},
wantEffectiveCurrentRX: -1,
wantEffectiveCurrentTX: -1,
wantEffectiveMaxRX: -1,
wantEffectiveMaxTX: -1,
},
{
name: "separate queues take precedence over combined queues",
counts: &EthtoolQueueCounts{
CurrentRXQueues: 3,
CurrentTXQueues: 5,
CurrentCombinedQueues: 12,
MaxRXQueues: 15,
MaxTXQueues: 25,
MaxCombinedQueues: 48,
},
wantEffectiveCurrentRX: 3,
wantEffectiveCurrentTX: 5,
wantEffectiveMaxRX: 15,
wantEffectiveMaxTX: 25,
name: "invalid output missing section",
input: "Invalid output",
wantErr: true,
},
}

for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
if got := tc.counts.EffectiveCurrentRXQueues(); got != tc.wantEffectiveCurrentRX {
t.Errorf("EffectiveCurrentRXQueues() = %d, want %d", got, tc.wantEffectiveCurrentRX)
}
if got := tc.counts.EffectiveCurrentTXQueues(); got != tc.wantEffectiveCurrentTX {
t.Errorf("EffectiveCurrentTXQueues() = %d, want %d", got, tc.wantEffectiveCurrentTX)
}
if got := tc.counts.EffectiveMaxRXQueues(); got != tc.wantEffectiveMaxRX {
t.Errorf("EffectiveMaxRXQueues() = %d, want %d", got, tc.wantEffectiveMaxRX)
got, err := ParseEthtoolGOutput(tc.input)
if (err != nil) != tc.wantErr {
t.Errorf("ParseEthtoolGOutput(%q) returned error %v, wantErr %t", tc.input, err, tc.wantErr)
return
}
if got := tc.counts.EffectiveMaxTXQueues(); got != tc.wantEffectiveMaxTX {
t.Errorf("EffectiveMaxTXQueues() = %d, want %d", got, tc.wantEffectiveMaxTX)
if diff := cmp.Diff(got, tc.want); diff != "" {
t.Errorf("ParseEthtoolGOutput(%q) mismatch (-got +want):\n%s", tc.input, diff)
}
})
}
Expand Down