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 <nnam...@gmail.com>
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
disc...@openvswitch.org
https://mail.openvswitch.org/mailman/listinfo/ovs-discuss

Reply via email to