[PATCH iproute2-next v3] Add support for printing per-flow PIE statistics exposed by the kernel via the new TCA_FQ_PIE_XSTATS_CLASS type in tc_fq_pie_xstats.
From: Hemendra M. Naik <hidden>
Date: 2026-06-30 18:45:12
Also in:
lkml
Subsystem:
tc subsystem, the rest · Maintainers:
Jamal Hadi Salim, Jiri Pirko, Linus Torvalds
'tc -s class show' against an fq_pie qdisc now prints:
prob drop probability for the flow
delay per-flow queue sojourn time (microseconds)
deficit remaining DRR byte credits (signed integer)
avg_dq_rate dequeue rate estimate in bytes/second
(dq_rate_estimator mode only)
avg_dq_rate is formatted using tc_print_rate(), which converts the
kernel's bytes/second value to a human-readable bits/second string
(e.g. '3906Kbit'), consistent with how other tc schedulers display
rate fields. Apply the same fix to tc/q_pie.c, where avg_dq_rate was
also printed as a raw integer without a unit.
Update the UAPI header to mirror tc_fq_pie_cl_stats from the kernel.
Fix the 'delay' field comment in struct tc_pie_xstats from "in ms" to
"in microseconds" to match the kernel's
PSCHED_TICKS2NS / NSEC_PER_USEC conversion.
Add a 'tc -s class show' example to tc-fq_pie(8) with dq_rate_estimator
enabled, showing all per-flow fields (prob, delay, deficit, avg_dq_rate)
across multiple flows. Update tc-pie(8) avg_dq_rate example from a raw
integer to a formatted bits/second string.
The corresponding kernel patch can be viewed here:
https://lore.kernel.org/netdev/20260630183702.170798-1-hemendranaik@gmail.com/ (local)
Signed-off-by: Hemendra M. Naik <redacted>
Signed-off-by: Vishal Kamath <redacted>
Signed-off-by: Mohit P. Tahiliani <redacted>
---
Changelog:
v3:
- Moved print_nl() into each xstats type block to preserve output formatting.
v2:
- Addressed ABI backward compatibility issue. (https://lore.kernel.org/netdev/20260614130729.10076-1-hemendranaik@gmail.com/ (local))
v1:
- https://lore.kernel.org/netdev/20260531131411.28213-1-hemendranaik@gmail.com/ (local)
---
include/uapi/linux/pkt_sched.h | 29 +++++++++++++++--
man/man8/tc-fq_pie.8 | 18 +++++++++++
man/man8/tc-pie.8 | 2 +-
tc/q_fq_pie.c | 59 +++++++++++++++++++++++-----------
tc/q_pie.c | 4 +--
5 files changed, 88 insertions(+), 24 deletions(-)
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index d1f67ef8..f56f24ad 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h@@ -920,9 +920,9 @@ enum { struct tc_pie_xstats { __u64 prob; /* current probability */ - __u32 delay; /* current delay in ms */ + __u32 delay; /* current delay in microseconds */ __u32 avg_dq_rate; /* current average dq_rate in - * bits/pie_time + * bytes/second */ __u32 dq_rate_estimating; /* is avg_dq_rate being calculated? */ __u32 packets_in; /* total number of packets enqueued */
@@ -953,6 +953,25 @@ enum { }; #define TCA_FQ_PIE_MAX (__TCA_FQ_PIE_MAX - 1) +enum { + TCA_FQ_PIE_XSTATS_QDISC, + TCA_FQ_PIE_XSTATS_CLASS, +}; + +struct tc_fq_pie_cl_stats { + __u64 prob; /* current probability */ + __u32 delay; /* current delay in microseconds */ + __s32 deficit; /* number of remaining byte credits */ + __u32 avg_dq_rate; /* current average dq_rate in + * bytes/second + */ + __u32 dq_rate_estimating; /* is avg_dq_rate being calculated? */ +}; + +struct tc_fq_pie_xqd_stats { + /* placeholder for new qdisc-level stats */ +}; + struct tc_fq_pie_xstats { __u32 packets_in; /* total number of packets enqueued */ __u32 dropped; /* packets dropped due to fq_pie_action */
@@ -963,6 +982,12 @@ struct tc_fq_pie_xstats { __u32 new_flows_len; /* count of flows in new list */ __u32 old_flows_len; /* count of flows in old list */ __u32 memory_usage; /* total memory across all queues */ + __u32 type; + union { + struct tc_fq_pie_cl_stats class_stats; + struct tc_fq_pie_xqd_stats xqdisc_stats; + }; + }; /* CBS */
diff --git a/man/man8/tc-fq_pie.8 b/man/man8/tc-fq_pie.8
index 457a56bb..bf988f5f 100644
--- a/man/man8/tc-fq_pie.8
+++ b/man/man8/tc-fq_pie.8@@ -153,6 +153,24 @@ dq_rate_estimator pkts_in 6082 overlimit 0 overmemory 0 dropped 4 ecn_mark 0 new_flow_count 94 new_flows_len 0 old_flows_len 8 memory_used 1157632 +# tc qdisc add dev eth0 parent 100:1 handle 200: fq_pie target 2ms flows 3 +.br +# tc -s class show dev eth0 +.br +class fq_pie 200:2 parent 200: + Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) + backlog 22800b 2p requeues 0 + prob 0.57679 delay 2.38ms + +# tc qdisc add dev eth0 parent 100:1 handle 200: fq_pie target 2ms flows 3 dq_rate_estimator +.br +# tc -s class show dev eth0 +.br +class fq_pie 200:2 parent 200: + Sent 0 bytes 0 pkt (dropped 0, overlimits 0 requeues 0) + backlog 22800b 2p requeues 0 + prob 0.57679 delay 2.38ms avg_dq_rate 10742Kbit + .SH SEE ALSO .BR tc (8), .BR tc-pie (8),
diff --git a/man/man8/tc-pie.8 b/man/man8/tc-pie.8
index 5a8c7820..f3a09616 100644
--- a/man/man8/tc-pie.8
+++ b/man/man8/tc-pie.8@@ -115,7 +115,7 @@ is turned off. qdisc pie 8036: dev eth0 root refcnt 2 limit 1000p target 15.0ms tupdate 16.0ms alpha 2 beta 20 Sent 63947420 bytes 42414 pkt (dropped 41, overlimits 0 requeues 0) backlog 271006b 179p requeues 0 - prob 0.000092 delay 22200us avg_dq_rate 12145996 + prob 0.000092 delay 22200us avg_dq_rate 10742Kbit pkts_in 41 overlimit 343 dropped 0 maxq 50 ecn_mark 0 # tc qdisc add dev eth0 root pie limit 100 target 20ms tupdate 30ms ecn
diff --git a/tc/q_fq_pie.c b/tc/q_fq_pie.c
index dc2710cd..1b89ee3a 100644
--- a/tc/q_fq_pie.c
+++ b/tc/q_fq_pie.c@@ -274,6 +274,8 @@ static int fq_pie_print_xstats(const struct qdisc_util *qu, FILE *f, { struct tc_fq_pie_xstats _st = {}, *st; + SPRINT_BUF(b1); + if (xstats == NULL) return 0;
@@ -283,25 +285,44 @@ static int fq_pie_print_xstats(const struct qdisc_util *qu, FILE *f, st = &_st; } - print_uint(PRINT_ANY, "pkts_in", " pkts_in %u", - st->packets_in); - print_uint(PRINT_ANY, "overlimit", " overlimit %u", - st->overlimit); - print_uint(PRINT_ANY, "overmemory", " overmemory %u", - st->overmemory); - print_uint(PRINT_ANY, "dropped", " dropped %u", - st->dropped); - print_uint(PRINT_ANY, "ecn_mark", " ecn_mark %u", - st->ecn_mark); - print_nl(); - print_uint(PRINT_ANY, "new_flow_count", " new_flow_count %u", - st->new_flow_count); - print_uint(PRINT_ANY, "new_flows_len", " new_flows_len %u", - st->new_flows_len); - print_uint(PRINT_ANY, "old_flows_len", " old_flows_len %u", - st->old_flows_len); - print_uint(PRINT_ANY, "memory_used", " memory_used %u", - st->memory_usage); + if (!st->type || st->type == TCA_FQ_PIE_XSTATS_QDISC) { + print_uint(PRINT_ANY, "pkts_in", " pkts_in %u", + st->packets_in); + print_uint(PRINT_ANY, "overlimit", " overlimit %u", + st->overlimit); + print_uint(PRINT_ANY, "overmemory", " overmemory %u", + st->overmemory); + print_uint(PRINT_ANY, "dropped", " dropped %u", + st->dropped); + print_uint(PRINT_ANY, "ecn_mark", " ecn_mark %u", + st->ecn_mark); + print_nl(); + print_uint(PRINT_ANY, "new_flow_count", " new_flow_count %u", + st->new_flow_count); + print_uint(PRINT_ANY, "new_flows_len", " new_flows_len %u", + st->new_flows_len); + print_uint(PRINT_ANY, "old_flows_len", " old_flows_len %u", + st->old_flows_len); + print_uint(PRINT_ANY, "memory_used", " memory_used %u", + st->memory_usage); + print_nl(); + } + + if (st->type == TCA_FQ_PIE_XSTATS_CLASS) { + print_float(PRINT_ANY, "prob", " prob %lg", + (double)st->class_stats.prob / (double)UINT64_MAX); + print_uint(PRINT_JSON, "delay", NULL, st->class_stats.delay); + print_string(PRINT_FP, NULL, " delay %s", + sprint_time(st->class_stats.delay, b1)); + print_int(PRINT_ANY, "deficit", " deficit %d", + st->class_stats.deficit); + + if (st->class_stats.dq_rate_estimating) { + tc_print_rate(PRINT_ANY, "avg_dq_rate", " avg_dq_rate %s", + st->class_stats.avg_dq_rate); + } + print_nl(); + } return 0;
diff --git a/tc/q_pie.c b/tc/q_pie.c
index 04c9aa61..abae1ced 100644
--- a/tc/q_pie.c
+++ b/tc/q_pie.c@@ -220,8 +220,8 @@ static int pie_print_xstats(const struct qdisc_util *qu, FILE *f, print_string(PRINT_FP, NULL, " delay %s", sprint_time(st->delay, b1)); if (st->dq_rate_estimating) - print_uint(PRINT_ANY, "avg_dq_rate", " avg_dq_rate %u", - st->avg_dq_rate); + tc_print_rate(PRINT_ANY, "avg_dq_rate", " avg_dq_rate %s", + st->avg_dq_rate); print_nl(); print_uint(PRINT_ANY, "pkts_in", " pkts_in %u", st->packets_in);
--
2.34.1