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 committed Sep 30, 2020
1 parent 1e3d26b commit 88f673d
Show file tree
Hide file tree
Showing 5 changed files with 225 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 + 0x44
)

// 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 // 1
Perturb int32 // 4
Limit uint32 // 4
Divisor uint8 // 1
Flows uint8 // 1
}

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 SizeofTcSfqRedStats
}

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

func (x *TcSfqQoptV1) Serialize() []byte {
return (*(*[SizeofTcSfqRedStats]byte)(unsafe.Pointer(x)))[:]
}
25 changes: 25 additions & 0 deletions qdisc.go
Original file line number Diff line number Diff line change
Expand Up @@ -338,3 +338,28 @@ 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
Flows uint8
}

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

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

func (qdisc *Sfq) Type() string {
return "sfq"
}
27 changes: 27 additions & 0 deletions qdisc_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,15 @@ 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
opt.TcSfqQopt.Flows = qdisc.Flows

options = nl.NewRtAttr(nl.TCA_OPTIONS, opt.Serialize())
default:
options = nil
}
Expand Down Expand Up @@ -362,6 +371,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 +428,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 +597,18 @@ 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.Quantum
sfq.Perturb = uint8(opt.Perturb)
sfq.Limit = opt.Limit
sfq.Divisor = opt.Divisor
sfq.Flows = opt.Flows

return nil
}

const (
TIME_UNITS_PER_SEC = 1000000
)
Expand Down
69 changes: 69 additions & 0 deletions qdisc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,75 @@ 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: 3,
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 sfq.Flows != qdisc.Flows {
t.Fatal("Flows 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 88f673d

Please sign in to comment.