* [PATCH net-next v4 0/3] net/sched: sch_fq_pie: add per-flow class statistics
@ 2026-07-27 16:40 Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops Hemendra M. Naik
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Hemendra M. Naik @ 2026-07-27 16:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, jhs, shuah,
linux-kernel, linux-kselftest, vishy0777, tahiliani,
Hemendra M. Naik
FQ-PIE runs an independent PIE controller per flow but exposes no
per-flow statistics. This series wires up fq_pie_class_ops to expose
per-flow AQM state (prob, delay, deficit, avg_dq_rate)
via 'tc -s class show', following a similar pattern as FQ-CoDel.
---
Changelog:
v4:
- Fixed unaligned commit message; moved typo fixes to another patch.
v3: https://lore.kernel.org/netdev/20260630183702.170798-1-hemendranaik@gmail.com/
- No changes since v2.
- Resent after the previous submission was deferred due to the
net-next tree closing during review.
- Updated corresponding iproute2 patch in response to review comments;
kernel patches unchanged.
v2: https://lore.kernel.org/netdev/20260614125000.6058-1-hemendranaik@gmail.com/
- Addressed ABI backward compatibility issue for tc_fq_pie_xstats.
v1: https://lore.kernel.org/netdev/20260531125314.22492-1-hemendranaik@gmail.com/
- Initial submission
Hemendra M. Naik (3):
net/sched: sch_fq_pie: add per-flow statistics via class ops
selftests: tc-testing: add fq_pie per-flow class stats test
net/sched: pie: correct tc_pie_xstats field documentation
include/uapi/linux/pkt_sched.h | 29 ++++-
net/sched/sch_fq_pie.c | 109 +++++++++++++++++-
tools/include/uapi/linux/pkt_sched.h | 4 +-
.../tc-testing/tc-tests/qdiscs/fq_pie.json | 22 ++++
4 files changed, 159 insertions(+), 5 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops
2026-07-27 16:40 [PATCH net-next v4 0/3] net/sched: sch_fq_pie: add per-flow class statistics Hemendra M. Naik
@ 2026-07-27 16:40 ` Hemendra M. Naik
2026-07-31 9:56 ` Simon Horman
2026-07-27 16:40 ` [PATCH net-next v4 2/3] selftests: tc-testing: add fq_pie per-flow class stats test Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 3/3] net/sched: pie: correct tc_pie_xstats field documentation Hemendra M. Naik
2 siblings, 1 reply; 5+ messages in thread
From: Hemendra M. Naik @ 2026-07-27 16:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, jhs, shuah,
linux-kernel, linux-kselftest, vishy0777, tahiliani,
Hemendra M. Naik
FQ-PIE schedules independent PIE controllers per flow but exposes no
per-flow AQM state. Without class-level statistics there is no way to
observe the per-flow drop probability, queue delay, deficit or
dequeue rate from userspace.
Extend tc_fq_pie_xstats to support both qdisc and class-level
extended statistics.
- Add enum with QDISC and CLASS type discriminators.
- Add struct tc_fq_pie_cl_stats for per-flow metrics (prob,
delay, deficit, avg_dq_rate, dq_rate_estimating).
- Add empty struct tc_fq_pie_xqd_stats placeholder.
Wire up fq_pie_class_ops (.walk, .dump, .dump_stats) so that
'tc -s class show' against an fq_pie qdisc reports per-flow state:
prob per-flow PIE drop probability
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)
dq_rate_estimating flag indicating whether avg_dq_rate
is being calculated
Signed-off-by: Hemendra M. Naik <hemendranaik@gmail.com>
Signed-off-by: Vishal Kamath <vishy0777@gmail.com>
Signed-off-by: Mohit P. Tahiliani <tahiliani@nitk.edu.in>
---
include/uapi/linux/pkt_sched.h | 25 ++++++++
net/sched/sch_fq_pie.c | 109 ++++++++++++++++++++++++++++++++-
2 files changed, 133 insertions(+), 1 deletion(-)
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 490efd288526..9a86fa4d6846 100644
--- a/include/uapi/linux/pkt_sched.h
+++ b/include/uapi/linux/pkt_sched.h
@@ -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/net/sched/sch_fq_pie.c b/net/sched/sch_fq_pie.c
index 72f48fa4010b..664fef18568b 100644
--- a/net/sched/sch_fq_pie.c
+++ b/net/sched/sch_fq_pie.c
@@ -509,7 +509,9 @@ static int fq_pie_dump(struct Qdisc *sch, struct sk_buff *skb)
static int fq_pie_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
{
struct fq_pie_sched_data *q = qdisc_priv(sch);
- struct tc_fq_pie_xstats st = { 0 };
+ struct tc_fq_pie_xstats st = {
+ .type = TCA_FQ_PIE_XSTATS_QDISC,
+ };
struct list_head *pos;
sch_tree_lock(sch);
@@ -561,7 +563,112 @@ static void fq_pie_destroy(struct Qdisc *sch)
kvfree(q->flows);
}
+static struct Qdisc *fq_pie_leaf(struct Qdisc *sch, unsigned long arg)
+{
+ return NULL;
+}
+
+static unsigned long fq_pie_find(struct Qdisc *sch, u32 classid)
+{
+ return 0;
+}
+
+static unsigned long fq_pie_bind(struct Qdisc *sch, unsigned long parent,
+ u32 classid)
+{
+ return 0;
+}
+
+static void fq_pie_unbind(struct Qdisc *q, unsigned long cl)
+{
+}
+
+static struct tcf_block *fq_pie_tcf_block(struct Qdisc *sch, unsigned long cl,
+ struct netlink_ext_ack *extack)
+{
+ struct fq_pie_sched_data *q = qdisc_priv(sch);
+
+ if (cl)
+ return NULL;
+ return q->block;
+}
+
+static int fq_pie_dump_class(struct Qdisc *sch, unsigned long cl,
+ struct sk_buff *skb, struct tcmsg *tcm)
+{
+ tcm->tcm_handle |= TC_H_MIN(cl);
+ return 0;
+}
+
+static int fq_pie_dump_class_stats(struct Qdisc *sch, unsigned long cl,
+ struct gnet_dump *d)
+{
+ struct fq_pie_sched_data *q = qdisc_priv(sch);
+ struct gnet_stats_queue qs = { 0 };
+ struct tc_fq_pie_xstats xstats;
+ u32 idx = cl - 1;
+
+ if (idx < q->flows_cnt) {
+ const struct fq_pie_flow *flow = &q->flows[idx];
+
+ memset(&xstats, 0, sizeof(xstats));
+ xstats.type = TCA_FQ_PIE_XSTATS_CLASS;
+ xstats.class_stats.prob =
+ READ_ONCE(flow->vars.prob) << BITS_PER_BYTE;
+ xstats.class_stats.delay =
+ ((u32)PSCHED_TICKS2NS(READ_ONCE(flow->vars.qdelay))) /
+ NSEC_PER_USEC;
+ xstats.class_stats.deficit = READ_ONCE(flow->deficit);
+ xstats.class_stats.dq_rate_estimating =
+ READ_ONCE(q->p_params.dq_rate_estimator);
+
+ if (xstats.class_stats.dq_rate_estimating) {
+ xstats.class_stats.avg_dq_rate =
+ READ_ONCE(flow->vars.avg_dq_rate) *
+ (PSCHED_TICKS_PER_SEC) >> PIE_SCALE;
+ }
+
+ qs.qlen = READ_ONCE(flow->qlen);
+ qs.backlog = READ_ONCE(flow->backlog);
+ }
+ if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0)
+ return -1;
+ if (idx < q->flows_cnt)
+ return gnet_stats_copy_app(d, &xstats, sizeof(xstats));
+ return 0;
+}
+
+static void fq_pie_walk(struct Qdisc *sch, struct qdisc_walker *arg)
+{
+ struct fq_pie_sched_data *q = qdisc_priv(sch);
+ unsigned int i;
+
+ if (arg->stop)
+ return;
+
+ for (i = 0; i < q->flows_cnt; i++) {
+ if (list_empty(&q->flows[i].flowchain)) {
+ arg->count++;
+ continue;
+ }
+ if (!tc_qdisc_stats_dump(sch, i + 1, arg))
+ break;
+ }
+}
+
+static const struct Qdisc_class_ops fq_pie_class_ops = {
+ .leaf = fq_pie_leaf,
+ .find = fq_pie_find,
+ .tcf_block = fq_pie_tcf_block,
+ .bind_tcf = fq_pie_bind,
+ .unbind_tcf = fq_pie_unbind,
+ .dump = fq_pie_dump_class,
+ .dump_stats = fq_pie_dump_class_stats,
+ .walk = fq_pie_walk,
+};
+
static struct Qdisc_ops fq_pie_qdisc_ops __read_mostly = {
+ .cl_ops = &fq_pie_class_ops,
.id = "fq_pie",
.priv_size = sizeof(struct fq_pie_sched_data),
.enqueue = fq_pie_qdisc_enqueue,
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net-next v4 2/3] selftests: tc-testing: add fq_pie per-flow class stats test
2026-07-27 16:40 [PATCH net-next v4 0/3] net/sched: sch_fq_pie: add per-flow class statistics Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops Hemendra M. Naik
@ 2026-07-27 16:40 ` Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 3/3] net/sched: pie: correct tc_pie_xstats field documentation Hemendra M. Naik
2 siblings, 0 replies; 5+ messages in thread
From: Hemendra M. Naik @ 2026-07-27 16:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, jhs, shuah,
linux-kernel, linux-kselftest, vishy0777, tahiliani,
Hemendra M. Naik
Add a tc-testing entry (id: 83c0) to verify the fq_pie class ops
wired up in the previous patch do not crash and integrate cleanly
with the tc class show path.
The test creates an fq_pie root qdisc on a dummy interface and runs
'tc -s class show'.
Signed-off-by: Hemendra M. Naik <hemendranaik@gmail.com>
Signed-off-by: Vishal Kamath <vishy0777@gmail.com>
Signed-off-by: Mohit P. Tahiliani <tahiliani@nitk.edu.in>
---
.../tc-testing/tc-tests/qdiscs/fq_pie.json | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json
index 229fe1bf4a90..88139f429430 100644
--- a/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json
+++ b/tools/testing/selftests/tc-testing/tc-tests/qdiscs/fq_pie.json
@@ -40,5 +40,27 @@
"matchPattern": "qdisc fq_pie 1: root refcnt [0-9]+ limit 1p",
"matchCount": "1",
"teardown": ["$TC qdisc del dev $DEV1 handle 1: root"]
+ },
+ {
+ "id": "83c0",
+ "name": "FQ-PIE class stats accessible via tc class show",
+ "category": [
+ "qdisc",
+ "fq_pie"
+ ],
+ "plugins": {
+ "requires": "nsPlugin"
+ },
+ "setup": [
+ "$TC qdisc add dev $DUMMY handle 1: root fq_pie"
+ ],
+ "cmdUnderTest": "$TC -s class show dev $DUMMY",
+ "expExitCode": "0",
+ "verifyCmd": "$TC -s class show dev $DUMMY",
+ "matchPattern": "class fq_pie",
+ "matchCount": "0",
+ "teardown": [
+ "$TC qdisc del dev $DUMMY handle 1: root"
+ ]
}
]
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH net-next v4 3/3] net/sched: pie: correct tc_pie_xstats field documentation
2026-07-27 16:40 [PATCH net-next v4 0/3] net/sched: sch_fq_pie: add per-flow class statistics Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 2/3] selftests: tc-testing: add fq_pie per-flow class stats test Hemendra M. Naik
@ 2026-07-27 16:40 ` Hemendra M. Naik
2 siblings, 0 replies; 5+ messages in thread
From: Hemendra M. Naik @ 2026-07-27 16:40 UTC (permalink / raw)
To: netdev
Cc: davem, edumazet, kuba, pabeni, horms, jiri, jhs, shuah,
linux-kernel, linux-kselftest, vishy0777, tahiliani,
Hemendra M. Naik
The comments describing `struct tc_pie_xstats` do not match the values
exported by the kernel.
Update the `delay` field comment to indicate that the value is reported
in microseconds, matching the `PSCHED_TICKS2NS() / NSEC_PER_USEC`
conversion.
Similarly, update the `avg_dq_rate` field comment to indicate that the
value is reported in bytes/second, matching the conversion performed by
`avg_dq_rate * PSCHED_TICKS_PER_SEC >> PIE_SCALE`.
These are documentation-only changes and do not modify the UAPI layout
or runtime behavior.
Signed-off-by: Hemendra M. Naik <hemendranaik@gmail.com>
Signed-off-by: Vishal Kamath <vishy0777@gmail.com>
Signed-off-by: Mohit P. Tahiliani <tahiliani@nitk.edu.in>
---
include/uapi/linux/pkt_sched.h | 4 ++--
tools/include/uapi/linux/pkt_sched.h | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/include/uapi/linux/pkt_sched.h b/include/uapi/linux/pkt_sched.h
index 9a86fa4d6846..26b7e6964adb 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 */
diff --git a/tools/include/uapi/linux/pkt_sched.h b/tools/include/uapi/linux/pkt_sched.h
index 587481a19433..9bc190f0b3cf 100644
--- a/tools/include/uapi/linux/pkt_sched.h
+++ b/tools/include/uapi/linux/pkt_sched.h
@@ -847,8 +847,8 @@ enum {
struct tc_pie_xstats {
__u32 prob; /* current probability */
- __u32 delay; /* current delay in ms */
- __u32 avg_dq_rate; /* current average dq_rate in bits/pie_time */
+ __u32 delay; /* current delay in microseconds */
+ __u32 avg_dq_rate; /* current average dq_rate in bytes/second */
__u32 packets_in; /* total number of packets enqueued */
__u32 dropped; /* packets dropped due to pie_action */
__u32 overlimit; /* dropped due to lack of space in queue */
--
2.34.1
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops
2026-07-27 16:40 ` [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops Hemendra M. Naik
@ 2026-07-31 9:56 ` Simon Horman
0 siblings, 0 replies; 5+ messages in thread
From: Simon Horman @ 2026-07-31 9:56 UTC (permalink / raw)
To: Hemendra M. Naik
Cc: netdev, davem, edumazet, kuba, pabeni, jiri, jhs, shuah,
linux-kernel, linux-kselftest, vishy0777, tahiliani
On Mon, Jul 27, 2026 at 10:10:54PM +0530, Hemendra M. Naik wrote:
...
> +static struct tcf_block *fq_pie_tcf_block(struct Qdisc *sch, unsigned long cl,
> + struct netlink_ext_ack *extack)
> +{
> + struct fq_pie_sched_data *q = qdisc_priv(sch);
> +
> + if (cl)
> + return NULL;
> + return q->block;
> +}
Hi,
The AI-generated review of this patch on netdev-ai [1] reports several
findings. And while I'd appreciate it if you could look over each of them
the one below seems to warrant highlighting here.
[1] https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260727164056.106203-1-hemendranaik%40gmail.com?part=1
Is the side effect of making fq_pie filter-capable intended, and should
it be described in the commit message?
Before this patch fq_pie_qdisc_ops had no .cl_ops, so filter attachment
was rejected in net/sched/cls_api.c:__tcf_qdisc_find():
cops = (*q)->ops->cl_ops;
if (!cops) {
NL_SET_ERR_MSG(extack, "Qdisc not classful");
err = -EINVAL;
q->filter_list was therefore always NULL and the filter branch of
fq_pie_classify() was unreachable since fq_pie was added. With .cl_ops
present and fq_pie_tcf_block() returning q->block, 'tc filter add ...
parent <fq_pie handle>' now succeeds and classifier verdicts plus
classid-driven flow selection become live in the enqueue fast path.
That newly reachable code does not handle TC_ACT_CONSUMED:
net/sched/sch_fq_pie.c:fq_pie_classify() {
result = tcf_classify_qdisc(skb, filter, &res, false);
if (result >= 0) {
switch (result) {
case TC_ACT_STOLEN:
case TC_ACT_QUEUED:
case TC_ACT_TRAP:
*qerr = NET_XMIT_SUCCESS | __NET_XMIT_STOLEN;
fallthrough;
case TC_ACT_SHOT:
return 0;
}
if (TC_H_MIN(res.classid) <= q->flows_cnt)
return TC_H_MIN(res.classid);
}
TC_ACT_CONSUMED is TC_ACT_VALUE_MAX + 1, so it is >= 0 and matches no
case arm, and control reaches the classid path. act_ct produces it
after the skb ownership has already been handed away:
net/sched/act_ct.c:tcf_ct_act() {
out_frag:
if (err != -EINPROGRESS)
tcf_action_inc_drop_qstats(&c->common);
return TC_ACT_CONSUMED;
tcf_action_exec() propagates any value != TC_ACT_PIPE and
__tcf_classify() returns it verbatim. For a filter such as
tc filter add dev eth0 parent 1: matchall action ct
with no flowid, res.classid is 0, TC_H_MIN(0) <= flows_cnt holds and 0
is returned, so fq_pie_qdisc_enqueue() frees the consumed skb a second
time:
idx = fq_pie_classify(skb, sch, &ret);
if (idx == 0) {
if (ret & __NET_XMIT_BYPASS)
qdisc_qstats_drop(sch);
__qdisc_drop(skb, to_free);
and with 'flowid 1:N' a valid flow index is returned instead, so
flow_queue_add() links the already freed skb into q->flows[idx]. Can
fragmented traffic hitting that qdisc then cause a double free or
use-after-free?
That TC_ACT_CONSUMED is expected from qdisc-attached filter chains is
also visible in net/sched/cls_api.c:tcf_qevent_handle(), which has an
explicit case TC_ACT_CONSUMED: arm that must not free the skb.
fq_codel, sfq and cake have the same missing arm, but for fq_pie the
path only becomes reachable with this patch.
...
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-07-31 9:56 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-27 16:40 [PATCH net-next v4 0/3] net/sched: sch_fq_pie: add per-flow class statistics Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 1/3] net/sched: sch_fq_pie: add per-flow statistics via class ops Hemendra M. Naik
2026-07-31 9:56 ` Simon Horman
2026-07-27 16:40 ` [PATCH net-next v4 2/3] selftests: tc-testing: add fq_pie per-flow class stats test Hemendra M. Naik
2026-07-27 16:40 ` [PATCH net-next v4 3/3] net/sched: pie: correct tc_pie_xstats field documentation Hemendra M. Naik
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).