From: Jamal Hadi Salim <jhs@mojatatu.com>
To: netdev@vger.kernel.org
Cc: "Jamal Hadi Salim" <jhs@mojatatu.com>,
"Toke Høiland-Jørgensen" <toke@toke.dk>,
cake@lists.bufferbloat.net, "Jiri Pirko" <jiri@resnulli.us>,
"David S . Miller" <davem@davemloft.net>,
"Eric Dumazet" <edumazet@google.com>,
"Jakub Kicinski" <kuba@kernel.org>,
"Paolo Abeni" <pabeni@redhat.com>,
"Simon Horman" <horms@kernel.org>,
"Victor Nogueira" <victor@mojatatu.com>,
hybris <hybris@mojatatu.ai>, Sashiko <sashiko-bot@kernel.org>
Subject: [Cake] [PATCH net-next] net/sched: fq_codel, cake: widen backlogs to u64
Date: Fri, 25 Sep 2026 04:53:53 -0400 [thread overview]
Message-ID: <QDISC-BA27.v1.20260922092618@mojatatu.com> (raw)
This is a follow-up to commit 8f735d64382d ("net/sched: bound
qdisc_pkt_len to prevent qdisc soft lockup"), which capped
qdisc_pkt_len() at QDISC_PKT_LEN_MAX (1 MiB). That cap bounds the stab
amplifier but leaves the per-flow backlog counter u32:
fq_codel_enqueue() accumulates qdisc_pkt_len(skb) into q->backlogs[idx],
so a flow can still accumulate 4096 packets of 1 MiB each and wrap the
counter mod 2^32. After a wrap, fq_codel_drop() sees a tiny maxbacklog
and drops from an almost-empty flow, and the dequeue-side subtractions
corrupt the counter further.
Widen the fq_codel backlogs table, the fat-flow scan (maxbacklog/len) and
the drop threshold to u64. fq_codel is not lockless: every writer runs
under the root qdisc lock, so plain u64 arithmetic keeps the WRITE_ONCE
publish / READ_ONCE-consume pattern. The dump path
(fq_codel_dump_class_stats) stays a lockless stat-only read.
CAKE accumulates the same generic qdisc_pkt_len(skb) into its per-flow
b->backlogs[] and per-tin b->tin_backlog and consumes the values for
longest-flow pruning (cake_heapify/cake_heapify_up) and for the shaper
staleness check, so it shares the bug. Widen those counters and the heap
comparison locals to u64; the class/tin stats keep exporting the low 32
bits through the unchanged uAPI fields.
Conditions to recreate the bug: CAP_NET_ADMIN in a user namespace;
CONFIG_NET_SCH_FQ_CODEL=y.
ip tuntap add tun0 mode tun
ip link set tun0 txqueuelen 32 up
ip addr add 10.99.0.1/24 dev tun0
tc qdisc add dev tun0 root handle 1: stab overhead 2000000000 \
fq_codel flows 1 limit 4200 ecn drop_batch 4096
# hold the tun fd open without reading (IFF_BACKPRESSURE) so the qdisc
# backlog persists, then send at least 4300 packets (the wrap starts
# at 4096 resident; the over-limit drop that reads the wrapped
# threshold fires past the 4200 limit): backlogs[0] wraps at 4096 x
# 1 MiB and the fat-flow threshold reads the wrapped value.
With a 1 MiB qdisc_pkt_len cap the counter wraps at 4096 resident
packets. At limit 4200 the first over-limit enqueue (the 4201st) sees a
wrapped 105 MiB (half-backlog threshold 52 MiB, a ~52 packet drop burst),
where the u64 counter sees 4201 MiB (threshold 2100 MiB, a ~2100 packet
drop burst).
Reported-by: Sashiko (nipa) <sashiko-bot@kernel.org>
Closes: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260818101130.16203-1-jhs@mojatatu.com
Link: https://lore.kernel.org/netdev/20260818101130.16203-1-jhs@mojatatu.com/
Tested-by: hybris <hybris@mojatatu.ai>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
net/sched/sch_cake.c | 23 ++++++++++++-----------
net/sched/sch_fq_codel.c | 22 ++++++++++++----------
2 files changed, 24 insertions(+), 21 deletions(-)
diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index dc93267029e7..8e99c85dab17 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -150,7 +150,7 @@ struct cake_heap_entry {
struct cake_tin_data {
struct cake_flow flows[CAKE_QUEUES];
- u32 backlogs[CAKE_QUEUES];
+ u64 backlogs[CAKE_QUEUES];
u32 tags[CAKE_QUEUES]; /* for set association */
u16 overflow_idx[CAKE_QUEUES];
struct cake_host hosts[CAKE_QUEUES]; /* for triple isolation */
@@ -177,7 +177,7 @@ struct cake_tin_data {
u16 tin_quantum;
s32 tin_deficit;
- u32 tin_backlog;
+ u64 tin_backlog;
u32 tin_dropped;
u32 tin_ecn_mark;
@@ -1466,17 +1466,17 @@ static void cake_heap_swap(struct cake_sched_data *q, u16 i, u16 j)
q->tins[jj.t].overflow_idx[jj.b] = i;
}
-static u32 cake_heap_get_backlog(const struct cake_sched_data *q, u16 i)
+static u64 cake_heap_get_backlog(const struct cake_sched_data *q, u16 i)
{
struct cake_heap_entry ii = q->overflow_heap[i];
- return q->tins[ii.t].backlogs[ii.b];
+ return READ_ONCE(q->tins[ii.t].backlogs[ii.b]);
}
static void cake_heapify(struct cake_sched_data *q, u16 i)
{
static const u32 a = CAKE_MAX_TINS * CAKE_QUEUES;
- u32 mb = cake_heap_get_backlog(q, i);
+ u64 mb = cake_heap_get_backlog(q, i);
u32 m = i;
while (m < a) {
@@ -1484,7 +1484,7 @@ static void cake_heapify(struct cake_sched_data *q, u16 i)
u32 r = l + 1;
if (l < a) {
- u32 lb = cake_heap_get_backlog(q, l);
+ u64 lb = cake_heap_get_backlog(q, l);
if (lb > mb) {
m = l;
@@ -1493,7 +1493,7 @@ static void cake_heapify(struct cake_sched_data *q, u16 i)
}
if (r < a) {
- u32 rb = cake_heap_get_backlog(q, r);
+ u64 rb = cake_heap_get_backlog(q, r);
if (rb > mb) {
m = r;
@@ -1514,8 +1514,8 @@ static void cake_heapify_up(struct cake_sched_data *q, u16 i)
{
while (i > 0 && i < CAKE_MAX_TINS * CAKE_QUEUES) {
u16 p = (i - 1) >> 1;
- u32 ib = cake_heap_get_backlog(q, i);
- u32 pb = cake_heap_get_backlog(q, p);
+ u64 ib = cake_heap_get_backlog(q, i);
+ u64 pb = cake_heap_get_backlog(q, p);
if (ib > pb) {
cake_heap_swap(q, i, p);
@@ -3046,7 +3046,8 @@ static int cake_dump_stats(struct Qdisc *sch, struct gnet_dump *d)
PUT_TSTAT_U64(THRESHOLD_RATE64, READ_ONCE(b->tin_rate_bps));
PUT_TSTAT_U64(SENT_BYTES64, READ_ONCE(b->bytes));
- PUT_TSTAT_U32(BACKLOG_BYTES, READ_ONCE(b->tin_backlog));
+ PUT_TSTAT_U32(BACKLOG_BYTES,
+ (u32)READ_ONCE(b->tin_backlog));
PUT_TSTAT_U32(TARGET_US,
ktime_to_us(ns_to_ktime(READ_ONCE(b->cparams.target))));
@@ -3152,7 +3153,7 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl,
}
sch_tree_unlock(sch);
}
- qs.backlog = READ_ONCE(b->backlogs[idx % CAKE_QUEUES]);
+ qs.backlog = (u32)READ_ONCE(b->backlogs[idx % CAKE_QUEUES]);
qs.drops = READ_ONCE(flow->dropped);
}
if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0)
diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 969b2510b0b8..3c20297cef07 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -51,7 +51,7 @@ struct fq_codel_sched_data {
struct tcf_proto __rcu *filter_list; /* optional external classifier */
struct tcf_block *block;
struct fq_codel_flow *flows; /* Flows table [flows_cnt] */
- u32 *backlogs; /* backlog table [flows_cnt] */
+ u64 *backlogs; /* backlog table [flows_cnt] */
u32 flows_cnt; /* number of flows */
u32 quantum; /* psched_mtu(qdisc_dev(sch)); */
u32 drop_batch_size;
@@ -138,22 +138,25 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets,
struct sk_buff **to_free)
{
struct fq_codel_sched_data *q = qdisc_priv(sch);
+ u64 maxbacklog = 0, len = 0;
struct sk_buff *skb;
- unsigned int maxbacklog = 0, idx = 0, i, len;
struct fq_codel_flow *flow;
- unsigned int threshold;
+ unsigned int idx = 0, i;
unsigned int mem = 0;
+ u64 threshold;
/* Queue is full! Find the fat flow and drop packet(s) from it.
* This might sound expensive, but with 1024 flows, we scan
- * 4KB of memory, and we dont need to handle a complex tree
+ * 8KB of memory, and we dont need to handle a complex tree
* in fast path (packet queue/enqueue) with many cache misses.
* In stress mode, we'll try to drop 64 packets from the flow,
* amortizing this linear lookup to one cache line per drop.
*/
for (i = 0; i < q->flows_cnt; i++) {
- if (q->backlogs[i] > maxbacklog) {
- maxbacklog = q->backlogs[i];
+ u64 backlog = READ_ONCE(q->backlogs[i]);
+
+ if (backlog > maxbacklog) {
+ maxbacklog = backlog;
idx = i;
}
}
@@ -162,7 +165,6 @@ static unsigned int fq_codel_drop(struct Qdisc *sch, unsigned int max_packets,
threshold = maxbacklog >> 1;
flow = &q->flows[idx];
- len = 0;
i = 0;
do {
skb = dequeue_head(flow);
@@ -384,7 +386,7 @@ static void fq_codel_reset(struct Qdisc *sch)
INIT_LIST_HEAD(&flow->flowchain);
codel_vars_init(&flow->cvars);
}
- memset(q->backlogs, 0, q->flows_cnt * sizeof(u32));
+ memset(q->backlogs, 0, q->flows_cnt * sizeof(u64));
q->memory_usage = 0;
}
@@ -542,7 +544,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
err = -ENOMEM;
goto init_failure;
}
- q->backlogs = kvcalloc(q->flows_cnt, sizeof(u32), GFP_KERNEL);
+ q->backlogs = kvcalloc(q->flows_cnt, sizeof(u64), GFP_KERNEL);
if (!q->backlogs) {
err = -ENOMEM;
goto alloc_failure;
@@ -720,7 +722,7 @@ static int fq_codel_dump_class_stats(struct Qdisc *sch, unsigned long cl,
}
sch_tree_unlock(sch);
}
- qs.backlog = READ_ONCE(q->backlogs[idx]);
+ qs.backlog = (u32)READ_ONCE(q->backlogs[idx]);
qs.drops = 0;
}
if (gnet_stats_copy_queue(d, NULL, &qs, qs.qlen) < 0)
--
2.43.0
next reply other threads:[~2026-09-25 8:54 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-25 8:53 Jamal Hadi Salim [this message]
2026-09-25 9:12 ` [Cake] Re: [PATCH net-next] net/sched: fq_codel, cake: widen backlogs to u64 Eric Dumazet
2026-09-25 11:27 ` Jamal Hadi Salim
2026-09-25 12:22 ` Jamal Hadi Salim
2026-09-25 13:03 ` Eric Dumazet
2026-09-25 13:30 ` Sebastian Moeller
2026-09-25 13:43 ` Eric Dumazet
2026-09-25 14:02 ` Sebastian Moeller
[not found] ` <CAAFAkD_U8PGfdJAgSLsh3ku+tTBvQYB012k7jEUacfm6HothyQ@mail.gmail.com>
2026-09-26 9:48 ` Eric Dumazet
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://lists.bufferbloat.net/postorius/lists/cake.lists.bufferbloat.net/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=QDISC-BA27.v1.20260922092618@mojatatu.com \
--to=jhs@mojatatu.com \
--cc=cake@lists.bufferbloat.net \
--cc=davem@davemloft.net \
--cc=edumazet@google.com \
--cc=horms@kernel.org \
--cc=hybris@mojatatu.ai \
--cc=jiri@resnulli.us \
--cc=kuba@kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=sashiko-bot@kernel.org \
--cc=toke@toke.dk \
--cc=victor@mojatatu.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox