From 8e1ae8a844fdda81e40a40fbf6c0a991af9080d9 Mon Sep 17 00:00:00 2001 From: Kangjie Xu Date: Tue, 4 Jun 2024 20:37:44 -0700 Subject: [PATCH] qdisc: add TCA_XSTATS stats for fq qdisc. When using fq, we rely on xstats useful metrics to check fq behavior and diangosis, it is what can be seen from iproute2 tools, `tc -s qdisc show` as well. It exposes metrics as: https://github.com/iproute2/iproute2/blob/main/include/uapi/linux/pkt_sched.h#L847 Signed-off-by: Kangjie Xu --- class_linux.go | 10 ++++++++++ nl/tc_linux.go | 18 ++++++++++++++++++ qdisc.go | 4 ++++ qdisc_linux.go | 10 ++++++++++ qdisc_test.go | 4 ++++ 5 files changed, 46 insertions(+) diff --git a/class_linux.go b/class_linux.go index a82eb09d..18d3bba5 100644 --- a/class_linux.go +++ b/class_linux.go @@ -398,3 +398,13 @@ func parseTcStats2(data []byte) (*ClassStatistics, error) { return stats, nil } + +func parseTcFqXStats(data []byte) (*nl.TcFqQdStats, error) { + buf := &bytes.Buffer{} + buf.Write(data) + stats := &nl.TcFqQdStats{} + if err := binary.Read(buf, native, stats); err != nil { + return nil, err + } + return stats, nil +} diff --git a/nl/tc_linux.go b/nl/tc_linux.go index 0720729a..47f0eb22 100644 --- a/nl/tc_linux.go +++ b/nl/tc_linux.go @@ -954,6 +954,24 @@ const ( TCA_FQ_HORIZON_DROP // drop packets beyond horizon, or cap their EDT ) +type TcFqQdStats struct { + GcFlows uint64 + HighPrioPackets uint64 + TcpRetrans uint64 + Throttled uint64 + FlowsPlimit uint64 + PktsTooLong uint64 + AllocationErrors uint64 + TimeNextDelayedFlow int64 + Flows uint32 + InactiveFlows uint32 + ThrottledFlows uint32 + UnthrottleLatencyNs uint32 + CeMark uint64 // packets above ce_threshold + HorizonDrops uint64 + HorizonCaps uint64 +} + const ( TCA_FQ_CODEL_UNSPEC = iota TCA_FQ_CODEL_TARGET diff --git a/qdisc.go b/qdisc.go index 6f5d6df4..f27d4b94 100644 --- a/qdisc.go +++ b/qdisc.go @@ -3,6 +3,8 @@ package netlink import ( "fmt" "math" + + "github.com/vishvananda/netlink/nl" ) const ( @@ -308,6 +310,8 @@ type Fq struct { LowRateThreshold uint32 Horizon uint32 HorizonDropPolicy uint8 + + Stats *nl.TcFqQdStats } func (fq *Fq) String() string { diff --git a/qdisc_linux.go b/qdisc_linux.go index 3c3780d3..98df7e4e 100644 --- a/qdisc_linux.go +++ b/qdisc_linux.go @@ -488,6 +488,16 @@ func (h *Handle) QdiscList(link Link) ([]Qdisc, error) { return nil, err } base.Statistics = (*QdiscStatistics)(s) + case nl.TCA_XSTATS: + switch qdisc.Type() { + case "fq": + fq := qdisc.(*Fq) + s, err := parseTcFqXStats(attr.Value) + if err != nil { + return nil, err + } + fq.Stats = s + } } } *qdisc.Attrs() = base diff --git a/qdisc_test.go b/qdisc_test.go index 24307484..870478b4 100644 --- a/qdisc_test.go +++ b/qdisc_test.go @@ -515,6 +515,10 @@ func TestFqHorizon(t *testing.T) { t.Fatal("HorizonDropPolicy does not match") } + if fq.Stats.GcFlows != 0 || fq.Stats.ThrottledFlows != 0 || fq.Stats.HorizonDrops != 0 { + t.Fatal("Stats is not zero") + } + if err := QdiscDel(qdisc); err != nil { t.Fatal(err) }