[PATCH] net/sched: codel: refresh CAN_BYPASS when limit changes

From: Samuel Moelius

Date: Mon Jun 08 2026 - 20:12:46 EST


sch_codel and sch_fq_codel update their packet limit without refreshing
the queue bypass state. Changing the limit to zero can leave CAN_BYPASS
set from the previous configuration.

The enqueue path can then bypass limit enforcement even though the new
limit should prevent queued packets.

Recompute the bypass flag whenever the configured limit changes.

Assisted-by: Codex:gpt-5.5-cyber-preview
Signed-off-by: Samuel Moelius <sam.moelius@xxxxxxxxxxxxxxx>
---
net/sched/sch_codel.c | 17 ++++++++++++-----
net/sched/sch_fq_codel.c | 17 ++++++++++++-----
2 files changed, 24 insertions(+), 10 deletions(-)

diff --git a/net/sched/sch_codel.c b/net/sched/sch_codel.c
index 317aae0ec7bd..b8de40e63822 100644
--- a/net/sched/sch_codel.c
+++ b/net/sched/sch_codel.c
@@ -97,6 +97,14 @@ static const struct nla_policy codel_policy[TCA_CODEL_MAX + 1] = {
[TCA_CODEL_CE_THRESHOLD]= { .type = NLA_U32 },
};

+static void codel_set_can_bypass(struct Qdisc *sch)
+{
+ if (sch->limit >= 1)
+ sch->flags |= TCQ_F_CAN_BYPASS;
+ else
+ sch->flags &= ~TCQ_F_CAN_BYPASS;
+}
+
static int codel_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
@@ -133,9 +141,11 @@ static int codel_change(struct Qdisc *sch, struct nlattr *opt,
((u64)interval * NSEC_PER_USEC) >> CODEL_SHIFT);
}

- if (tb[TCA_CODEL_LIMIT])
+ if (tb[TCA_CODEL_LIMIT]) {
WRITE_ONCE(sch->limit,
nla_get_u32(tb[TCA_CODEL_LIMIT]));
+ codel_set_can_bypass(sch);
+ }

if (tb[TCA_CODEL_ECN])
WRITE_ONCE(q->params.ecn,
@@ -176,10 +186,7 @@ static int codel_init(struct Qdisc *sch, struct nlattr *opt,
return err;
}

- if (sch->limit >= 1)
- sch->flags |= TCQ_F_CAN_BYPASS;
- else
- sch->flags &= ~TCQ_F_CAN_BYPASS;
+ codel_set_can_bypass(sch);

sch->flags |= TCQ_F_DEQUEUE_DROPS;

diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c
index 24db54684e8a..2695181cb046 100644
--- a/net/sched/sch_fq_codel.c
+++ b/net/sched/sch_fq_codel.c
@@ -364,6 +364,14 @@ static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = {
[TCA_FQ_CODEL_CE_THRESHOLD_MASK] = { .type = NLA_U8 },
};

+static void fq_codel_set_can_bypass(struct Qdisc *sch)
+{
+ if (sch->limit >= 1)
+ sch->flags |= TCQ_F_CAN_BYPASS;
+ else
+ sch->flags &= ~TCQ_F_CAN_BYPASS;
+}
+
static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt,
struct netlink_ext_ack *extack)
{
@@ -422,9 +430,11 @@ static int fq_codel_change(struct Qdisc *sch, struct nlattr *opt,
(interval * NSEC_PER_USEC) >> CODEL_SHIFT);
}

- if (tb[TCA_FQ_CODEL_LIMIT])
+ if (tb[TCA_FQ_CODEL_LIMIT]) {
WRITE_ONCE(sch->limit,
nla_get_u32(tb[TCA_FQ_CODEL_LIMIT]));
+ fq_codel_set_can_bypass(sch);
+ }

if (tb[TCA_FQ_CODEL_ECN])
WRITE_ONCE(q->cparams.ecn,
@@ -514,10 +524,7 @@ static int fq_codel_init(struct Qdisc *sch, struct nlattr *opt,
codel_vars_init(&flow->cvars);
}
}
- if (sch->limit >= 1)
- sch->flags |= TCQ_F_CAN_BYPASS;
- else
- sch->flags &= ~TCQ_F_CAN_BYPASS;
+ fq_codel_set_can_bypass(sch);

sch->flags |= TCQ_F_DEQUEUE_DROPS;

--
2.43.0