Skip to content
Open
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
10 changes: 10 additions & 0 deletions attributeTcMsg.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,11 @@ func extractTCAOptions(data []byte, tc *Attribute, kind string) error {
err := unmarshalFq(data, info)
multiError = concatError(multiError, err)
tc.Fq = info
case "fq_pie":
info := &FqPie{}
err := unmarshalFqPie(data, info)
multiError = concatError(multiError, err)
tc.FqPie = info
case "pie":
info := &Pie{}
err := unmarshalPie(data, info)
Expand Down Expand Up @@ -374,6 +379,11 @@ func extractXStats(data []byte, tc *XStats, kind string) error {
err := unmarshalStruct(data, info)
multiError = concatError(multiError, err)
tc.Pie = info
case "fq_pie":
info := &FqPieXStats{}
err := unmarshalStruct(data, info)
multiError = concatError(multiError, err)
tc.FqPie = info
case "fq_codel":
info := &FqCodelXStats{}
err := unmarshalFqCodelXStats(data, info)
Expand Down
1 change: 1 addition & 0 deletions attributeTcMsg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -358,6 +358,7 @@ func TestQdiscAttribute(t *testing.T) {
"dsmark": {val: &Attribute{Kind: "dsmark", Dsmark: &Dsmark{Indices: uint16Ptr(12), DefaultIndex: uint16Ptr(34), Mask: uint8Ptr(56), Value: uint8Ptr(78)}}},
"fq": {val: &Attribute{Kind: "fq", Fq: &Fq{PLimit: uint32Ptr(1), FlowPLimit: uint32Ptr(2), Quantum: uint32Ptr(3), InitQuantum: uint32Ptr(4), RateEnable: uint32Ptr(5), FlowDefaultRate: uint32Ptr(6), FlowMaxRate: uint32Ptr(7), BucketsLog: uint32Ptr(8), FlowRefillDelay: uint32Ptr(9), OrphanMask: uint32Ptr(10), LowRateThreshold: uint32Ptr(11), CEThreshold: uint32Ptr(12)}}},
"fq_codel": {val: &Attribute{Kind: "fq_codel", FqCodel: &FqCodel{Target: uint32Ptr(1), Limit: uint32Ptr(2), Interval: uint32Ptr(3), ECN: uint32Ptr(4), Flows: uint32Ptr(5), Quantum: uint32Ptr(6), CEThreshold: uint32Ptr(7), DropBatchSize: uint32Ptr(8), MemoryLimit: uint32Ptr(9)}}},
"fq_pie": {val: &Attribute{Kind: "fq_pie", FqPie: &FqPie{Limit: uint32Ptr(10240), Flows: uint32Ptr(1024), Target: uint32Ptr(15), TUpdate: uint32Ptr(15), Alpha: uint32Ptr(2), Beta: uint32Ptr(20), Quantum: uint32Ptr(1514), MemoryLimit: uint32Ptr(32), EcnProb: uint32Ptr(10), Ecn: uint32Ptr(0), Bytemode: uint32Ptr(0), DqRateEstimator: uint32Ptr(0)}}},
"hfsc": {val: &Attribute{Kind: "hfsc", HfscQOpt: &HfscQOpt{DefCls: 42}}},
"hhf": {val: &Attribute{Kind: "hhf", Hhf: &Hhf{BacklogLimit: uint32Ptr(1), Quantum: uint32Ptr(2), HHFlowsLimit: uint32Ptr(3), ResetTimeout: uint32Ptr(4), AdmitBytes: uint32Ptr(5), EVICTTimeout: uint32Ptr(6), NonHHWeight: uint32Ptr(7)}}},
"htb": {val: &Attribute{Kind: "htb", Htb: &Htb{Init: &HtbGlob{Version: 0x3, Rate2Quantum: 0xa, Defcls: 0x30}}}},
Expand Down
137 changes: 137 additions & 0 deletions q_fqPie.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package tc

import (
"fmt"

"github.com/mdlayher/netlink"
)

const (
tcaFqPieUnspec = iota // Corresponds to similar attr struct in kernel
tcaFqPieLimit
tcaFqPieFlows
tcaFqPieTarget
tcaFqPieTUpdate
tcaFqPieAlpha
tcaFqPieBeta
tcaFqPieQuantum // sch_fq_pie.c indicates 32 bit uint; kernel validates range between 1, 2^20
tcaFqPieMemoryLimit
tcaFqPieEcnProb
tcaFqPieEcn
tcaFqPieBytemode
tcaFqPieDqRateEstimator
)

type FqPie struct {
Limit *uint32
Flows *uint32
Target *uint32
TUpdate *uint32
Alpha *uint32
Beta *uint32
Quantum *uint32
MemoryLimit *uint32
EcnProb *uint32
Ecn *uint32
Bytemode *uint32
DqRateEstimator *uint32
}

// marshalFqPie returns the binary encoding of FqPie
func marshalFqPie(info *FqPie) ([]byte, error) {
options := []tcOption{}

if info == nil {
return []byte{}, fmt.Errorf("FqPie: %w", ErrNoArg)
}

// TODO: improve logic and check combinations
if info.Limit != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieLimit, Data: uint32Value(info.Limit)})
}

if info.Flows != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieFlows, Data: uint32Value(info.Flows)})
}

if info.Target != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieTarget, Data: uint32Value(info.Target)})
}

if info.TUpdate != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieTUpdate, Data: uint32Value(info.TUpdate)})
}

if info.Alpha != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieAlpha, Data: uint32Value(info.Alpha)})
}

if info.Beta != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieBeta, Data: uint32Value(info.Beta)})
}

if info.Quantum != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieQuantum, Data: uint32Value(info.Quantum)})
}

if info.MemoryLimit != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieMemoryLimit, Data: uint32Value(info.MemoryLimit)})
}

if info.EcnProb != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieEcnProb, Data: uint32Value(info.EcnProb)})
}

if info.Ecn != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieEcn, Data: uint32Value(info.Ecn)})
}

if info.Bytemode != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieBytemode, Data: uint32Value(info.Bytemode)})
}

if info.DqRateEstimator != nil {
options = append(options, tcOption{Interpretation: vtUint32, Type: tcaFqPieDqRateEstimator, Data: uint32Value(info.DqRateEstimator)})
}

return marshalAttributes(options)
}

// unmarshalFqPie parses the FqPie-encoded data and stores the result in the value pointed to by info.
func unmarshalFqPie(data []byte, info *FqPie) error {
ad, err := netlink.NewAttributeDecoder(data)
if err != nil {
return err
}
for ad.Next() {
switch ad.Type() {
case tcaFqPieLimit:
info.Limit = uint32Ptr(ad.Uint32())
case tcaFqPieFlows:
info.Flows = uint32Ptr(ad.Uint32())
case tcaFqPieTarget:
info.Target = uint32Ptr(ad.Uint32())
case tcaFqPieTUpdate:
info.TUpdate = uint32Ptr(ad.Uint32())
case tcaFqPieAlpha:
info.Alpha = uint32Ptr(ad.Uint32())
case tcaFqPieBeta:
info.Beta = uint32Ptr(ad.Uint32())
case tcaFqPieQuantum:
info.Quantum = uint32Ptr(ad.Uint32())
case tcaFqPieMemoryLimit:
info.MemoryLimit = uint32Ptr(ad.Uint32())
case tcaFqPieEcnProb:
info.EcnProb = uint32Ptr(ad.Uint32())
case tcaFqPieEcn:
info.Ecn = uint32Ptr(ad.Uint32())
case tcaFqPieBytemode:
info.Bytemode = uint32Ptr(ad.Uint32())
case tcaFqPieDqRateEstimator:
info.DqRateEstimator = uint32Ptr(ad.Uint32())
default:
return fmt.Errorf("unmarshalFqPie()\t%d\n\t%v", ad.Type(), ad.Bytes())
}
}
return ad.Err()
}
66 changes: 66 additions & 0 deletions q_fqPie_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package tc

import (
"errors"
"testing"

"github.com/google/go-cmp/cmp"
)

func TestFqPie(t *testing.T) {
tests := map[string]struct {
val FqPie
err1 error
err2 error
}{
"simple": {val: FqPie{ // Used defaults from https://man7.org/linux/man-pages/man8/tc-fq_pie.8.html
Limit: uint32Ptr(10240),
Flows: uint32Ptr(1024),
Target: uint32Ptr(15),
TUpdate: uint32Ptr(15),
Alpha: uint32Ptr(2),
Beta: uint32Ptr(20),
Quantum: uint32Ptr(1514),
MemoryLimit: uint32Ptr(32),
EcnProb: uint32Ptr(10),
Ecn: uint32Ptr(0),
Bytemode: uint32Ptr(0),
DqRateEstimator: uint32Ptr(0)}},
}

for name, testcase := range tests {
t.Run(name, func(t *testing.T) {
data, err1 := marshalFqPie(&testcase.val)
if err1 != nil {
if errors.Is(err1, testcase.err1) {
return
}
t.Fatalf("Unexpected error: %v", err1)
}
val := FqPie{}
err2 := unmarshalFqPie(data, &val)
if err2 != nil {
if errors.Is(err2, testcase.err2) {
return
}
t.Fatalf("Unexpected error: %v", err2)

}
if diff := cmp.Diff(val, testcase.val); diff != "" {
t.Fatalf("FqPie mismatch (want +got):\n%s", diff)
}
})
}
t.Run("nil", func(t *testing.T) {
_, err := marshalFqPie(nil)
if !errors.Is(err, ErrNoArg) {
t.Fatalf("unexpected error: %v", err)
}
})
t.Run("nil", func(t *testing.T) {
err := unmarshalFqPie([]byte{0x0}, nil)
if err == nil {
t.Fatalf("expected error but got none")
}
})
}
2 changes: 2 additions & 0 deletions qdisc.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ func validateQdiscObject(action int, info *Object) ([]tcOption, error) {
data, err = marshalAtm(info.Atm)
case "fq_codel":
data, err = marshalFqCodel(info.FqCodel)
case "fq_pie":
data, err = marshalFqPie(info.FqPie)
case "htb":
data, err = marshalHtb(info.Htb)
case "netem":
Expand Down
7 changes: 5 additions & 2 deletions qdisc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ func TestQdisc(t *testing.T) {
kind string
err error
fqCodel *FqCodel
fqPie *FqPie
red *Red
sfb *Sfb
sfq *Sfq
Expand All @@ -57,8 +58,9 @@ func TestQdisc(t *testing.T) {
kind: "fq_codel",
fqCodel: &FqCodel{Target: uint32Ptr(42), Limit: uint32Ptr(0xCAFE)},
},
"red": {kind: "red", red: &Red{MaxP: uint32Ptr(42)}},
"sfb": {kind: "sfb", sfb: &Sfb{Parms: &SfbQopt{Max: 0xFF}}},
"fq_pie": {kind: "fq_pie", fqPie: &FqPie{Target: uint32Ptr(15)}},
"red": {kind: "red", red: &Red{MaxP: uint32Ptr(42)}},
"sfb": {kind: "sfb", sfb: &Sfb{Parms: &SfbQopt{Max: 0xFF}}},
"sfq": {kind: "sfq", sfq: &Sfq{V0: SfqQopt{
PerturbPeriod: 64,
Limit: 3000,
Expand Down Expand Up @@ -113,6 +115,7 @@ func TestQdisc(t *testing.T) {
Kind: testcase.kind,
Cbs: testcase.cbs,
FqCodel: testcase.fqCodel,
FqPie: testcase.fqPie,
Red: testcase.red,
Sfb: testcase.sfb,
Sfq: testcase.sfq,
Expand Down
12 changes: 12 additions & 0 deletions structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,15 @@ type FqQdStats struct {
BandPktCount [3]uint32 // FQ_BANDS = 3
_ uint32 // padding
}

// FqPieXStats from include/uapi/linux/pkt_sched.h
type FqPieXStats struct {
PacketsIn uint32
Dropped uint32
OverLimit uint32
OverMemory uint32
EcnMark uint32
NewFlowsCount uint32
OldFlowsCount uint32
MemoryUsage uint32
}
4 changes: 4 additions & 0 deletions tc.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ type Attribute struct {
// Classless qdiscs
Cake *Cake
FqCodel *FqCodel
FqPie *FqPie
Codel *Codel
Fq *Fq
Pie *Pie
Expand Down Expand Up @@ -237,6 +238,7 @@ type XStats struct {
Hhf *HhfXStats
Pie *PieXStats
FqCodel *FqCodelXStats
FqPie *FqPieXStats
Fq *FqQdStats
Hfsc *HfscXStats
}
Expand All @@ -262,6 +264,8 @@ func marshalXStats(v XStats) ([]byte, error) {
return marshalStruct(v.Pie)
} else if v.FqCodel != nil {
return marshalFqCodelXStats(v.FqCodel)
} else if v.FqPie != nil {
return marshalStruct(v.FqPie)
}
return []byte{}, fmt.Errorf("could not marshal XStat")
}
Expand Down
2 changes: 2 additions & 0 deletions tc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,8 @@ func alterResponses(t *testing.T, cache *[]netlink.Message) []byte {
switch obj.Kind {
case "fq_codel":
data, err = marshalXStats(XStats{FqCodel: &FqCodelXStats{Type: 0, Qd: &FqCodelQdStats{}}})
case "fq_pie":
data, err = marshalXStats(XStats{FqPie: &FqPieXStats{PacketsIn: 0, Dropped: 1, OverLimit: 2, OverMemory: 3, EcnMark: 4, NewFlowsCount: 5, OldFlowsCount: 6, MemoryUsage: 7}})
case "sfb":
data, err = marshalXStats(XStats{Sfb: &SfbXStats{EarlyDrop: 1, PenaltyDrop: 2, AvgProb: 42}})
case "sfq":
Expand Down