Dear netdev maintainers and community,
I have encountered a kernel warning in the HTB scheduler (`htb_qlen_notify` at
`net/sched/sch_htb.c:609`) when using Open vSwitch (OVS) with a linux-htb QoS
configuration. The issue appears related to a recent change in
`qdisc_tree_reduce_backlog`.
### Environment
- Kernel version: 5.15.189-rt76-yocto-preempt-rt
- Open vSwitch version: 2.17.9
- Configuration:
- Created a veth pair (`veth0` and `veth1`), added `veth0` to an OVS bridge
(`br-test`).
- Applied QoS with linux-htb type, total max-rate=2Mbps, two queues (queue 0:
max-rate=1Mbps, queue 1: max-rate=0.5Mbps).
- Command sequence:
```bash
ip link add veth0 type veth peer name veth1
ip link set veth0 up
ip link set veth1 up
ovs-vsctl add-br br-test
ovs-vsctl add-port br-test veth0
ip addr add 10.0.0.1/24 dev veth1
ovs-vsctl set port veth0 qos=@newqos \
-- --id=@newqos create qos type=linux-htb other-config:max-rate=2000000
queues=0=@q0,1=@q1 \
-- --id=@q0 create queue other-config:min-rate=800000
other-config:max-rate=1000000 \
-- --id=@q1 create queue other-config:min-rate=400000
other-config:max-rate=500000
### Issue
After applying the QoS configuration, the following warning appears in dmesg:
[73591.168117] WARNING: CPU: 6 PID: 61296 at net/sched/sch_htb.c:609
htb_qlen_notify+0x3a/0x40 [sch_htb]
Suspected Cause
The warning seems related to a change in qdisc_tree_reduce_backlog
(/net/sched/sch_api.c)
the commit is e269f29e9395527bc00c213c6b15da04ebb35070 (5.15)
when I revert this commit, the warning disappeared.
I dont know if it is a known issue or have fixing ?
git show e269f29e9395527bc00c213c6b15da04ebb35070
commit e269f29e9395527bc00c213c6b15da04ebb35070
Author: Lion Ackermann <[email protected]>
Date: Mon Jun 30 15:27:30 2025 +0200
net/sched: Always pass notifications when child class becomes empty
[ Upstream commit 103406b38c600fec1fe375a77b27d87e314aea09 ]
Certain classful qdiscs may invoke their classes' dequeue handler on an
enqueue operation. This may unexpectedly empty the child qdisc and thus
make an in-flight class passive via qlen_notify(). Most qdiscs do not
expect such behaviour at this point in time and may re-activate the
class eventually anyways which will lead to a use-after-free.
..............
diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c
index d9ce273ba43d..222921b4751f 100644
--- a/net/sched/sch_api.c
+++ b/net/sched/sch_api.c
@@ -768,15 +768,12 @@ static u32 qdisc_alloc_handle(struct net_device *dev)
void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n, int len)
{
- bool qdisc_is_offloaded = sch->flags & TCQ_F_OFFLOADED;
const struct Qdisc_class_ops *cops;
unsigned long cl;
u32 parentid;
bool notify;
int drops;
- if (n == 0 && len == 0)
- return;
drops = max_t(int, n, 0);
rcu_read_lock();
while ((parentid = sch->parent)) {
@@ -785,17 +782,8 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n,
int len)
if (sch->flags & TCQ_F_NOPARENT)
break;
- /* Notify parent qdisc only if child qdisc becomes empty.
- *
- * If child was empty even before update then backlog
- * counter is screwed and we skip notification because
- * parent class is already passive.
- *
- * If the original child was offloaded then it is allowed
- * to be seem as empty, so the parent is notified anyway.
- */
- notify = !sch->q.qlen && !WARN_ON_ONCE(!n &&
- !qdisc_is_offloaded);
+ /* Notify parent qdisc only if child qdisc becomes empty. */
+ notify = !sch->q.qlen;
/* TODO: perform the search on a per txq basis */
sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid));
if (sch == NULL) {
@@ -804,6 +792,9 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, int n,
int len)
}
cops = sch->ops->cl_ops;
if (notify && cops->qlen_notify) {
+ /* Note that qlen_notify must be idempotent as it may
get called
+ * multiple times.
+ */
cl = cops->find(sch, parentid);
cops->qlen_notify(sch, cl);
}
Thanks
Guocai
_______________________________________________
discuss mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-discuss