netdev.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration
@ 2026-10-08  7:47 Jamal Hadi Salim
  2026-10-08  7:47 ` [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath Jamal Hadi Salim
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08  7:47 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Toke Høiland-Jørgensen, Jiri Pirko,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, cake, Victor Nogueira, sashiko-bot

Four independent fixes in the CAKE qdisc.  Three are follow-ups to the
autorate reconfiguration work around commit 7cbfb180945c ("net/sched:
sch_cake: fix autorate reconfiguration throttling") and the 32-bit
admission-counter wraps fixed in gred/bfifo/plug by commit 4c660ee8c809
("net: sched: fix 32-bit backlog wrap in gred, bfifo and plug enqueue");
the fourth is a dead-code cleanup found while auditing the dump path.

Patch 1 serializes a 'tc qdisc change' with the datapath's autorate
update under the qdisc lock, so the netlink writer and the autorate
writer can no longer lose the configured rate; rate_bps becomes
atomic64_t so the 64-bit store is atomic on 32-bit machines too.

Patch 2 propagates the backlog reduction of the reconfigure-time tin
purge to classful parents, so a CAKE leaf under HTB no longer leaves the
purged packets in the parent's qlen/backlog forever.

Patch 3 widens buffer_used and buffer_max_used to u64.  The u32
accumulator wraps once the resident truesize passes 2^32, bypassing the
over-limit drop loop while the configured limit is a u32 attribute.

Patch 4 drops an unreachable 'nla_nest_end() < 0' error check: the
helper never returns a negative value.

All four are hardening for net-next (no Fixes:, no Cc: stable): the
counter wrap, the missing parent-backlog propagation and the
reconfiguration race all predate any contract we could anchor a
regression on.  Patches 1-2 were reported by Sashiko (gemini) and patch
3 comes from the gred 32-bit-wrap family; patch 4 is a static cleanup.

Jamal Hadi Salim (4):
  net/sched: sch_cake: serialize reconfiguration with the datapath
  net/sched: sch_cake: reduce parent backlog when clearing a tin
  net/sched: sch_cake: widen buffer_used to u64
  net/sched: sch_cake: drop dead nla_nest_end() error check

 net/sched/sch_cake.c | 60 +++++++++++++++++++++++++++++++++-------------------
 1 file changed, 38 insertions(+), 22 deletions(-)

-- 
2.43.0


^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath
  2026-10-08  7:47 [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration Jamal Hadi Salim
@ 2026-10-08  7:47 ` Jamal Hadi Salim
  2026-10-08 10:06   ` Toke Høiland-Jørgensen
  2026-10-08  7:47 ` [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin Jamal Hadi Salim
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08  7:47 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Toke Høiland-Jørgensen, Jiri Pirko,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, cake, Victor Nogueira, Sashiko

This is a follow-up to commit 7cbfb180945c ("net/sched: sch_cake: fix
autorate reconfiguration throttling").  That change only stored
last_reconfig_time; it did not address the concurrency between the
autorate rate update in cake_enqueue() and the netlink reconfiguration
path, which the original review flagged.

cake_change() -> cake_config_change() updates the fields of the shared
struct cake_sched_config under RTNL but NOT under the qdisc lock: it
only took sch_tree_lock() later, around cake_reconfigure().  The datapath
in turn serializes cake_enqueue() with itself under the qdisc lock, and
its autorate-ingress path stores the estimated rate into
q->config->rate_bps.  Two writers therefore update the same rate with no
common lock:

  CPU0: cake_enqueue() sees CAKE_FLAG_AUTORATE_INGRESS, computes an
        estimate, and is about to store it.
  CPU1: 'tc qdisc change ... bandwidth X' clears autorate in its local
        rate_flags, writes rate_bps = X, publishes the cleared flag, and
        waits in sch_tree_lock().
  CPU0: stores the estimate, calls cake_reconfigure(), and unlocks.
  CPU1: reconfigures from the estimate and returns.

The qdisc is left with autorate disabled but with the estimate installed
instead of X.  On 32-bit machines the unlocked 64-bit rate_bps store is
also not atomic, so a reader can observe a torn rate.  iproute2 reaches
this because parsing 'bandwidth X' sets autorate = 0 and emits both
TCA_CAKE_BASE_RATE64 and TCA_CAKE_AUTORATE.

Commit the parsed configuration and reconfigure while holding the same
qdisc lock that serializes the datapath, so the netlink writer and the
autorate writer can no longer interleave.  Make rate_bps an atomic64_t
so a 64-bit rate update is atomic on 32-bit machines as well, and keep
the READ_ONCE()/WRITE_ONCE() annotations for the remaining lockless
readers (cake_config_dump(), which runs without the lock, and the
cake_mq shared config, which readers observe under different child
locks).

Conditions to recreate the bug: build with CONFIG_KCSAN=y (the race is
otherwise not observable on 64-bit), put cake in autorate-ingress on a
device, drive bursty traffic with gaps so the 250ms autorate
reconfiguration fires, and concurrently loop 'tc qdisc change dev ...
cake bandwidth <N>kbit autorate-ingress'.  The lost update of the
installed rate is reproducible without a sanitizer by running bursty
senders while repeatedly changing an autorate qdisc to a fixed
'bandwidth X' and reading the installed rate back.

Reported-by: Sashiko (gemini) <sashiko-bot@kernel.org>
Link: https://sashiko.dev/#/patchset/20260816012109.2865223-1-ooonea@gmail.com
Link: https://lore.kernel.org/netdev/20260816012109.2865223-1-ooonea@gmail.com/
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/sched/sch_cake.c | 42 +++++++++++++++++++++++++-----------------
 1 file changed, 25 insertions(+), 17 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index dc93267029e7..1c29695dc928 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -199,7 +199,7 @@ struct cake_tin_data {
 }; /* number of tins is small, so size of this struct doesn't matter much */
 
 struct cake_sched_config {
-	u64		rate_bps;
+	atomic64_t	rate_bps;
 	u64		interval;
 	u64		target;
 	u64		sync_time;
@@ -1906,7 +1906,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			if (ktime_after(now,
 					ktime_add_ms(q->last_reconfig_time,
 						     250))) {
-				q->config->rate_bps = (q->avg_peak_bandwidth * 15) >> 4;
+				atomic64_set(&q->config->rate_bps,
+					     (q->avg_peak_bandwidth * 15) >> 4);
 				q->last_reconfig_time = now;
 				cake_reconfigure(sch);
 			}
@@ -2021,7 +2022,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 	    now - q->last_checked_active >= q->config->sync_time) {
 		struct net_device *dev = qdisc_dev(sch);
 		struct cake_sched_data *other_priv;
-		u64 new_rate = q->config->rate_bps;
+		u64 new_rate = atomic64_read(&q->config->rate_bps);
 		u64 other_qlen, other_last_active;
 		struct Qdisc *other_sch;
 		u32 num_active_qs = 1;
@@ -2042,7 +2043,7 @@ static struct sk_buff *cake_dequeue(struct Qdisc *sch)
 		}
 
 		if (num_active_qs > 1)
-			new_rate = div64_u64(q->config->rate_bps, num_active_qs);
+			new_rate = div64_u64(atomic64_read(&q->config->rate_bps), num_active_qs);
 
 		cake_configure_rates(sch, new_rate, true);
 		q->last_checked_active = now;
@@ -2626,12 +2627,12 @@ static void cake_reconfigure(struct Qdisc *sch)
 	struct cake_sched_config *q = qd->config;
 	u32 buffer_limit;
 
-	cake_configure_rates(sch, qd->config->rate_bps, false);
+	cake_configure_rates(sch, atomic64_read(&qd->config->rate_bps), false);
 
 	if (q->buffer_config_limit) {
 		buffer_limit = q->buffer_config_limit;
-	} else if (q->rate_bps) {
-		u64 t = q->rate_bps * q->interval;
+	} else if (atomic64_read(&q->rate_bps)) {
+		u64 t = atomic64_read(&q->rate_bps) * q->interval;
 
 		do_div(t, USEC_PER_SEC / 4);
 		buffer_limit = max_t(u32, t, 4U << 20);
@@ -2686,8 +2687,8 @@ static int cake_config_change(struct cake_sched_config *q, struct nlattr *opt,
 	}
 
 	if (tb[TCA_CAKE_BASE_RATE64])
-		WRITE_ONCE(q->rate_bps,
-			   nla_get_u64(tb[TCA_CAKE_BASE_RATE64]));
+		atomic64_set(&q->rate_bps,
+			     nla_get_u64(tb[TCA_CAKE_BASE_RATE64]));
 
 	if (tb[TCA_CAKE_DIFFSERV_MODE])
 		WRITE_ONCE(q->tin_mode,
@@ -2784,9 +2785,16 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt,
 		return -EOPNOTSUPP;
 	}
 
+	/* Once the qdisc is live, commit and reconfigure under the lock that
+	 * serializes the datapath, so a concurrent 'tc qdisc change' and the
+	 * autorate update cannot lose the configured rate.
+	 */
+	if (qd->tins)
+		sch_tree_lock(sch);
+
 	ret = cake_config_change(q, opt, extack, &overhead_changed);
 	if (ret)
-		return ret;
+		goto unlock;
 
 	if (overhead_changed) {
 		WRITE_ONCE(qd->max_netlen, 0);
@@ -2795,13 +2803,13 @@ static int cake_change(struct Qdisc *sch, struct nlattr *opt,
 		WRITE_ONCE(qd->min_adjlen, ~0);
 	}
 
-	if (qd->tins) {
-		sch_tree_lock(sch);
+	if (qd->tins)
 		cake_reconfigure(sch);
+unlock:
+	if (qd->tins)
 		sch_tree_unlock(sch);
-	}
 
-	return 0;
+	return ret;
 }
 
 static void cake_destroy(struct Qdisc *sch)
@@ -2818,7 +2826,7 @@ static void cake_config_init(struct cake_sched_config *q, bool is_shared)
 	q->tin_mode = CAKE_DIFFSERV_DIFFSERV3;
 	q->flow_mode  = CAKE_FLOW_TRIPLE;
 
-	q->rate_bps = 0; /* unlimited by default */
+	atomic64_set(&q->rate_bps, 0); /* unlimited by default */
 
 	q->interval = 100000; /* 100ms default */
 	q->target   =   5000; /* 5ms: codel RFC argues
@@ -2889,7 +2897,7 @@ static int cake_init(struct Qdisc *sch, struct nlattr *opt,
 	}
 
 	cake_reconfigure(sch);
-	qd->avg_peak_bandwidth = q->rate_bps;
+	qd->avg_peak_bandwidth = atomic64_read(&q->rate_bps);
 	qd->min_netlen = ~0;
 	qd->min_adjlen = ~0;
 	qd->active_queues = 0;
@@ -2917,7 +2925,7 @@ static int cake_config_dump(struct cake_sched_config *q, struct sk_buff *skb)
 		goto nla_put_failure;
 
 	if (nla_put_u64_64bit(skb, TCA_CAKE_BASE_RATE64,
-			      READ_ONCE(q->rate_bps), TCA_CAKE_PAD))
+			      atomic64_read(&q->rate_bps), TCA_CAKE_PAD))
 		goto nla_put_failure;
 
 	flow_mode = READ_ONCE(q->flow_mode);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin
  2026-10-08  7:47 [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration Jamal Hadi Salim
  2026-10-08  7:47 ` [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath Jamal Hadi Salim
@ 2026-10-08  7:47 ` Jamal Hadi Salim
  2026-10-08 10:19   ` Toke Høiland-Jørgensen
  2026-10-08  7:47 ` [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64 Jamal Hadi Salim
  2026-10-08  7:47 ` [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check Jamal Hadi Salim
  3 siblings, 1 reply; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08  7:47 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Toke Høiland-Jørgensen, Jiri Pirko,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, cake, Victor Nogueira, Sashiko

CAKE has purged unused tins on DiffServ reconfiguration since commit
83f8fd69af4f ("sch_cake: Add DiffServ handling") without propagating the
resulting backlog reduction to classful parents. The issue was noticed
while reviewing 7cbfb180945c, but predates that change.

cake_clear_tin() drops every packet from a tin with
kfree_skb_reason() but never calls qdisc_tree_reduce_backlog(), so a
CAKE qdisc under a classful parent (e.g. HTB) leaves the dropped packets
in the parent qdisc's and class's qlen/backlog accounting forever. The
normal dequeue path does reduce the parent; only the live reconfigure
purge misses it.

Tins are cleared from cake_reset() and, on reconfigure, from
cake_configure_rates(). The qdisc core already owns the parent delta for
the reset path: qdisc_purge_queue() snapshots qlen/backlog, runs
qdisc_reset(), then reduces the tree, so propagating the reduction from
cake_clear_tin() itself would subtract the same backlog twice. Snapshot
CAKE's qlen/backlog around only the reconfiguration purge in
cake_configure_rates() and reduce the parent once afterwards, matching
the delta pattern already used by cake_enqueue().

Conditions to recreate the bug: put CAKE (diffserv8) under an HTB class,
flood DSCP-classified traffic into a high tin so it backlogs, then change
CAKE to diffserv3. The HTB parent keeps the purged packets in its qlen
and backlog; a kernel with the fix shows the parent at the leaf's backlog
(426b/5p) instead of retaining the purged packets.

Reported-by: Sashiko (gemini) <sashiko-bot@kernel.org>
Link: https://sashiko.dev/#/patchset/20260816012109.2865223-1-ooonea@gmail.com
Link: https://lore.kernel.org/netdev/20260816012109.2865223-1-ooonea@gmail.com/
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/sched/sch_cake.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 1c29695dc928..2bad0b6eb13f 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -2611,10 +2611,17 @@ static void cake_configure_rates(struct Qdisc *sch, u64 rate, bool rate_adjust)
 	}
 
 	if (!rate_adjust) {
+		u32 backlog = sch->qstats.backlog, qlen = sch->q.qlen;
+
 		for (c = qd->tin_cnt; c < CAKE_MAX_TINS; c++) {
 			cake_clear_tin(sch, c);
 			qd->tins[c].cparams.mtu_time = qd->tins[ft].cparams.mtu_time;
 		}
+
+		backlog -= sch->qstats.backlog;
+		qlen -= sch->q.qlen;
+		if (qlen)
+			qdisc_tree_reduce_backlog(sch, qlen, backlog);
 	}
 
 	qd->rate_ns   = qd->tins[ft].tin_rate_ns;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64
  2026-10-08  7:47 [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration Jamal Hadi Salim
  2026-10-08  7:47 ` [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath Jamal Hadi Salim
  2026-10-08  7:47 ` [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin Jamal Hadi Salim
@ 2026-10-08  7:47 ` Jamal Hadi Salim
  2026-10-08 10:24   ` Toke Høiland-Jørgensen
  2026-10-08  7:47 ` [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check Jamal Hadi Salim
  3 siblings, 1 reply; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08  7:47 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Toke Høiland-Jørgensen, Jiri Pirko,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Victor Nogueira, cake, Sashiko

This is a follow-up to commit 4c660ee8c809 ("net: sched: fix 32-bit
backlog wrap in gred, bfifo and plug enqueue"), which promoted the
backlog + length admission sums in gred, bfifo and plug to u64.  CAKE's
own memory accounting has the same 32-bit wrap.

cake_sched_data.buffer_used accumulates skb->truesize and is compared
against memory_limit:

	q->buffer_used += skb->truesize;
	if (q->buffer_used <= q->buffer_limit)
		return NET_XMIT_SUCCESS;
	while (q->buffer_used > q->buffer_limit)
		drop...

buffer_used is u32, so once the resident truesize passes 2^32 it wraps to
a small value, the <= test passes and the drop loop is skipped even
though far more than the configured limit is queued.  memory_limit is a
u32 attribute (TCA_CAKE_MEMORY), so buffer_limit can be as large as
U32_MAX and never caps buffer_used below the wrap point; with a large
memory_limit the queue then grows until the machine is out of memory.

Widen buffer_used and buffer_max_used to u64, the same shape as the
gred/bfifo/plug fix.  buffer_limit stays u32; the comparison promotes it
to u64, so the bounded queue stops at the configured limit below 2^32 and
the counter can no longer wrap.  buffer_used is subtracted on dequeue and
drop, so the widened type flows through unchanged.

The ACK-filter replacement applied the net delta as one expression,
q->buffer_used += skb->truesize - ack->truesize.  Both operands are
unsigned int, so the subtraction is evaluated at u32 and a larger removed
ACK wraps the delta; the old u32 accumulator folded that back to the
correct net value, but the widened u64 accumulator would persist it as a
roughly 4 GiB over-count.  Apply the replacement as two exact operations
on the widened counter instead; the queue already accounts ack->truesize,
so the subtraction cannot underflow.

Conditions to recreate the bug: CAP_NET_ADMIN (root or a user namespace,
the qdisc attach is gated by netlink_net_capable(CAP_NET_ADMIN)).

  # hold a tun device's tx ring so the root qdisc parks packets
  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: cake memlimit 4294967295

  # drive >4 GiB of resident truesize through the qdisc (e.g. several UDP
  # sockets with SO_SNDBUFFORCE). On the unfixed kernel buffer_used wraps,
  # the drop loop is bypassed and the guest OOMs; with the fix the counter
  # passes the limit without wrapping and drops the excess.

  # the ACK-filter over-count is reached with ack-filter enabled and a
  # queued pure ACK whose truesize is larger than the replacement ACK:
  tc qdisc add dev tun0 root handle 1: cake ack-filter

Reported-by: Sashiko (gemini) <sashiko-bot@kernel.org>
Closes: https://sashiko.dev/#/patchset/20260818095927.15901-1-jhs@mojatatu.com
Link: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260818095927.15901-1-jhs@mojatatu.com
Link: https://lore.kernel.org/netdev/20260818095927.15901-1-jhs@mojatatu.com/
Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/sched/sch_cake.c | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 2bad0b6eb13f..4caf9718c9bd 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -234,8 +234,9 @@ struct cake_sched_data {
 	u16		tin_cnt;
 
 	/* resource tracking */
-	u32		buffer_used;
-	u32		buffer_max_used;
+
+	u64		buffer_max_used;
+	u64		buffer_used;
 	u32		buffer_limit;
 
 	/* indices for dequeue */
@@ -1850,7 +1851,8 @@ static s32 cake_enqueue(struct sk_buff *skb, struct Qdisc *sch,
 			qdisc_qstats_drop(sch);
 			ack_pkt_len = qdisc_pkt_len(ack);
 			WRITE_ONCE(b->bytes, b->bytes + ack_pkt_len);
-			q->buffer_used += skb->truesize - ack->truesize;
+			q->buffer_used += skb->truesize;
+			q->buffer_used -= ack->truesize;
 			if (q->config->rate_flags & CAKE_FLAG_INGRESS)
 				cake_advance_shaper(q, b, ack, now, true);
 
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check
  2026-10-08  7:47 [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration Jamal Hadi Salim
                   ` (2 preceding siblings ...)
  2026-10-08  7:47 ` [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64 Jamal Hadi Salim
@ 2026-10-08  7:47 ` Jamal Hadi Salim
  2026-10-08 10:24   ` Toke Høiland-Jørgensen
  3 siblings, 1 reply; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08  7:47 UTC (permalink / raw)
  To: netdev
  Cc: Jamal Hadi Salim, Toke Høiland-Jørgensen, Jiri Pirko,
	David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	Simon Horman, Victor Nogueira, cake

cake_dump_class_stats() closes the TCA_STATS_APP nest with

	if (nla_nest_end(d->skb, stats) < 0)
		return -1;

nla_nest_end() never returns a negative value: it has a single
`return skb->len;` and no error path (that is why the overflow-aware
nla_nest_end_safe() helper was later added). The '< 0' test can never
be true, so the 'return -1' is unreachable dead code.

Drop the check and close the nest with the bare statement, matching the
sibling cake nests in the same file.

This issue was discovered while fixing a bug on the gate action, see:
https://lore.kernel.org/netdev/QDISC-H19Z.v1.20261001053234@mojatatu.com/

Conditions to recreate the bug:
- not runtime observable: the issue is found by inspection.

Reviewed-by: Victor Nogueira <victor@mojatatu.com>
Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>
---
 net/sched/sch_cake.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/net/sched/sch_cake.c b/net/sched/sch_cake.c
index 4caf9718c9bd..124f552023f8 100644
--- a/net/sched/sch_cake.c
+++ b/net/sched/sch_cake.c
@@ -3211,8 +3211,7 @@ static int cake_dump_class_stats(struct Qdisc *sch, unsigned long cl,
 						       READ_ONCE(flow->cvars.drop_next))));
 		}
 
-		if (nla_nest_end(d->skb, stats) < 0)
-			return -1;
+		nla_nest_end(d->skb, stats);
 	}
 
 	return 0;
-- 
2.43.0


^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath
  2026-10-08  7:47 ` [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath Jamal Hadi Salim
@ 2026-10-08 10:06   ` Toke Høiland-Jørgensen
  2026-10-08 11:53     ` Jamal Hadi Salim
  0 siblings, 1 reply; 11+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-10-08 10:06 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, cake, Victor Nogueira,
	Sashiko

> The qdisc is left with autorate disabled but with the estimate installed
> instead of X.  On 32-bit machines the unlocked 64-bit rate_bps store is
> also not atomic, so a reader can observe a torn rate.  iproute2 reaches
> this because parsing 'bandwidth X' sets autorate = 0 and emits both
> TCA_CAKE_BASE_RATE64 and TCA_CAKE_AUTORATE.
>
> Commit the parsed configuration and reconfigure while holding the same
> qdisc lock that serializes the datapath, so the netlink writer and the
> autorate writer can no longer interleave.  Make rate_bps an atomic64_t
> so a 64-bit rate update is atomic on 32-bit machines as well, and keep
> the READ_ONCE()/WRITE_ONCE() annotations for the remaining lockless
> readers (cake_config_dump(), which runs without the lock, and the
> cake_mq shared config, which readers observe under different child
> locks).

So atomic_t.txt has this section:

"SEMANTICS
---------

Non-RMW ops:

The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
smp_store_release() respectively. Therefore, if you find yourself only using
the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
and are doing it wrong."


So AFAICT, we don't need atomic_t, we just need READ/WRITE_ONCE()
annotations?

-Toke

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin
  2026-10-08  7:47 ` [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin Jamal Hadi Salim
@ 2026-10-08 10:19   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 11+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-10-08 10:19 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, cake, Victor Nogueira,
	Sashiko

Jamal Hadi Salim <jhs@mojatatu.com> writes:

> CAKE has purged unused tins on DiffServ reconfiguration since commit
> 83f8fd69af4f ("sch_cake: Add DiffServ handling") without propagating the
> resulting backlog reduction to classful parents. The issue was noticed
> while reviewing 7cbfb180945c, but predates that change.
>
> cake_clear_tin() drops every packet from a tin with
> kfree_skb_reason() but never calls qdisc_tree_reduce_backlog(), so a
> CAKE qdisc under a classful parent (e.g. HTB) leaves the dropped packets
> in the parent qdisc's and class's qlen/backlog accounting forever. The
> normal dequeue path does reduce the parent; only the live reconfigure
> purge misses it.
>
> Tins are cleared from cake_reset() and, on reconfigure, from
> cake_configure_rates(). The qdisc core already owns the parent delta for
> the reset path: qdisc_purge_queue() snapshots qlen/backlog, runs
> qdisc_reset(), then reduces the tree, so propagating the reduction from
> cake_clear_tin() itself would subtract the same backlog twice. Snapshot
> CAKE's qlen/backlog around only the reconfiguration purge in
> cake_configure_rates() and reduce the parent once afterwards, matching
> the delta pattern already used by cake_enqueue().
>
> Conditions to recreate the bug: put CAKE (diffserv8) under an HTB class,
> flood DSCP-classified traffic into a high tin so it backlogs, then change
> CAKE to diffserv3. The HTB parent keeps the purged packets in its qlen
> and backlog; a kernel with the fix shows the parent at the leaf's backlog
> (426b/5p) instead of retaining the purged packets.
>
> Reported-by: Sashiko (gemini) <sashiko-bot@kernel.org>
> Link: https://sashiko.dev/#/patchset/20260816012109.2865223-1-ooonea@gmail.com
> Link: https://lore.kernel.org/netdev/20260816012109.2865223-1-ooonea@gmail.com/
> Reviewed-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64
  2026-10-08  7:47 ` [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64 Jamal Hadi Salim
@ 2026-10-08 10:24   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 11+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-10-08 10:24 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Victor Nogueira, cake,
	Sashiko

Jamal Hadi Salim <jhs@mojatatu.com> writes:

> This is a follow-up to commit 4c660ee8c809 ("net: sched: fix 32-bit
> backlog wrap in gred, bfifo and plug enqueue"), which promoted the
> backlog + length admission sums in gred, bfifo and plug to u64.  CAKE's
> own memory accounting has the same 32-bit wrap.
>
> cake_sched_data.buffer_used accumulates skb->truesize and is compared
> against memory_limit:
>
> 	q->buffer_used += skb->truesize;
> 	if (q->buffer_used <= q->buffer_limit)
> 		return NET_XMIT_SUCCESS;
> 	while (q->buffer_used > q->buffer_limit)
> 		drop...
>
> buffer_used is u32, so once the resident truesize passes 2^32 it wraps to
> a small value, the <= test passes and the drop loop is skipped even
> though far more than the configured limit is queued.  memory_limit is a
> u32 attribute (TCA_CAKE_MEMORY), so buffer_limit can be as large as
> U32_MAX and never caps buffer_used below the wrap point; with a large
> memory_limit the queue then grows until the machine is out of memory.
>
> Widen buffer_used and buffer_max_used to u64, the same shape as the
> gred/bfifo/plug fix.  buffer_limit stays u32; the comparison promotes it
> to u64, so the bounded queue stops at the configured limit below 2^32 and
> the counter can no longer wrap.  buffer_used is subtracted on dequeue and
> drop, so the widened type flows through unchanged.
>
> The ACK-filter replacement applied the net delta as one expression,
> q->buffer_used += skb->truesize - ack->truesize.  Both operands are
> unsigned int, so the subtraction is evaluated at u32 and a larger removed
> ACK wraps the delta; the old u32 accumulator folded that back to the
> correct net value, but the widened u64 accumulator would persist it as a
> roughly 4 GiB over-count.  Apply the replacement as two exact operations
> on the widened counter instead; the queue already accounts ack->truesize,
> so the subtraction cannot underflow.
>
> Conditions to recreate the bug: CAP_NET_ADMIN (root or a user namespace,
> the qdisc attach is gated by netlink_net_capable(CAP_NET_ADMIN)).
>
>   # hold a tun device's tx ring so the root qdisc parks packets
>   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: cake memlimit 4294967295
>
>   # drive >4 GiB of resident truesize through the qdisc (e.g. several UDP
>   # sockets with SO_SNDBUFFORCE). On the unfixed kernel buffer_used wraps,
>   # the drop loop is bypassed and the guest OOMs; with the fix the counter
>   # passes the limit without wrapping and drops the excess.
>
>   # the ACK-filter over-count is reached with ack-filter enabled and a
>   # queued pure ACK whose truesize is larger than the replacement ACK:
>   tc qdisc add dev tun0 root handle 1: cake ack-filter
>
> Reported-by: Sashiko (gemini) <sashiko-bot@kernel.org>
> Closes: https://sashiko.dev/#/patchset/20260818095927.15901-1-jhs@mojatatu.com
> Link: https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260818095927.15901-1-jhs@mojatatu.com
> Link: https://lore.kernel.org/netdev/20260818095927.15901-1-jhs@mojatatu.com/
> Reviewed-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check
  2026-10-08  7:47 ` [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check Jamal Hadi Salim
@ 2026-10-08 10:24   ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 11+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-10-08 10:24 UTC (permalink / raw)
  To: Jamal Hadi Salim, netdev
  Cc: Jamal Hadi Salim, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, Victor Nogueira, cake

Jamal Hadi Salim <jhs@mojatatu.com> writes:

> cake_dump_class_stats() closes the TCA_STATS_APP nest with
>
> 	if (nla_nest_end(d->skb, stats) < 0)
> 		return -1;
>
> nla_nest_end() never returns a negative value: it has a single
> `return skb->len;` and no error path (that is why the overflow-aware
> nla_nest_end_safe() helper was later added). The '< 0' test can never
> be true, so the 'return -1' is unreachable dead code.
>
> Drop the check and close the nest with the bare statement, matching the
> sibling cake nests in the same file.
>
> This issue was discovered while fixing a bug on the gate action, see:
> https://lore.kernel.org/netdev/QDISC-H19Z.v1.20261001053234@mojatatu.com/
>
> Conditions to recreate the bug:
> - not runtime observable: the issue is found by inspection.
>
> Reviewed-by: Victor Nogueira <victor@mojatatu.com>
> Signed-off-by: Jamal Hadi Salim <jhs@mojatatu.com>

Acked-by: Toke Høiland-Jørgensen <toke@toke.dk>

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath
  2026-10-08 10:06   ` Toke Høiland-Jørgensen
@ 2026-10-08 11:53     ` Jamal Hadi Salim
  2026-10-08 12:03       ` Toke Høiland-Jørgensen
  0 siblings, 1 reply; 11+ messages in thread
From: Jamal Hadi Salim @ 2026-10-08 11:53 UTC (permalink / raw)
  To: Toke Høiland-Jørgensen
  Cc: netdev, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, cake, Victor Nogueira,
	Sashiko

On Thu, Oct 8, 2026 at 6:29 AM Toke Høiland-Jørgensen <toke@toke.dk> wrote:
>
> > The qdisc is left with autorate disabled but with the estimate installed
> > instead of X.  On 32-bit machines the unlocked 64-bit rate_bps store is
> > also not atomic, so a reader can observe a torn rate.  iproute2 reaches
> > this because parsing 'bandwidth X' sets autorate = 0 and emits both
> > TCA_CAKE_BASE_RATE64 and TCA_CAKE_AUTORATE.
> >
> > Commit the parsed configuration and reconfigure while holding the same
> > qdisc lock that serializes the datapath, so the netlink writer and the
> > autorate writer can no longer interleave.  Make rate_bps an atomic64_t
> > so a 64-bit rate update is atomic on 32-bit machines as well, and keep
> > the READ_ONCE()/WRITE_ONCE() annotations for the remaining lockless
> > readers (cake_config_dump(), which runs without the lock, and the
> > cake_mq shared config, which readers observe under different child
> > locks).
>
> So atomic_t.txt has this section:
>
> "SEMANTICS
> ---------
>
> Non-RMW ops:
>
> The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
> implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
> smp_store_release() respectively. Therefore, if you find yourself only using
> the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
> and are doing it wrong."
>
>
> So AFAICT, we don't need atomic_t, we just need READ/WRITE_ONCE()
> annotations?
>

hrm. actually, the sch_tree_lock() already protects cake_change(): it
commits the parsed config and reconfigures together with the datapath
under the same qdisc lock, so the netlink writer and the autorate
store can no longer interleave. So the atomic64_t was never needed for
the lost update.

on the atomic_t.txt point you pointed out: rate_bps takes only non-RMW
ops, so per SEMANTICS it should be a plain u64 with
READ_ONCE()/WRITE_ONCE(). I'll drop the atomic64_t.

The only lockless readers left are cake_config_dump() (cosmetic) and
the cake_mq shared config, which a child reads while holding a
different child's lock than the parent writes it under - on 32-bit
READ_ONCE() on a u64 can tear there. It's an advisory rate that
self-corrects on the next sync interval, so best-effort is fine.

I will resend a v2 with rate_bps as u64 + READ/WRITE_ONCE. Let's wait
for Sashiko first - it will always find something to complain about.

cheers,
jamal

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath
  2026-10-08 11:53     ` Jamal Hadi Salim
@ 2026-10-08 12:03       ` Toke Høiland-Jørgensen
  0 siblings, 0 replies; 11+ messages in thread
From: Toke Høiland-Jørgensen @ 2026-10-08 12:03 UTC (permalink / raw)
  To: Jamal Hadi Salim
  Cc: netdev, Jiri Pirko, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Simon Horman, cake, Victor Nogueira,
	Sashiko

Jamal Hadi Salim <jhs@mojatatu.com> writes:

> On Thu, Oct 8, 2026 at 6:29 AM Toke Høiland-Jørgensen <toke@toke.dk> wrote:
>>
>> > The qdisc is left with autorate disabled but with the estimate installed
>> > instead of X.  On 32-bit machines the unlocked 64-bit rate_bps store is
>> > also not atomic, so a reader can observe a torn rate.  iproute2 reaches
>> > this because parsing 'bandwidth X' sets autorate = 0 and emits both
>> > TCA_CAKE_BASE_RATE64 and TCA_CAKE_AUTORATE.
>> >
>> > Commit the parsed configuration and reconfigure while holding the same
>> > qdisc lock that serializes the datapath, so the netlink writer and the
>> > autorate writer can no longer interleave.  Make rate_bps an atomic64_t
>> > so a 64-bit rate update is atomic on 32-bit machines as well, and keep
>> > the READ_ONCE()/WRITE_ONCE() annotations for the remaining lockless
>> > readers (cake_config_dump(), which runs without the lock, and the
>> > cake_mq shared config, which readers observe under different child
>> > locks).
>>
>> So atomic_t.txt has this section:
>>
>> "SEMANTICS
>> ---------
>>
>> Non-RMW ops:
>>
>> The non-RMW ops are (typically) regular LOADs and STOREs and are canonically
>> implemented using READ_ONCE(), WRITE_ONCE(), smp_load_acquire() and
>> smp_store_release() respectively. Therefore, if you find yourself only using
>> the Non-RMW operations of atomic_t, you do not in fact need atomic_t at all
>> and are doing it wrong."
>>
>>
>> So AFAICT, we don't need atomic_t, we just need READ/WRITE_ONCE()
>> annotations?
>>
>
> hrm. actually, the sch_tree_lock() already protects cake_change(): it
> commits the parsed config and reconfigures together with the datapath
> under the same qdisc lock, so the netlink writer and the autorate
> store can no longer interleave. So the atomic64_t was never needed for
> the lost update.
>
> on the atomic_t.txt point you pointed out: rate_bps takes only non-RMW
> ops, so per SEMANTICS it should be a plain u64 with
> READ_ONCE()/WRITE_ONCE(). I'll drop the atomic64_t.
>
> The only lockless readers left are cake_config_dump() (cosmetic) and
> the cake_mq shared config, which a child reads while holding a
> different child's lock than the parent writes it under - on 32-bit
> READ_ONCE() on a u64 can tear there. It's an advisory rate that
> self-corrects on the next sync interval, so best-effort is fine.
>
> I will resend a v2 with rate_bps as u64 + READ/WRITE_ONCE. Let's wait
> for Sashiko first - it will always find something to complain about.

SGTM.

-Toke

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-10-08 12:03 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-08  7:47 [PATCH net-next 0/4] net/sched: sch_cake: fixes for accounting and reconfiguration Jamal Hadi Salim
2026-10-08  7:47 ` [PATCH net-next 1/4] net/sched/sch_cake: serialize reconfiguration with the datapath Jamal Hadi Salim
2026-10-08 10:06   ` Toke Høiland-Jørgensen
2026-10-08 11:53     ` Jamal Hadi Salim
2026-10-08 12:03       ` Toke Høiland-Jørgensen
2026-10-08  7:47 ` [PATCH net-next 2/4] net/sched/sch_cake: reduce parent backlog when clearing a tin Jamal Hadi Salim
2026-10-08 10:19   ` Toke Høiland-Jørgensen
2026-10-08  7:47 ` [PATCH net-next 3/4] net/sched/sch_cake: widen buffer_used to u64 Jamal Hadi Salim
2026-10-08 10:24   ` Toke Høiland-Jørgensen
2026-10-08  7:47 ` [PATCH net-next 4/4] net/sched/sch_cake: drop dead nla_nest_end() error check Jamal Hadi Salim
2026-10-08 10:24   ` Toke Høiland-Jørgensen

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).