Skip to content

Commit

Permalink
added support for SFQ qdiscs
Browse files Browse the repository at this point in the history
  • Loading branch information
imilchev authored and vishvananda committed Oct 19, 2020
1 parent 1e3d26b commit abd6d09
Show file tree
Hide file tree
Showing 5 changed files with 219 additions and 0 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ before_script:
- sudo modprobe nf_conntrack_ipv4
- sudo modprobe nf_conntrack_ipv6
- sudo modprobe sch_hfsc
- sudo modprobe sch_sfq
install:
- go get -v -t ./...
go_import_path: github.com/vishvananda/netlink
103 changes: 103 additions & 0 deletions nl/tc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,9 @@ const (
SizeofTcTunnelKey = SizeofTcGen + 0x04
SizeofTcSkbEdit = SizeofTcGen
SizeofTcPolice = 2*SizeofTcRateSpec + 0x20
SizeofTcSfqQopt = 0x0b
SizeofTcSfqRedStats = 0x18
SizeofTcSfqQoptV1 = SizeofTcSfqQopt + SizeofTcSfqRedStats + 0x1c
)

// struct tcmsg {
Expand Down Expand Up @@ -878,3 +881,103 @@ const (
TCA_HFSC_FSC
TCA_HFSC_USC
)

// struct tc_sfq_qopt {
// unsigned quantum; /* Bytes per round allocated to flow */
// int perturb_period; /* Period of hash perturbation */
// __u32 limit; /* Maximal packets in queue */
// unsigned divisor; /* Hash divisor */
// unsigned flows; /* Maximal number of flows */
// };

type TcSfqQopt struct {
Quantum uint8
Perturb int32
Limit uint32
Divisor uint8
Flows uint8
}

func (x *TcSfqQopt) Len() int {
return SizeofTcSfqQopt
}

func DeserializeTcSfqQopt(b []byte) *TcSfqQopt {
return (*TcSfqQopt)(unsafe.Pointer(&b[0:SizeofTcSfqQopt][0]))
}

func (x *TcSfqQopt) Serialize() []byte {
return (*(*[SizeofTcSfqQopt]byte)(unsafe.Pointer(x)))[:]
}

// struct tc_sfqred_stats {
// __u32 prob_drop; /* Early drops, below max threshold */
// __u32 forced_drop; /* Early drops, after max threshold */
// __u32 prob_mark; /* Marked packets, below max threshold */
// __u32 forced_mark; /* Marked packets, after max threshold */
// __u32 prob_mark_head; /* Marked packets, below max threshold */
// __u32 forced_mark_head;/* Marked packets, after max threshold */
// };
type TcSfqRedStats struct {
ProbDrop uint32
ForcedDrop uint32
ProbMark uint32
ForcedMark uint32
ProbMarkHead uint32
ForcedMarkHead uint32
}

func (x *TcSfqRedStats) Len() int {
return SizeofTcSfqRedStats
}

func DeserializeTcSfqRedStats(b []byte) *TcSfqRedStats {
return (*TcSfqRedStats)(unsafe.Pointer(&b[0:SizeofTcSfqRedStats][0]))
}

func (x *TcSfqRedStats) Serialize() []byte {
return (*(*[SizeofTcSfqRedStats]byte)(unsafe.Pointer(x)))[:]
}

// struct tc_sfq_qopt_v1 {
// struct tc_sfq_qopt v0;
// unsigned int depth; /* max number of packets per flow */
// unsigned int headdrop;
// /* SFQRED parameters */
// __u32 limit; /* HARD maximal flow queue length (bytes) */
// __u32 qth_min; /* Min average length threshold (bytes) */
// __u32 qth_max; /* Max average length threshold (bytes) */
// unsigned char Wlog; /* log(W) */
// unsigned char Plog; /* log(P_max/(qth_max-qth_min)) */
// unsigned char Scell_log; /* cell size for idle damping */
// unsigned char flags;
// __u32 max_P; /* probability, high resolution */
// /* SFQRED stats */
// struct tc_sfqred_stats stats;
// };
type TcSfqQoptV1 struct {
TcSfqQopt
Depth uint32
HeadDrop uint32
Limit uint32
QthMin uint32
QthMax uint32
Wlog byte
Plog byte
ScellLog byte
Flags byte
MaxP uint32
TcSfqRedStats
}

func (x *TcSfqQoptV1) Len() int {
return SizeofTcSfqQoptV1
}

func DeserializeTcSfqQoptV1(b []byte) *TcSfqQoptV1 {
return (*TcSfqQoptV1)(unsafe.Pointer(&b[0:SizeofTcSfqQoptV1][0]))
}

func (x *TcSfqQoptV1) Serialize() []byte {
return (*(*[SizeofTcSfqQoptV1]byte)(unsafe.Pointer(x)))[:]
}
24 changes: 24 additions & 0 deletions qdisc.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,27 @@ func (qdisc *FqCodel) Attrs() *QdiscAttrs {
func (qdisc *FqCodel) Type() string {
return "fq_codel"
}

type Sfq struct {
QdiscAttrs
// TODO: Only the simplified options for SFQ are handled here. Support for the extended one can be added later.
Quantum uint8
Perturb uint8
Limit uint32
Divisor uint8
}

func (sfq *Sfq) String() string {
return fmt.Sprintf(
"{%v -- Quantum: %v, Perturb: %v, Limit: %v, Divisor: %v}",
sfq.Attrs(), sfq.Quantum, sfq.Perturb, sfq.Limit, sfq.Divisor,
)
}

func (qdisc *Sfq) Attrs() *QdiscAttrs {
return &qdisc.QdiscAttrs
}

func (qdisc *Sfq) Type() string {
return "sfq"
}
25 changes: 25 additions & 0 deletions qdisc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,14 @@ func qdiscPayload(req *nl.NetlinkRequest, qdisc Qdisc) error {
if qdisc.FlowDefaultRate > 0 {
options.AddRtAttr(nl.TCA_FQ_FLOW_DEFAULT_RATE, nl.Uint32Attr((uint32(qdisc.FlowDefaultRate))))
}
case *Sfq:
opt := nl.TcSfqQoptV1{}
opt.TcSfqQopt.Quantum = qdisc.Quantum
opt.TcSfqQopt.Perturb = int32(qdisc.Perturb)
opt.TcSfqQopt.Limit = qdisc.Limit
opt.TcSfqQopt.Divisor = qdisc.Divisor

options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
default:
options = nil
}
Expand Down Expand Up @@ -362,6 +370,8 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
qdisc = &FqCodel{}
case "netem":
qdisc = &Netem{}
case "sfq":
qdisc = &Sfq{}
default:
qdisc = &GenericQdisc{QdiscType: qdiscType}
}
Expand Down Expand Up @@ -417,6 +427,10 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) {
if err := parseNetemData(qdisc, attr.Value); err != nil {
return nil, err
}
case "sfq":
if err := parseSfqData(qdisc, attr.Value); err != nil {
return nil, err
}

// no options for ingress
}
Expand Down Expand Up @@ -582,6 +596,17 @@ func parseTbfData(qdisc Qdisc, data []syscall.NetlinkRouteAttr) error {
return nil
}

func parseSfqData(qdisc Qdisc, value []byte) error {
sfq := qdisc.(*Sfq)
opt := nl.DeserializeTcSfqQoptV1(value)
sfq.Quantum = opt.TcSfqQopt.Quantum
sfq.Perturb = uint8(opt.TcSfqQopt.Perturb)
sfq.Limit = opt.TcSfqQopt.Limit
sfq.Divisor = opt.TcSfqQopt.Divisor

return nil
}

const (
TIME_UNITS_PER_SEC = 1000000
)
Expand Down
66 changes: 66 additions & 0 deletions qdisc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,72 @@ func TestHtbAddDel(t *testing.T) {
}
}

func TestSfqAddDel(t *testing.T) {
tearDown := setUpNetlinkTestWithKModule(t, "sch_sfq")
defer tearDown()
if err := LinkAdd(&Ifb{LinkAttrs{Name: "foo"}}); err != nil {
t.Fatal(err)
}
link, err := LinkByName("foo")
if err != nil {
t.Fatal(err)
}
if err := LinkSetUp(link); err != nil {
t.Fatal(err)
}

attrs := QdiscAttrs{
LinkIndex: link.Attrs().Index,
Handle: MakeHandle(1, 0),
Parent: HANDLE_ROOT,
}

qdisc := Sfq{
QdiscAttrs: attrs,
Quantum: 2,
Perturb: 11,
Limit: 123,
Divisor: 4,
}
if err := QdiscAdd(&qdisc); err != nil {
t.Fatal(err)
}

qdiscs, err := SafeQdiscList(link)
if err != nil {
t.Fatal(err)
}
if len(qdiscs) != 1 {
t.Fatal("Failed to add qdisc")
}
sfq, ok := qdiscs[0].(*Sfq)
if !ok {
t.Fatal("Qdisc is the wrong type")
}
if sfq.Quantum != qdisc.Quantum {
t.Fatal("Quantum doesn't match")
}
if sfq.Perturb != qdisc.Perturb {
t.Fatal("Perturb doesn't match")
}
if sfq.Limit != qdisc.Limit {
t.Fatal("Limit doesn't match")
}
if sfq.Divisor != qdisc.Divisor {
t.Fatal("Divisor doesn't match")
}
if err := QdiscDel(&qdisc); err != nil {
t.Fatal(err)
}
qdiscs, err = SafeQdiscList(link)
if err != nil {
t.Fatal(err)
}
if len(qdiscs) != 0 {
t.Fatal("Failed to remove qdisc")
}
}

func TestPrioAddDel(t *testing.T) {
tearDown := setUpNetlinkTest(t)
defer tearDown()
Expand Down

0 comments on commit abd6d09

Please sign in to comment.